我想了很久了。就像题目说的,哪个更快,是实际函数还是简单地取1 / 2次幂?
更新
这不是一个过早优化的问题。这只是一个底层代码如何实际工作的问题。Python代码的工作原理是什么?
我给Guido van Rossum发了一封邮件,因为我真的很想知道这些方法的区别。
我的电子邮件:
在Python中至少有3种方法来求平方根:math。返回值,
'**'运算符和pow(x,.5)。我只是好奇它们之间的区别
每一个的实现。说到效率
是更好吗?
他的回答:
Pow和**是等价的;数学。根号方根不适用于复数,
并链接到C的sqrt()函数。至于哪一个是
快点,我不知道……
克劳狄的结果和我的不一样。我在一台旧的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。或者从数学导入根号。
有人评论《雷神之锤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