使用 math.pow
还是 **
运算符更有效率?我应该在什么情况下使用其中之一?
目前为止,我知道 x**y
可以返回一个整数或浮点数(如果使用小数)。而函数 pow
则会返回一个浮点数。
import math
print( math.pow(10, 2) )
print( 10. ** 2 )
使用幂运算符**
将更快,因为它不需要函数调用的开销。如果您反汇编Python代码,您可以看到这一点:
>>> dis.dis('7. ** i')
1 0 LOAD_CONST 0 (7.0)
3 LOAD_NAME 0 (i)
6 BINARY_POWER
7 RETURN_VALUE
>>> dis.dis('pow(7., i)')
1 0 LOAD_NAME 0 (pow)
3 LOAD_CONST 0 (7.0)
6 LOAD_NAME 1 (i)
9 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
12 RETURN_VALUE
>>> dis.dis('math.pow(7, i)')
1 0 LOAD_NAME 0 (math)
3 LOAD_ATTR 1 (pow)
6 LOAD_CONST 0 (7)
9 LOAD_NAME 2 (i)
12 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
15 RETURN_VALUE
请注意,我在这里使用变量i
作为指数,因为像7. ** 5
这样的常量表达式实际上是在编译时计算的。
现实中,这种差异并不太重要,您可以在计时时看到:
>>> from timeit import timeit
>>> timeit('7. ** i', setup='i = 5')
0.2894785532627111
>>> timeit('pow(7., i)', setup='i = 5')
0.41218495570683444
>>> timeit('math.pow(7, i)', setup='import math; i = 5')
0.5655053168791255
因此,尽管pow
和math.pow
的速度要慢大约两倍,但它们仍然足够快,不需要太在意。除非您确实可以将指数运算识别为瓶颈,否则如果清晰度降低,选择一种方法而不是另一种方法就没有理由。这尤其适用于pow
为例提供了一个内置的模操作。
Alfe在上面的评论中提出了一个很好的问题:
timeit
显示,在所有情况下,math.pow
都比**
慢。那么math.pow()
到底有什么用呢?有人有什么想法吗?
math.pow
与内置的pow
和幂运算符**
最大的区别是它总是使用浮点语义。因此,如果因为某种原因,您想确保得到一个浮点数作为结果,则math.pow
将确保这个属性。
让我们考虑一个例子:我们有两个数字和,并且不知道它们是浮点数还是整数。但是我们想要一个浮点结果j^j。那么我们有什么选择?
math.pow
。因此,让我们进行测试:
>>> timeit('float(i) ** j', setup='i, j = 7, 5')
0.7610865891750791
>>> timeit('i ** float(j)', setup='i, j = 7, 5')
0.7930400942188385
>>> timeit('float(i ** j)', setup='i, j = 7, 5')
0.8946636625872202
>>> timeit('math.pow(i, j)', setup='import math; i, j = 7, 5')
0.5699394063529439
如您所见,math.pow
实际上更快!而且如果您考虑一下,函数调用的开销也消失了,因为在所有其他替代方案中我们都需要调用 float()
。
另外值得注意的是,**
和 pow
的行为可以通过为自定义类型实现特殊的 __pow__
(和 __rpow__
)方法来重载。因此,如果您不希望这样做(无论出于何种原因),使用 math.pow
将不会实现这一点。
pow()函数允许您添加第三个参数作为模数。
例如:最近我在执行
2**23375247598357347582 % 23375247598357347583
时遇到了内存错误,所以我使用:
pow(2, 23375247598357347582, 23375247598357347583)
这只需几毫秒就能返回结果,而不是普通指数需要的大量时间和内存。因此,在处理大数和并行模数时,pow()更有效率,但在处理没有模数的小数时, ** 更有效率。
仅供参考: **
运算符相当于两个参数版本的 内置的 pow
函数, 如果前两个参数是整数,则 pow
函数 接受一个可选的第三个参数(模数)。
因此,如果您想计算幂的余数,请使用内置函数。对于合理大小的参数,math.pow
将给出错误的结果:
import math
base = 13
exp = 100
mod = 2
print math.pow(base, exp) % mod
print pow(base, exp, mod)
0.0
,显然这是不可能的,因为13是奇数(因此它的所有整数幂也是奇数)。math.pow
版本使用IEEE-754双精度(52位尾数,略小于16个十进制位数)的有限精度导致出现错误。math.pow
也可以更快:>>> import timeit
>>> min(timeit.repeat("pow(1.1, 9.9)", number=2000000, repeat=5))
0.3063715160001266
>>> min(timeit.repeat("math.pow(1.1, 9.9)", setup="import math", number=2000000, repeat=5))
0.2647279420000359
< p > < code > math.pow 函数在工程应用中有其优势(至今仍然如此),但是对于数论应用,您应该使用内置的< code > pow 函数。
一些在线示例
math.pow
时余数错误)pow
在int值上性能较低)pow
在float值上略有性能下降)math.pow(2,100)
和pow(2,100)
的时间比较,因为math.pow
会给出错误的结果,而例如pow(2,50)
和math.pow(2,50)
之间的比较会是公平的(虽然不是math
模块函数的实际用途)。我添加了一个更好的比较,并说明了导致math.pow
限制的细节。**
"确实比"math.pow()
"更快,但如果您想要一个像您的示例中那样简单的二次函数,使用乘积甚至更快。"10.*10.
将会更快
10.**2
虽然在单次操作(使用timeit
)中差异不大且不明显,但在大量操作时会产生明显的影响。
实际上,它们用于不同的任务。
当您需要整数运算时,请使用pow
(相当于带有两个参数的x ** y
)。
如果任一参数为浮点数且您需要浮点输出,则使用math.pow
。
如需了解pow
和math.pow
之间的差异,请参见此问题的讨论。
x*x
而不是x**2
或pow(x, 2)
。操作符 **
(与pow()
相同)可用于计算非常大的整数。
>>> 2 ** 12345
164171010688258216356020741663906501410127235530735881272116103087925094171390144280159034536439457734870419127140401667195510331085657185332721089236401193044493457116299768844344303479235489462...
>>> math.pow(2, 12345)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: math range error
timeit
来找出答案呢? - mgilsonmath.pow()
比**
的速度慢。那么math.pow()
到底有什么用呢?有人知道它在哪些情况下可能有优势吗? - Alfemath.pow
更快。 - Wolfpow
和math.pow
的比较,但我想要比较math.pow
和**
操作符。你的比较可以加上第三个版本,然后再次使用**
,这样就能打败其他所有选项:import timeit; print timeit.timeit("math.pow(2, 100)",setup='import math'), timeit.timeit("pow(2, 100)"), timeit.timeit("2 ** 100")
→0.170357942581 1.00546097755 0.013473033905
。 - Alfe