Algorithm Euler#36项目的更好解决方案?

Algorithm Euler#36项目的更好解决方案?,algorithm,binary,decimal,Algorithm,Binary,Decimal,欧拉项目指出: 十进制数585=1001(二进制),在两个基数中都是回文的 求以10为底和以2为底的所有回文数的总和,小于一百万 (请注意,回文数字在任何一个基中都可能不包括前导零。) 已经有一个解决堆栈溢出的方法,但是我想要一个更有效的解决方案 例如,由于回文不能有前导0,因此不需要检查偶数,只需检查二进制中最后一位为1的奇数。这个简单的观察已经将蛮力“检查范围内的每个数字”的速度提高了2倍 但我希望比这更聪明。理想情况下,我希望算法的运行时间与总和中的数字数量成比例。我认为不可能做得比这更好

欧拉项目指出:

十进制数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)