Palindromic数字分为两个基础,Project Euler#36

时间:2015-02-05 19:01:19

标签: python

以下是Project Euler problem 36的解决方案,内容如下:

  

十进制数,585 = 1001001001 2(二进制),两个基数都是回文。

     

找出所有数字的总和,少于一百万,在基数10和基数2中是回文。

     

(请注意,任一基地的回文数字可能不包括前导零。)

我的结果有些不对劲。有人可以帮忙吗?

def findnum(n):
    a = 0
    for i in range(0, n + 1):
        temp = '{0:08b}'.format(i)
        if str(i) == str(i)[::-1] and str(temp) == str(temp)[::-1]:
            a += i 
    return a 

print findnum(1000000)

我得到的结果是872096,但正确的答案似乎是872187.但这两者之间的差异,91,不是回文。我做错了什么?

2 个答案:

答案 0 :(得分:10)

你错过了问题描述中的一些内容:

  

请注意,任何基数的回文数字可能不包括前导

然而,您正在使用前导零:

temp = '{0:08b}'.format(i)

从格式中删除08;这里根本不需要将它填充到宽度:

temp = '{0:b}'.format(i)

通过此更改,您将得到正确答案。

您也可以使用format() function代替,因为您没有将字符串放入更大的模板中。您不需要将其提供给str(),因为format()已生成字符串。我先交换测试来测试二进制回文并避免额外的str()次调用。 n + 1来电中range()不需要;问题描述要求所有这些数字低于 100万:

for i in range(n):
    temp = format(i, 'b')
    if temp == temp[::-1] and str(i) == str(i)[::-1]:
        a += i

或者你可以先测试小数回文,然后格式:

for i in range(n):
    decimal = str(i)
    if decimal != decimal[::-1]:
        continue
    binary = format(i, 'b')
    if binary == binary[::-1]:
        a += i

这削减了整个运行时的大量调用,使最终版本的速度提高了两倍以上:

>>> from timeit import timeit
>>> def findnum_orig(n):
...     a = 0
...     for i in range(0, n + 1):
...         temp = '{0:b}'.format(i)
...         if str(i) == str(i)[::-1] and str(temp) == str(temp)[::-1]:
...             a += i 
...     return a 
... 
>>> def findnum_optimised(n):
...     a = 0
...     for i in range(n):
...         decimal = str(i)
...         if decimal != decimal[::-1]:
...             continue
...         binary = format(i, 'b')
...         if binary == binary[::-1]:
...             a += i
...     return a
... 
>>> timeit('fn(1000000)', 'from __main__ import findnum_orig as fn', number=10)
10.886759996414185
>>> timeit('fn(1000000)', 'from __main__ import findnum_optimised as fn', number=10)
3.7782959938049316

这是因为str()format()要快得多:

>>> timeit('for i in range(1000): str(i)', number=1000)
0.17951107025146484
>>> timeit('for i in range(1000): format(i, 'b')', number=1000)
0.2837510108947754

由于只有1999年十进制和2000个二元回文数低于100万:

>>> sum(1 for i in range(1000000) if str(i) == str(i)[::-1])
1999
>>> sum(1 for i in range(1000000) if format(i, 'b') == format(i, 'b')[::-1])
2000

避免慢速操作除了那些1999年的十进制回文外,你可以节省很多时间。

我们可以通过切换将整数转换为二进制的方式来加快速度。 bin() function也产生二进制数,尽管有0b前缀。即使必须使用该函数删除该前缀也比使用format()

更快
>>> timeit('for i in range(1000): format(i, "b")', number=1000)
0.46987009048461914
>>> timeit('for i in range(1000): bin(i)[2:]', number=1000)
0.24124693870544434

如果您使用的是Python 2.x,则还应使用xrange() function来避免创建包含1.000.000整数的列表。这将最终时间安排到:

>>> def findnum_bin_xrange(n):
...     a = 0
...     for i in xrange(n):
...         decimal = str(i)
...         if decimal != decimal[::-1]:
...             continue
...         binary = bin(i)[2:]
...         if binary == binary[::-1]:
...             a += i 
...     return a 
... 
>>> findnum_bin_xrange(1000000)
872187
>>> timeit('fn(1000000)', 'from __main__ import findnum_bin_xrange as fn', number=10)
3.5071611404418945

这大约是原始代码时间的1/3。

答案 1 :(得分:0)

我的解决方案大约比Martijn快20倍。

该方法也略有不同,使用的数字必须向后读取相同的事实。我不是检查所有数字以查看它们是否是回文,而是自己生成它们。这引入了一些额外的代码行,因为你必须处理3种不同的情况:

  • 个位数(1,2,3等),
  • 偶数(4334,5115等),
  • 和不均匀的数字(121,87378等)回文。

主要的循环范围可以减少到1000.

首先使用上述策略生成所有回文数字(base10)。然后在base2中检查生成的数字。如果它们是回文数,则将它们添加到数组中。 (如果跳过数组并将其加到变量中,则进一步提高速度。)

import time

#calculate digits
def digits(num):
    return len(str(num))
#reverse order
def reverse(num):
  return int(str(num)[::-1])

#check binary counterpartner
def bin_pal_chk(number):
    b = int(bin(number)[2:])
    if (b == reverse(b)):
        return True
    else:
        return False

#list with all base 10 palindromes
palin = []
#calculate palindromes
t = time.clock()
for i in range(1,1000):
    #single digits
    if (i < 10):
        if bin_pal_chk(i):
            palin.append(i)
    #even digits
    num = i*10**digits(i) + reverse(i)
    if bin_pal_chk(num):
        palin.append(num)
    #uneven digits
    if (i < 100):
        for j in range(10):
            num = i*10**(digits(i)+1) + j*10**digits(i) + reverse(i)
            if bin_pal_chk(num):
                palin.append(num)
print time.clock() - t