Python 为什么循环阶乘函数比递归函数更快?
我已经编写了两个函数来计算组合。第一个使用for循环,另一个使用递归阶乘函数。为什么第一个比第二个快Python 为什么循环阶乘函数比递归函数更快?,python,python-3.x,performance,Python,Python 3.x,Performance,我已经编写了两个函数来计算组合。第一个使用for循环,另一个使用递归阶乘函数。为什么第一个比第二个快 def combinations(n: int, k: int) -> int: # Collection >= Selection if n < k: raise ValueError( "The size of the collection we are selecting items from must be "
def combinations(n: int, k: int) -> int:
# Collection >= Selection
if n < k:
raise ValueError(
"The size of the collection we are selecting items from must be "
"larger than the size of the selection."
)
# Sizes > 0
if n < 0 or k < 0:
raise ValueError(
"Cannot work with negative integers."
)
# Compute with standard python only
numerator = 1
for i in range(n + 1 - k, n+1):
numerator *= i
denominator = 1
for i in range(1, k+1):
denominator *= i
return int(numerator / denominator)
及
新版本的组合2
好的,在评论之后,我同意combinations2正在做更多的操作。所以我重写了阶乘函数和组合函数,下面是它们的版本:
def factorial(n: int, lower: int=-1) -> int:
# n > 0
if n < 0:
raise ValueError(
"Cannot calculate factorial of a negative number."
)
# Recursive function up to n = 0 or up to lower bound
if n - 1 >= 0 and n - 1 >= lower:
return n * factorial(n - 1, lower)
return 1
只需计算操作数: 在
组合中
对分子进行(n+1)-(n+1-k)
乘法,对分母进行(k+1)-1
乘法
总计:2k
乘法
在cominations2
中,您正在进行n+k+(n-k)
乘法,即2n
乘法
您还进行了递归的2n
函数调用
使用
k=50
和n=1000
,难怪第一个解决方案更快。简短版本:递归非常昂贵。python中的递归并不快。递归仅在(某些)其他语言中是快速的,因为编译器优化了尾部递归的递归部分,基本上将其转化为循环。尝试以不同的方式实现阶乘:而不是返回n*factorial(n-1)…
定义一个辅助函数,该函数计算所有参数,即def factorial\u help(n:int,acc:int)->int:如果n-1>0,则返回factorial\u help(n-1,n*acc),否则acc
并将factorial重写为。。。返回阶乘帮助(n,1)
。还是慢得多吗?@TobiasBrösamle afaik Python没有尾部调用optimization@dashiell很高兴知道。我本以为这种方法会比“正常”递归方法更快,但在这种情况下,我认为不会。
def combinations2(n: int, k: int) -> int:
# Collection >= Selection
if n < k:
raise ValueError(
"The size of the collection we are selecting items from must be "
"larger than the size of the selection."
)
return int(factorial(n) / (factorial(k) * factorial(n - k)))
%timeit combinations(1000, 50)
16.2 µs ± 1.95 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit combinations2(1000, 50)
1.6 ms ± 129 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
def factorial(n: int, lower: int=-1) -> int:
# n > 0
if n < 0:
raise ValueError(
"Cannot calculate factorial of a negative number."
)
# Recursive function up to n = 0 or up to lower bound
if n - 1 >= 0 and n - 1 >= lower:
return n * factorial(n - 1, lower)
return 1
def combinations2(n: int, k: int) -> int:
if n < k:
raise ValueError(
"The size of the collection we are selecting items from must be "
"larger than the size of the selection."
)
return int(factorial(n, n - k) / factorial(k))
%timeit combinations(100, 50)
10.5 µs ± 1.67 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit combinations2(100, 50)
56.1 µs ± 5.79 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)