我想了很久了。就像题目说的,哪个更快,是实际函数还是简单地取1 / 2次幂?
更新
这不是一个过早优化的问题。这只是一个底层代码如何实际工作的问题。Python代码的工作原理是什么?
我给Guido van Rossum发了一封邮件,因为我真的很想知道这些方法的区别。
我的电子邮件:
在Python中至少有3种方法来求平方根:math。返回值,
'**'运算符和pow(x,.5)。我只是好奇它们之间的区别
每一个的实现。说到效率
是更好吗?
他的回答:
Pow和**是等价的;数学。根号方根不适用于复数,
并链接到C的sqrt()函数。至于哪一个是
快点,我不知道……
你好!我刚刚创建了一个堆栈交换配置文件来参与这次对话!
我所做的事情可能看起来微不足道,但在评判之前请先听我说完:
实验条件:
离线(没有internet编译器问题)
保持系统状态尽可能稳定
在一次尝试中测试所有3个功能
对于原问题中陈述的每个函数,我运行了3个循环,每个循环5个迭代。我在每个循环中计算了从0到10^8的整数的平方根。
以下是调查结果:
时间:
√(x) < x**0.5 < pow(x, 0.5)
注:以两位数的秒差,超过10^8的非负
整数。
输出截图:
输出
我的结论是:
我觉得Guido的邮件很好地证明了这些时间。
考虑以下语句:
"math.sqrt()链接到C并且不接受复数"
**和pow()是等价的
因此,我们可以暗示**和pow()都有一定的开销成本,因为它们都必须检查传递的输入是否为复数,即使我们传递的是整数。此外,复数是Python内置的,使用Python编写Python代码是计算机上的任务。
值得注意的是,math.sqrt()的工作速度相对较快,因为它既不需要检查复数参数的麻烦,也因为它直接与C语言函数连接,C语言函数被证明比一般的Python快一点。
如果你对这个结论的看法与我不同,请告诉我!
代码:
import time
import math
print("x**0.5 : ")
for _ in range(5):
start = time.time()
for i in range(int(1e8)):
i**0.5
end = time.time()
print(end-start)
print("math.sqrt(x) : ")
for _ in range(5):
start = time.time()
for i in range(int(1e8)):
math.sqrt(i)
end = time.time()
print(end-start)
print("pow(x,0.5) : ")
for _ in range(5):
start = time.time()
for i in range(int(1e8)):
pow(i,0.5)
end = time.time()
print(end-start)
优化的第一条规则:不要这么做
第二条规则:先别这么做
以下是一些计时(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
在python 2.6中,(float).__pow__()函数使用C pow()函数,math.sqrt()函数使用C sqrt()函数。
在glibc编译器中,pow(x,y)的实现相当复杂,并且针对各种例外情况进行了很好的优化。例如,调用C pow(x,0.5)只调用sqrt()函数。
**或数学使用速度的差异。sqrt是由围绕C函数的包装器引起的,速度很大程度上取决于系统上使用的优化标志/C编译器。
编辑:
这是克劳狄算法在我机器上的结果。我得到了不同的结果:
zoltan@host:~$ python2.4 p.py
Took 0.173994 seconds
Took 0.158991 seconds
zoltan@host:~$ python2.5 p.py
Took 0.182321 seconds
Took 0.155394 seconds
zoltan@host:~$ python2.6 p.py
Took 0.166766 seconds
Took 0.097018 seconds
克劳狄的结果和我的不一样。我在一台旧的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。或者从数学导入根号。