使用求幂**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 的 powsqrt 的实现)。但是,一旦添加了所有解释器开销(字节码、类型检查、方法分派等),原始速度的差异就不再那么重要了,并且可能会受到诸如您是否直接调用 sqrt 或查看它等问题的支配通过math 模块(如其他答案中的时间所示)。

【讨论】:

【参考方案3】:

** 必须支持任何指数,而math.sqrt 知道它始终是0.5math.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 效率低?的主要内容,如果未能解决你的问题,请参考以下文章

python 画正态曲线

Math.pow 是不是比使用临时分配的乘法更昂贵?

go中的平方负数

为啥求幂(例如 10^6)比 R 中的计算器符号(例如 1e6)花费的时间长 4 倍?

sqrt开平方算法的尝试,是的看了卡马克大叔的代码,我来试试用C#写个0x5f3759df和0x5f375a86跟System.Math.Sqrt到底哪个更强

JOIN 比两个 sql 查询效率低吗?