我正在做一个用于编程练习的Project Euler问题,以便自学。我非常清楚如何以数学方式完成该问题,以及如何以编程方式完成该问题。
但是,我必须想出一些疯狂的代码才能做到这一点; 100 个嵌套循环和 Python 在 100 级缩进上搞笑地引发了这个错误,而且可能是正确的:
IndentationError: too many levels of indentation
tally = 0
ceiling = 100
for integer_1 in range(0, 100, 1):
for integer_2 in range(0, 100 - integer_1, 2):
for integer_3 in range(0, 100 - integer_1 - integer_2, 3):
for integer_4 ....
for integer_5 ....
etc.
etc.
all the way to integer_100
我已经通过谷歌寻找解决方案,但这个问题非常罕见,几乎没有关于该主题的文献,我只能找到另一个堆栈溢出问题(Python IndentationError:太多级别的缩进),我找不到太多对于我的问题很有用。
我的问题是 - 有没有办法采用我的解决方案并找到一些解决方法或以使其有效的方式重构它?我真的被难住了。
编辑:
感谢nneonneo的回答,我能够解决这个问题。我的代码放在这里仅供将来寻找正确重构代码的方法的人们参考。
from time import time
t = time()
count_rec_dict = {}
# for finding ways to sum to 100
def count_rec(cursum, level):
global count_rec_dict
# 99 is the last integer that we could be using,
# so prevent the algorithm from going further.
if level == 99:
if cursum == 100:
return 1
else:
return 0
res = 0
for i in xrange(0, 101-cursum, level+1):
# fetch branch value from the dictionary
if (cursum+i, level+1) in count_rec_dict:
res += count_rec_dict[(cursum+i, level+1)]
# add branch value to the dictionary
else:
count_rec_dict[(cursum+i, level+1)] = count_rec(cursum+i, level+1)
res += count_rec_dict[(cursum+i, level+1)]
return res}
print count_rec(0, 0)
print time() - t
在我的电脑上运行时间仅为惊人的 0.041 秒。哇!!!!!今天我学到了一些新东西!
递归解决方案应该做得很好,尽管我确信对于这个问题有一个完全不同的解决方案,不需要这种操作。
def count_rec(cursum, level):
if level == 100:
return 1
res = 0
for i in xrange(0, 100-cursum, level+1):
res += count_rec(cursum+i, level+1)
return res
print count_rec(0, 0)
有趣的是,如果你memoize这个函数,它实际上会有一个合理的运行时间(这就是动态编程的力量)。玩得开心!
避免缩进错误的一种方法是将循环放在单独的函数中,每个循环仅嵌套一层深。
或者,您可以使用递归来一遍又一遍地调用函数,每次都使用更小的范围和更高的嵌套级别。
话虽如此,无论您如何编码,您的算法都将具有难以置信的长运行时间。你需要一个更好的算法:-)
要完全使用您的算法(将每个下一个数字限制为可能适合所需总和的数字)来完成此操作,您确实需要递归 - 但真正的强力方法可以是单行的:
sum(sum(i) == 100 for i in itertools.product(xrange(100), repeat=100))
当然,这比真正重构算法要慢一些(事实上,正如评论中提到的,事实证明这很棘手)。
最有效的解决方案是基于算术进位的思想。 您有最大值和步骤的列表, 还有当前值的列表。每次您想要更新这 100 个变量时,您都可以这样做:
inc_index = -1
currentvalue[inc_index] += stepval[inc_index]
# I use >= rather than > here to replicate range()s behaviour that range(0,100) generates numbers from 0 to 99.
while currentvalue[inc_index] >= maxval[inc_index]:
currentvalue[inc_index] = 0
inc_index -= 1
currentvalue[inc_index] += stepval[inc_index]
# now regenerate maxes for all subordinate indices
while inc_index < -1:
maxval[inc_index + 1] = 100 - sum (currentvalue[:inc_index])
inc_index += 1
当引发 IndexError 时,您已完成循环(用完要进位的“数字”。)