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

更新

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

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

我的电子邮件:

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

他的回答:

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


当前回答

优化的第一条规则:不要这么做 第二条规则:先别这么做

以下是一些计时(Python 2.5.2, Windows):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.445 usec per loop

$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.574 usec per loop

$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.727 usec per loop

这个测试表明x**。5比√(x)略快。

对于Python 3.0,结果正好相反:

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.803 usec per loop

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.695 usec per loop

$ \Python30\python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.761 usec per loop

Math.sqrt (x)总是比x**快。5在另一台机器上(Ubuntu, Python 2.6和3.1):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.173 usec per loop
$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.115 usec per loop
$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.158 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.194 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.123 usec per loop
$ python3.1 -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.157 usec per loop

其他回答

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

 from math import sqrt

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

有趣的是,做

 import math
 sqrt = math.sqrt

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


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

有人评论《雷神之锤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

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

克劳狄的结果和我的不一样。我在一台旧的P4 2.4Ghz机器上使用Ubuntu上的Python 2.6…以下是我的结果:

>>> timeit1()
Took 0.564911 seconds
>>> timeit2()
Took 0.403087 seconds
>>> timeit1()
Took 0.604713 seconds
>>> timeit2()
Took 0.387749 seconds
>>> timeit1()
Took 0.587829 seconds
>>> timeit2()
Took 0.379381 seconds

对我来说,SQRT一直都更快……甚至Codepad.org现在似乎也同意,在本地环境下,根号rt更快(http://codepad.org/6trzcM3j)。Codepad目前运行的是Python 2.5。也许克劳狄第一次回答的时候,他们使用的是2.4或更老的版本?

事实上,即使使用math.sqrt(i)来代替arg(i),我仍然可以得到更好的sqrt。在本例中,timeit2()在我的机器上花费了0.53到0.55秒,这仍然比timeit1的0.56-0.60秒要好。

我会说,在现代Python中,使用数学。一定要把它带入本地环境,或者用somevar=math。或者从数学导入根号。

不管怎样(见吉姆的回答)。在我的机器上,运行python 2.5:

PS C:\> python -m timeit -n 100000 10000**.5
100000 loops, best of 3: 0.0543 usec per loop
PS C:\> python -m timeit -n 100000 -s "import math" math.sqrt(10000)
100000 loops, best of 3: 0.162 usec per loop
PS C:\> python -m timeit -n 100000 -s "from math import sqrt" sqrt(10000)
100000 loops, best of 3: 0.0541 usec per loop