Algorithm Euler#36项目的更好解决方案?
欧拉项目指出: 十进制数585=1001(二进制),在两个基数中都是回文的 求以10为底和以2为底的所有回文数的总和,小于一百万 (请注意,回文数字在任何一个基中都可能不包括前导零。) 已经有一个解决堆栈溢出的方法,但是我想要一个更有效的解决方案 例如,由于回文不能有前导0,因此不需要检查偶数,只需检查二进制中最后一位为1的奇数。这个简单的观察已经将蛮力“检查范围内的每个数字”的速度提高了2倍 但我希望比这更聪明。理想情况下,我希望算法的运行时间与总和中的数字数量成比例。我认为不可能做得比这更好。但这可能是不可能的。例如,我们可以生成所有回文十进制数,其时间小于一百万,与满足该属性的十进制数成比例吗?(我认为答案是肯定的)Algorithm Euler#36项目的更好解决方案?,algorithm,binary,decimal,Algorithm,Binary,Decimal,欧拉项目指出: 十进制数585=1001(二进制),在两个基数中都是回文的 求以10为底和以2为底的所有回文数的总和,小于一百万 (请注意,回文数字在任何一个基中都可能不包括前导零。) 已经有一个解决堆栈溢出的方法,但是我想要一个更有效的解决方案 例如,由于回文不能有前导0,因此不需要检查偶数,只需检查二进制中最后一位为1的奇数。这个简单的观察已经将蛮力“检查范围内的每个数字”的速度提高了2倍 但我希望比这更聪明。理想情况下,我希望算法的运行时间与总和中的数字数量成比例。我认为不可能做得比这更好
<强>什么是解决这个回文和问题的最有效算法?< /强>我想考虑由n参数化的运行时间:数字范围的大小(在这种情况下为100万),D:范围内的十进制回文集,和B:范围内的二进制回文集。我希望运行时是o(N)+o(| D相交B |),否则是o(min(| D |,| B |))
注意:和回文的顺序是众所周知的 e、 二元回文<100:0,1,3,5,7,9,15,17,21,27,31,33,45,51,63,65,73,85,93,99 。十进制回文<100:0,1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99 两个碱基的回文:0,1,3,5,7,9,33,99 33和99的二进制表示分别为10001
和1100011
。
下一个在这两种语言中都是回文的数字是
585=1001
长度2*k
的基b
中的回文数是(b-1)*b^(k-1)
,长度2*k-1
的回文数也是如此。因此,任何基中不超过N
的回文数都是O(sqrt(N))imk_。因此,如果您在一个库中生成所有回文(不超过N
),并检查它们在另一个库中是否也是回文,那么您有一个O(sqrt(N)*log(N))算法(log因子来自回文检查)。这是o(N),但我还不知道它是否也是o(| D与B相交)
这不是O(| D相交B |):(在1010之前只有32个数字在两个基数中都是回文的。我看不到任何模式允许只构建那些
如果
N
有d
位(在基b
中),则不超过N
的回文数介于最多有d-1
位的回文数和最多有d
位的回文数之间(包括两个限制)。有(b-1)*b^(k-1)
数字正好有k
位(以b
为基数),其中(b-1)*b^(floor((k-1)/2))
是回文。求和给出了最多为k
位的基回文数,即2*(b^(k/2)-1
(如果k
为偶数)或(b-1)*b^((k-1)/2*(b^((k-1)/2)-1
(如果k
为奇数)。因此,给定或取系数2*b
,最多d位的回文数为b^(d/2)
。因此,不超过N
的回文数大致为N^0.5
,其系数以基数的倍数为界。考虑到1到1000000之间只有大约2000个十进制回文。从1到999,您可以将数字及其反面串在一起,同时可以复制或不复制“中间”数字(左半部分的最后一个数字)。对于每一个,你检查它是否也是一个二进制回文,如果是,它是总和的一部分。我的第一个假设完全错误,所以我修正了它们。我提供了两种算法,一种是迭代算法,另一种是递归算法。它们显然远没有user988052
那么令人印象深刻和高效,但它们确实更容易阅读!第一个算法是迭代的,运行时间为9ms。第二种算法是递归的,运行时间为16ms。尽管第二种解决方案更干净,但递归调用可能会减慢速度
第一个算法(9ms):
/** Given half a palindrome, construct the rest of the palindrome with
* an optional string inserted in the middle. The returned string is
* only guaranteed to be a palindrome if 'mid' is empty or a palindrome. */
public static String getPalindrome(String bin_part, String mid) {
return bin_part + mid + (new StringBuilder(bin_part)).reverse();
}
/** Check if the specified string is a palindrome. */
public static boolean isPalindrome(String p) {
for (int i=0; i<p.length()/2; i++)
if (p.charAt(i) != p.charAt(p.length()-1-i))
return false;
return true;
}
public static void main(String[] args) {
String[] mids = {"0","1"};
long total = 0;
boolean longDone = false; // have the numbers with extra digits been tested
long start = System.currentTimeMillis();
for (long i=0; i<1000; i++) {
String bin_part = Long.toBinaryString(i);
String bin = getPalindrome(bin_part, "");
long dec = Long.valueOf(bin, 2);
if (dec >= 1000000) break; // totally done
if (isPalindrome(Long.toString(dec)))
total += dec;
if (!longDone) {
for (int m=0; m<mids.length; m++) {
bin = getPalindrome(bin_part, mids[m]);
dec = Long.valueOf(bin, 2);
if (dec >= 1000000) {
longDone = true;
break;
}
if (isPalindrome(Long.toString(dec)))
total += dec;
}
}
}
long end = System.currentTimeMillis();
System.out.println("Total: " + total + " in " + (end-start) + " ms");
}
/**给定半个回文,用
*一个可选的字符串插入中间。返回的字符串是
*仅当“mid”为空或是回文时才保证为回文*/
公共静态字符串getPalindrome(字符串bin_部分,字符串中间){
返回bin_part+mid+(新的StringBuilder(bin_part)).reverse();
}
/**检查指定的字符串是否为回文*/
公共静态布尔值isAlindrome(字符串p){
对于(inti=0;i(不是对您问题的回答,而是针对ProjectEuler36的一个可爱的递归位处理解决方案)
这可能不是最有效的算法,但我喜欢它的样子。我在读了Daniel Fischer的答案后写下了它,建议在一个库中生成所有回文,然后在另一个库中检查它是否也是回文
在18行代码(包括括号)中,它生成以2为底的所有回文,然后检查它们是否也是以10为底的回文
在我的系统上大约需要6毫秒
这可能是可以优化的(我喜欢太多的位移位操作,这里可能有一些不必要的垃圾),可能还有更好的算法,但我“喜欢”(+)我代码的外观;)
@测试
公共无效测试项目EULER36(){
最终整数v=rec(1,1)
public long total = 0;
public long max_value = 1000000;
public long runtime = -1;
public static boolean isPalindrome(String s) {
for (int i=0; i<s.length()/2; i++)
if (s.charAt(i) != s.charAt(s.length()-1-i))
return false;
return true;
}
public void gen(String bin, boolean done) {
if (done) { // generated a valid binary number
// check current value and add to total if possible
long val = Long.valueOf(bin, 2);
if (val >= max_value)
return;
if (isPalindrome(Long.toString(val))) {
total += val;
}
// generate next value
gen('1' + bin + '1', true);
gen('0' + bin + '0', false);
} else { // generated invalid binary number (contains leading and trailing zero)
if (Long.valueOf('1' + bin + '1', 2) < max_value) {
gen('1' + bin + '1', true);
gen('0' + bin + '0', false);
}
}
}
public void start() {
total = 0;
runtime = -1;
long start = System.currentTimeMillis();
gen("",false);
gen("1",true);
gen("0",false);
long end = System.currentTimeMillis();
runtime = end - start;
}
public static void main(String[] args) {
Palindromes2 p = new Palindromes2();
p.start();
System.out.println("Total: " + p.total + " in " + p.runtime + " ms.");
}
@Test
public void testProjectEuler36() {
final int v = rec(1, 1);
assertEquals( 872187, v );
}
public static int rec( final int n, final int z ) {
if ( n > 1000000 )
return 0;
if ( z % 2 == 0 ) {
final int l = n << 1 & -1 << z / 2 + 1;
final int r = n & -1 >>> 32 - z / 2;
return v(n) + rec( l | 1 << z / 2 | r, z + 1 ) + rec( l | r, z + 1 );
} else
return v(n) + rec( n << 1 & -1 << z / 2 + 1 | n & -1 >>> 31 - z / 2, z + 1 );
}
public static int v( final int n ) {
final String s = "" + n;
boolean ok = true;
for ( int j = s.length(), i = j / 2 - 1; i >= 0 && ok; i--)
ok = s.charAt(i) == s.charAt(j-(i+1));
return ok ? n : 0;
}
sum = 0
for i in range(1000000):
bina = int(str(bin(i)).replace('0b',''))
if(i==int(str(i)[::-1]))or(bina==int(str(bina)[::-1])):
#print("i : "+str(i))
#print("bina : "+str(bina))
sum+=i
print("Sum of numbers : ",sum)