lru_cache 在你的情况下可以创造奇迹。确保 maxsize 是 2 的幂。可能需要为您的应用程序调整该大小。 cache_info() 将对此有所帮助。
也使用// 代替/ 进行整数除法。
from functools import lru_cache
@lru_cache(maxsize=512, typed=False)
def fusc(n):
if n <= 1:
return n
while n > 2 and n % 2 == 0:
n //= 2
return fusc((n - 1) // 2) + fusc((n + 1) // 2)
print(fusc(1000000000078093254329870980000043298))
print(fusc.cache_info())
是的,这只是 Filip Malczak 提出的 meomization。
您可能会在 while 循环中使用位操作获得额外的 tiny 加速:
while not n & 1: # as long as the lowest bit is not 1
n >>= 1 # shift n right by one
更新:
这是一种“手动”进行 meomzation 的简单方法:
def fusc(n, _mem={}): # _mem will be the cache of the values
# that have been calculated before
if n in _mem: # if we know that one: just return the value
return _mem[n]
if n <= 1:
return n
while not n & 1:
n >>= 1
if n == 1:
return 1
ret = fusc((n - 1) // 2) + fusc((n + 1) // 2)
_mem[n] = ret # store the value for next time
return ret
更新
在阅读了short article by dijkstra himself 之后的小更新。
文章指出,f(n) = f(m) 如果m 的第一个和最后一个位与n 的相同并且它们之间的位被反转。我们的想法是让n 尽可能小。
这就是位掩码(1<<n.bit_length()-1)-2 的用途(第一个和最后一个位是0;中间的那些是1;xoring n 与上面描述的那样给出m)。
我只能做小的基准测试;如果这对您的输入量有任何帮助,我很感兴趣...这将减少缓存的内存并希望带来一些加速。
def fusc_ed(n, _mem={}):
if n <= 1:
return n
while not n & 1:
n >>= 1
if n == 1:
return 1
# https://www.cs.utexas.edu/users/EWD/transcriptions/EWD05xx/EWD578.html
# bit invert the middle bits and check if this is smaller than n
m = n ^ (1<<n.bit_length()-1)-2
n = m if m < n else n
if n in _mem:
return _mem[n]
ret = fusc(n >> 1) + fusc((n >> 1) + 1)
_mem[n] = ret
return ret
我不得不增加递归限制:
import sys
sys.setrecursionlimit(10000) # default limit was 1000
基准测试给出了奇怪的结果;使用下面的代码并确保我总是开始一个新的interperter(有一个空的_mem)我有时会得到更好的运行时;在其他情况下,新代码速度较慢...
基准代码:
print(n.bit_length())
ti = timeit('fusc(n)', setup='from __main__ import fusc, n', number=1)
print(ti)
ti = timeit('fusc_ed(n)', setup='from __main__ import fusc_ed, n', number=1)
print(ti)
这是我得到的三个随机结果:
6959
24.117448464001427
0.013900151001507766
6989
23.92404893300045
0.013844672999766772
7038
24.33894686200074
24.685758719999285
那是我停下来的地方......