引自“ Python 编程:计算机科学导论”
我们本可以使用求幂 ** 取平方根。使用 math.sqrt 效率更高一些。
“有点”,但在多大程度上,以及如何?
从理论上讲,hammar 的答案和duffymo 的答案都是很好的猜测。但实际上,在我的机器上,它的效率并不高:
>>> import timeit
>>> timeit.timeit(stmt='[n ** 0.5 for n in range(100)]', setup='import math', number=10000)
0.15518403053283691
>>> timeit.timeit(stmt='[math.sqrt(n) for n in range(100)]', setup='import math', number=10000)
0.17707490921020508
部分问题在于.
操作。如果您sqrt
直接导入到命名空间,您会得到轻微的改进。
>>> timeit.timeit(stmt='[sqrt(n) for n in range(100)]', setup='from math import sqrt', number=10000)
0.15312695503234863
那里的关键词:轻微。
进一步的测试表明,随着数量的增加,您从使用中获得的好处sqrt
也会增加。但仍然不是很多!
>>> timeit.timeit(stmt='[n ** 0.5 for n in range(1000000)]', setup='import math', number=1)
0.18888211250305176
>>> timeit.timeit(stmt='[math.sqrt(n) for n in range(1000000)]', setup='import math', number=1)
0.18425297737121582
>>> timeit.timeit(stmt='[sqrt(n) for n in range(1000000)]', setup='from math import sqrt', number=1)
0.1571958065032959
无需猜测实现,我们可以阅读代码!
math.sqrt
sqrt
是标准 C 库的一个瘦包装器:参见mathmodule.c
第 956 行
运算符根据**
参数的类型有多种实现,但在浮点指数的情况下,它最终会pow
从标准 C 库中调度(参见floatobject.c
第 783 行)。
现代 CPU 通常具有特殊的平方根指令,一般取幂例程不使用这些指令(例如,比较和对比 glibc 和 x86-64 的pow
实现sqrt
)。但是一旦添加了所有解释器开销(字节码、类型检查、方法分派等),原始速度的差异就不再那么重要了,并且可能会受到诸如是sqrt
直接调用还是通过math
模块(如其他答案中的时间所示)。
**
必须支持任何指数,而math.sqrt
知道它总是0.5
。math.sqrt
因此可以使用更专业(因此可能更有效)的算法。
我的猜测是 math.sqrt 使用牛顿方法,它以二次方收敛,而求幂使用其他更慢的方法。
这是一种略有不同的方法。我们想要一个比平方根大的 int。两种方法(不同意平方数,但没关系):
>>>timeit.timeit(stmt='[int(n**0.5)+1 for n in range(1000000)]', setup='', number=1)
0.481772899628
>>>timeit.timeit(stmt='[ceil(sqrt(n)) for n in range(1000000)]', setup='from math import sqrt, ceil', number=1)
0.293844938278
>>>timeit.timeit(stmt='[int(ceil(sqrt(n))) for n in range(1000000)]', setup='from math import sqrt, ceil', number=1)
0.511347055435
所以数学函数更快......直到你将浮点数转换为整数。(我需要对值做很多比较,虽然我还没有测试过,但比较整数应该比比较浮点数便宜。)
但是,嘿,它是Python。您处于太多抽象之上,无法尝试以这种粒度级别优化性能。