问题描述
当我注意到这一点时,我正在尝试用不同的方式在 python 中进行递归:
[为简洁而简化]
import time
def fac(n):
if n in [0,1]:
return 1
else:
return n * fac(n-1)
def fac2(n):
total = 1
if n == 0:
return total
else:
return n * fac2(n-1)
t0 = time.time()
fac(997)
t1 = time.time()
print(t1-t0)
t3 = time.time()
fac2(997)
t4 = time.time()
print(t4-t3)
>>> 0.00124359130859375
>>> 0.00046324729919433594
我知道 time
实际上并不测量速度,但这些函数之间存在 [可以忽略不计] 整个数量级的差异。
我觉得 fac2()
只是看起来更漂亮,更容易阅读,但是,我应该避免使用它,因为声明一个新的 var 可能那么昂贵吗?
先谢谢你!
编辑:
感谢您的回复! (是的,我说 fac()
更快是错误的)我接受了回复中的建议并拨打了具体电话:
import time
def fac(n):
if n in [0,1]:
return 1
else:
return n * fac(n-1)
def fac2(n):
total = 1
if n == 0:
return total
else:
return n * fac2(n-1)
def fac3(n):
if n < 2:
return 1
else:
return n * fac(n-1)
def fac4(N,m=0):
if m == N:
return max(1,N)
h = (N+m)//2
return fac4(h,m)*fac4(N,h+1)
print('fac1: ',end='')
t0 = time.time()
for i in range(1000):
fac(997)
t1 = time.time()
print(t1-t0)
print('fac2: ',end='')
t3 = time.time()
for i in range(1000):
fac2(997)
t4 = time.time()
print(t4-t3)
print('fac3: ',end='')
t5 = time.time()
for i in range(1000):
fac3(997)
t6 = time.time()
print(t6-t5)
print('fac4: ',end='')
t7 = time.time()
for i in range(1000):
fac4(997)
t8 = time.time()
print(t8-t7)
这大约是我多次运行程序时得到的平均值(WSL2 Ubuntu 18.04):
fac1: 0.4169285297393799
fac2: 0.46125292778015137
fac3: 0.36595988273620605
fac4: 0.3895738124847412
>>>
fac1: 0.42001867294311523
fac2: 0.454434871673584
fac3: 0.3698153495788574
fac4: 0.3903229236602783
>>>
fac1: 0.4169285297393799
fac2: 0.46125292778015137
fac3: 0.36595988273620605
fac4: 0.3895738124847412
现在,我非常很想知道为什么 fac3()
看起来是“最快”的。有人能解释一下原因吗?
再次感谢您。
解决方法
您的衡量方法具有误导性:
函数的单次执行并不能说明什么:分页、编译等的成本会对结果产生重大影响。当函数执行的次数较多时,可以减少这种一次性效应的影响。
按以下方式包装对不同 fac
函数的调用时:
t0 = time.time()
for i in range(10000):
fac(997)
t1 = time.time()
print(t1-t0)
然后差异在 3% 的范围内 - 至少在我的系统上是这样。基于如此小的差异进行设计更改是不可取的 - 差异很容易是由于不在代码中的原因(跨越缓存页面边界等)。
结论:
- 通常最好提高可读性
- 在性能很重要的情况下,进行相应的测量和优化是可以的。但是,如您的示例所示,获得可靠且有意义的测量数据并非易事。
- 不同解决方案之间的性能差异越小,您就越需要了解差异的真正原因并排除环境影响。
在 n<2
中使用 n in [0,1]
而不是 fac()
并避免在 fac2()
中使用不必要的变量(或至少给它一个适当的名称)会更好并仅在需要时分配)。
如果你要玩递归,你应该尝试绕过递归深度限制。那将更具挑战性。
例如:
def fact(N,m=0):
if m==N: return max(1,N)
h = (N+m)//2
return fact(h,m)*fact(N,h+1)