Python 的 math.factorial 没有被记忆。
我将通过一些试验和错误示例来指导您了解为什么要获得一个真正记忆化且有效的阶乘函数,您必须从头重新定义它,并考虑几件事情。
另一个答案实际上是不正确的。这里,
import math
cache = {}
def myfact(x):
return cache.setdefault(x,math.factorial(x))
线
return cache.setdefault(x,math.factorial(x))
每次都计算x 和math.factorial(x),因此您不会获得任何性能提升。
你可能会想到做这样的事情:
if x not in cache:
cache[x] = math.factorial(x)
return cache[x]
但实际上这也是错误的。是的,您避免再次计算相同x 的阶乘,但请考虑,例如,如果您要计算myfact(1000) 并在此之后不久计算myfact(999)。它们都被完全计算,因此没有利用myfact(1000)自动计算myfact(999)这一事实。
这样写是很自然的:
def memoize(f):
"""Returns a memoized version of f"""
memory = {}
def memoized(*args):
if args not in memory:
memory[args] = f(*args)
return memory[args]
return memoized
@memoize
def my_fact(x):
assert x >= 0
if x == 0:
return 1
return x * my_fact(x - 1)
这将起作用。不幸的是,它很快就达到了最大递归深度。
那么如何实现呢?
这是一个真正记忆阶乘的示例,它利用了阶乘的工作原理,并且不会通过递归调用消耗所有堆栈:
# The 'max' key stores the maximum number for which the factorial is stored.
fact_memory = {0: 1, 1: 1, 'max': 1}
def my_fact(num):
# Factorial is defined only for non-negative numbers
assert num >= 0
if num <= fact_memory['max']:
return fact_memory[num]
for x in range(fact_memory['max']+1, num+1):
fact_memory[x] = fact_memory[x-1] * x
fact_memory['max'] = num
return fact_memory[num]
我希望你觉得这很有用。
编辑:
请注意,实现相同优化同时具有递归的简洁和优雅的一种方法是将函数重新定义为tail-recursive 函数。
def memoize(f):
"""Returns a memoized version of f"""
memory = {}
def memoized(*args):
if args not in memory:
memory[args] = f(*args)
return memory[args]
return memoized
@memoize
def my_fact(x, fac=1):
assert x >= 0
if x < 2:
return fac
return my_fact(x-1, x*fac)
实际上,尾递归函数可以被解释器/编译器识别并自动翻译/优化为迭代版本,但并非所有解释器/编译器都支持这一点。
可惜python不支持尾递归优化,所以还是得到:
RuntimeError: maximum recursion depth exceeded
当 my_fact 的输入为高时。