使用指数运算0.5效率低于math.sqrt?

11 投票
5 回答
2612 浏览
提问于 2025-04-16 21:11

这段话摘自一本叫《Python编程:计算机科学导论》的书。

我们本可以用指数运算符 ** 来计算平方根,但使用 math.sqrt 更有效率一些。

这里说的“更有效率一些”,但具体有多有效率呢?是怎么回事呢?

5 个回答

1

我猜测,math.sqrt这个函数可能是用的牛顿法,这种方法收敛速度很快,能很快找到结果。而指数运算可能用的是其他方法,速度就比较慢。

11

不用猜测具体的实现,我们可以直接看代码!

math.sqrt 其实是对标准C库中的 sqrt 的一个简单封装:可以查看 mathmodule.c 的第956行

** 这个运算符的实现方式会根据参数的类型不同而不同,但当指数是浮点数时,它最终会调用标准C库中的 pow 函数(可以查看 floatobject.c 的第783行)。

现代的CPU通常有专门的平方根指令,而一般的指数运算程序并不会使用这些指令(可以对比一下 powsqrt 在glibc中的实现,特别是针对x86-64的)。但是一旦加上了解释器的开销(比如字节码、类型检查、方法调用等),原始速度的差异就不那么重要了,可能还会被你是直接调用 sqrt 还是通过 math 模块查找来影响(这在其他答案中的计时结果中有体现)。

16

理论上,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

撰写回答