我使用Python来解决Project Euler问题。许多人需要缓存过去计算的结果以提高性能,从而产生如下代码:
pastResults = [None] * 1000000
def someCalculation(integerArgument):
# return result of a calculation performed on numberArgument
# for example, summing the factorial or square of its digits
for eachNumber in range(1, 1000001)
if pastResults[eachNumber - 1] is None:
pastResults[eachNumber - 1] = someCalculation(eachNumber)
# perform additional actions with pastResults[eachNumber - 1]
重复递减是否会对计划绩效产生负面影响?是否有一个空的或虚拟的第零个元素(所以基于零的数组模拟一个基于数组的数组)通过消除重复的递减来提高性能?
pastResults = [None] * 1000001
def someCalculation(integerArgument):
# return result of a calculation performed on numberArgument
# for example, summing the factorial or square of its digits
for eachNumber in range(1, 1000001)
if pastResults[eachNumber] is None:
pastResults[eachNumber] = someCalculation(eachNumber)
# perform additional actions with pastResults[eachNumber]
我还觉得模拟一个基于数组的数组会使代码更容易理解。这就是为什么我不会将for eachNumber in range(1000000)
作为范围从零开始,因为someCalculation(eachNumber + 1)
不合逻辑。
空的第0个元素的附加内存有多重要?我还应该考虑哪些其他因素?我更喜欢那些不仅限于Python和Project Euler的答案。
编辑:应该是is None
而不是is not None
。
答案 0 :(得分:3)
不是关于性能问题的答案,而是关于缓存先前计算值的一般提示。通常的方法是使用映射(Python dict
),因为这允许使用更复杂的键而不是整数,如浮点数,字符串甚至元组。此外,如果您的密钥相当稀疏,您也不会遇到问题。
pastResults = {}
def someCalculation(integerArgument):
if integerArgument not in pastResults:
pastResults[integerArgument] = # calculation performed on numberArg.
return pastResults[integerArgument]
此外,无需使用循环“按顺序”执行计算。只需为你感兴趣的值调用函数,if
语句就会注意,当递归调用时,每个参数只调用一次函数。
最终,如果你经常使用它(就像Project Euler的情况那样),你可以自己定义function decorator,就像这样:
def memo(f):
f.cache = {}
def _f(*args, **kwargs):
if args not in f.cache:
f.cache[args] = f(*args, **kwargs)
return f.cache[args]
return _f
它的作用是:它接受一个函数并定义另一个函数,它首先检查是否可以在缓存中找到给定的参数,否则计算原始函数的结果并将其放入缓存中。只需将@memo
注释添加到函数定义中,这将为您处理缓存。
@memo
def someCalculation(integerArgument):
# function body
这是someCalculation = memo(someCalculation)
的语法糖。但请注意,这并不总是很好。首先,paremters必须是可清洗的(没有列表或其他可变类型);第二,如果您传递的参数与结果无关(例如,调试内容等),您的缓存可能会不必要地变大,因为 all 将参数用作密钥。