我想了很久了。就像题目说的,哪个更快,是实际函数还是简单地取1 / 2次幂?

更新

这不是一个过早优化的问题。这只是一个底层代码如何实际工作的问题。Python代码的工作原理是什么?

我给Guido van Rossum发了一封邮件,因为我真的很想知道这些方法的区别。

我的电子邮件:

在Python中至少有3种方法来求平方根:math。返回值, '**'运算符和pow(x,.5)。我只是好奇它们之间的区别 每一个的实现。说到效率 是更好吗?

他的回答:

Pow和**是等价的;数学。根号方根不适用于复数, 并链接到C的sqrt()函数。至于哪一个是 快点,我不知道……


当前回答

有人评论《雷神之锤3》中的“快速牛顿-拉弗森平方根”……我用ctypes实现了它,但与本机版本相比,它非常慢。我将尝试一些优化和替代实现。

from ctypes import c_float, c_long, byref, POINTER, cast

def sqrt(num):
 xhalf = 0.5*num
 x = c_float(num)
 i = cast(byref(x), POINTER(c_long)).contents.value
 i = c_long(0x5f375a86 - (i>>1))
 x = cast(byref(i), POINTER(c_float)).contents.value

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num

这是另一个使用struct的方法,比ctypes版本快3.6倍,但仍然是C的1/10。

from struct import pack, unpack

def sqrt_struct(num):
 xhalf = 0.5*num
 i = unpack('L', pack('f', 28.0))[0]
 i = 0x5f375a86 - (i>>1)
 x = unpack('f', pack('L', i))[0]

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num

其他回答

在这些微观基准测试中,数学。SQRT会慢一些,因为在数学名称空间中查找SQRT所花费的时间很少。你可以用

 from math import sqrt

即使这样,在timeit中运行一些变化,显示x**的轻微(4-5%)性能优势。5

有趣的是,做

 import math
 sqrt = math.sqrt

进一步加速,速度差异在1%以内,几乎没有统计学意义。


我将重复Kibbee,并说这可能是一个不成熟的优化。

Math.sqrt (x)比x**0.5快得多。

import math
N = 1000000
%%timeit
for i in range(N):
    z=i**.5

10个循环,最好的3:156毫秒每循环

%%timeit
for i in range(N):
    z=math.sqrt(i)

10个循环,最好的3:91.1毫秒每循环

使用Python 3.6.9(笔记本)。

你到底做了多少次平方根?你正在尝试用Python编写一些3D图形引擎吗?如果不是,那么为什么要使用晦涩的代码而不是易于阅读的代码呢?在我能预见的任何应用中,时间差都比任何人能注意到的要小。我真的不想放下你的问题,但看起来你在不成熟的优化上走得有点太远了。

当然,如果要处理字面量并且需要一个常量值,Python运行时可以在编译时预先计算该值,如果它是用操作符编写的——在这种情况下不需要分析每个版本:

In [77]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

In [78]: def a(): 
    ...:     return 2 ** 0.5 
    ...:                                                                                                                                  

In [79]: import dis                                                                                                                       

In [80]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

我最近解决的SQRMINSUM问题需要在一个大型数据集上重复计算平方根。在我做其他优化之前,我历史上最老的2个提交,唯一的区别是用sqrt()替换**0.5,从而将PyPy中的运行时从3.74秒减少到0.51秒。这几乎是克劳狄测量的400%的巨大改进的两倍。