使用求幂**0.5 比 math.sqrt 效率低?
Posted
技术标签:
【中文标题】使用求幂**0.5 比 math.sqrt 效率低?【英文标题】:Using exponentiation **0.5 less efficient than math.sqrt? 【发布时间】:2011-10-02 00:41:52 【问题描述】:引用“Python Programming: An Introduction to Computer Science”
我们可以取平方根 使用求幂**。使用 math.sqrt 效率更高。
“有点”,但程度如何?
【问题讨论】:
您可以随时使用timeit
自行测量。根据记录,math.sqrt
对我来说只快了大约 5%。
【参考方案1】:
理论上,hammar's answer 和 duffymo's answer 是很好的猜测。但实际上,在我的机器上,它没有更高效:
>>> 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
【讨论】:
我得出了同样的结论。【参考方案2】:实现不用猜,看代码即可!
math.sqrt
是标准 C 库中关于 sqrt
的精简包装器:参见 mathmodule.c
, line 956
**
运算符根据参数的类型有多种实现,但在浮点指数的情况下,它最终会从标准 C 库中分派到pow
(请参阅floatobject.c
line 783)。
现代 CPU 通常具有特殊的平方根指令,而一般的求幂例程不使用这些指令(例如,比较和对比 glibc 中用于 x86-64 的 pow
和 sqrt
的实现)。但是,一旦添加了所有解释器开销(字节码、类型检查、方法分派等),原始速度的差异就不再那么重要了,并且可能会受到诸如您是否直接调用 sqrt
或查看它等问题的支配通过math
模块(如其他答案中的时间所示)。
【讨论】:
【参考方案3】:**
必须支持任何指数,而math.sqrt
知道它始终是0.5
。 math.sqrt
因此可以使用更专业(因此可能更有效)的算法。
【讨论】:
如果指数小于 1,**
的最佳实现可以简单地分支到 math.sqrt
。这可能会产生几乎无法衡量的影响。
@zneak:大多数实现做。
@zneak:即便如此,它也必须进行该测试,所以它总是会(尽管稍微)慢一些。【参考方案4】:
我的猜测是 math.sqrt 使用 Newton's method,它以二次方收敛,而求幂使用其他更慢的东西。
【讨论】:
正如 zneak 在评论中所指出的那样:没有理由不应该使用相同的算法,或者简单地重用现有的实现,以进行 0.5 的幂运算。math.sqrt
可能是 C 数学函数 sqrt
的别名,它是使用适合您平台的最佳算法实现的。如果您的 CPU 支持 SSE 指令,您将获得一个 sqrt*
指令系列,其中所有成员都尽可能快。【参考方案5】:
这里有一个稍微不同的方法。我们想要一个比平方根大的 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。您处于太多抽象之上,无法尝试以这种粒度级别优化性能。
【讨论】:
以上是关于使用求幂**0.5 比 math.sqrt 效率低?的主要内容,如果未能解决你的问题,请参考以下文章
为啥求幂(例如 10^6)比 R 中的计算器符号(例如 1e6)花费的时间长 4 倍?
sqrt开平方算法的尝试,是的看了卡马克大叔的代码,我来试试用C#写个0x5f3759df和0x5f375a86跟System.Math.Sqrt到底哪个更强