这是问题所在:
多少个整数0≤n<0。 10 ^ 18具有n的数字之和等于137n的数字之和的属性
这种解决方案非常低效。我错过了什么?
#!/usr/bin/env python
#coding: utf-8
import time
from timestrings import *
start = time.clock()
maxpower = 18
count = 0
for i in range(0, 10 ** maxpower - 1):
if i % 9 == 0:
result1 = list(str(i))
result2 = list(str(137 * i))
sum1 = 0
for j in result1:
sum1 += int(j)
sum2 = 0
for j in result2:
sum2 += int(j)
if sum1 == sum2:
print (i, sum1)
count += 1
finish = time.clock()
print ("Project Euler, Project 290")
print ()
print ("Answer:", count)
print ("Time:", stringifytime(finish - start))
答案 0 :(得分:6)
首先,你要数,而不是显示命中。
这非常重要。您所要做的就是设置一种有效的方法来计算它。就像乔恩·本特利(Jon Bentley)在编程珍珠(Programming Pearls)中所写的那样:“任何考虑所有字母排列的方法都注定要失败”。事实上,我试过在python中,只要“i”命中10 ^ 9,系统就已经冻结了。消耗了1.5 G内存。更不用说10 ^ 18了。这也告诉我们,再次引用宾利,“定义问题大约是这场战斗的百分之九十。”
要解决这个问题,我看不到没有动态编程(dp)的方法。事实上,大多数那些荒谬巨大的欧拉问题都需要某种dp。 dp本身的理论相当学术和干燥,但实现dp解决实际问题的想法实际上并不是很有趣和丰富多彩。
问题的一个解决方案是,我们从0-9然后是10-99然后是100-999,依此类推并提取数字的签名,汇总具有相同签名的数字并将它们全部作为一块处理,从而节省空间和时间。
观察: 3 * 137 = 411和13 * 137 = 1781.让我们将第一个结果“411”分解为两部分:前两个数字“41”和最后一个数字“1”。 “1”正在停留,但“41”部分将被“携带”进行进一步的计算。让我们称之为“41”,即签名的第一个元素。当我们计算13 * 137,23 * 137,33 * 137或43 * 137时,“1”将保持最小的数字。所有这些* 3数字都有一个“3”作为他们最右边的数字和最后一位数字137 * n总是1.也就是说,这个“3”和“1”之间的差值是+2,把这个+2称为“差异”作为签名的第二个元素。
好的,如果我们要找到一个两位数字,最后一位数为3,我们必须找到一个满足
的数字“m” diff_of_digitsum (m, 137*m+carry) = -2 (1)
中和早先积累的+2差异。如果我能做到这一点,那么你知道m * 10 + 3,在你写的纸上:“m3”,是一个打击。
例如,在我们的例子中,我们尝试了数字1. diff_of_digitsum(数字,137 *数字+进位)= diff_of_digitsum(1,137 * 1 + 41)= -15。哪个不是-2,所以13不是命中。
让我们看99. 9 * 137 = 1233.“差异”是9 - 3 = +6。 “Carry”是123.在第二次迭代中,当我们尝试添加数字9到9并使其为99时,我们有diff_of_digitsum(数字,137 *数字+进位)= diff_of_digitsum(9,137 * 9 + 123)= diff_of_digitsum (9,1356)= -6并且它中和了我们的剩余6.所以99是一个打击!
在代码中,我们只需要18次迭代。在第一轮中,我们处理单位数字,第二轮2位数字,然后是3位数......直到我们得到18位数字。在具有如下结构的迭代之前创建一个表:
table[(diff, carry)] = amount_of_numbers_with_the_same_diff_and_carry
然后迭代开始,你需要不断更新表。如果遇到新签名,请添加新条目,并始终更新amount_of_numbers_with_the_same_diff_and_carry。第一轮,单个数字,填充表:
0:0 * 137 = 0,diff:0; carry:0。table [(0,0)] = 1
1:1 * 137 = 137.diff:1 - 7 = -6; carry:13。table [( - 6,13)] = 1
2:2 * 137 = 274.diff:2 - 7 = -5;随身携带:27.表[( - 5,27)] = 1
等等。
第二次迭代,“10”数字,我们将作为你的“m”越过数字0-9并在(1)中使用它来查看它是否能产生中和“差异”的结果。如果是,则表示此m将使所有那些amount_of_numbers_with_the_same_diff_and_carry成为命中。因此计数不显示。然后我们可以计算出新的差异和携带这个数字的加载,就像在例子9中有diff 6并携带123但99有diff 9 - 6(最后一位数从1356)= 3并携带135,替换旧表使用新信息。
最后一条评论,请注意数字0.在迭代中会出现很多次并且不要过多计算,因为0009 = 009 = 09 = 9.如果使用c ++,请确保总和是无符号的很长很长,因为它很大。祝你好运。
答案 1 :(得分:4)
您正试图通过暴力解决项目欧拉问题。这可能适用于前几个问题,但对于大多数问题,您需要考虑更复杂的方法。
由于恕我直言并未提供有关此问题的具体建议,请查看this answer中的一般建议。
答案 2 :(得分:0)
这个7位数的暴力Python解决方案为我跑了19秒:
print sum(sum(map(int, str(n))) == sum(map(int, str(137 * n)))
for n in xrange(0, 10 ** 7, 9))
在同一台机器上,单核心,相同的Python解释器,相同的代码,大约需要3170年来计算18位数(如问题所示)。
请参阅dgg32的答案,了解更快计算的灵感。