Python 中的指数: x * * y vs math.pow (x,y)

使用 math.pow**操作符哪一个更有效? 什么时候应该使用一个而不是另一个?

到目前为止,我知道如果使用小数,x**y可以返回 intfloat 函数 pow将返回一个浮点数

import math


print( math.pow(10, 2) )


print( 10. ** 2 )
76095 次浏览

它们是用来完成不同的任务的,真的。

如果需要整数算术,请使用 pow(相当于带有两个参数的 x ** y)。

如果任何一个参数是 float,并且您想要浮点数输出,则使用 math.pow

有关 powmath.pow之间的差异的讨论,请参阅 有个问题

使用电源操作符 **会更快,因为它不会有函数调用的开销。如果您反汇编 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

因此,尽管 powmath.pow的速度大约是它们的两倍,但它们仍然足够快,不会太在意。除非您能够将指数化确定为一个瓶颈,否则,如果清晰度降低,就没有理由选择一种方法而不选择另一种方法。这一点尤其适用,因为例如,pow提供了一个集成的模除。


阿尔菲在上面的评论中提出了一个很好的问题:

timeit显示 math.pow在所有情况下都慢于 **math.pow()到底有什么用?有人知道它在哪里能发挥作用吗?

与内置的 pow和电源操作符 **相比,math.pow的最大区别在于 一直都是使用浮点语义。因此,如果出于某种原因,希望确保返回一个 float,那么 math.pow将确保该属性。

让我们想一个例子: 我们有两个数字,ij,并且不知道它们是浮点数还是整数。但是我们希望得到 i^j的浮点结果。那我们还有什么选择?

  • 我们可以将至少一个参数转换为浮点数,然后执行 i ** j
  • 我们可以执行 i ** j并将结果转换为浮点数(当 ij为浮点数时,将自动使用浮点数指数,因此结果是相同的)。
  • 我们可以用 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__(和 __rpow__)方法,可以覆盖 **pow的行为。因此,如果你不想(不管什么原因) ,使用 math.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

math.pow函数在工程应用中具有(并且仍然具有)优势,但是对于大量的理论应用,您应该使用内置的 pow函数。


一些网上的例子


更新(不可避免的修正) :
我删除了 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 ()函数允许您添加第三个参数作为模。

例如: 我最近在做的时候遇到了一个记忆错误

2 * * 23375247598357347582% 23375247598357347583

相反,我做到了:

pow(2, 23375247598357347582, 23375247598357347583)

这只需要几毫秒就可以返回,而不是普通指数所需要的大量时间和内存。因此,当处理大数和平行模数时,pow ()更有效,但是当处理没有模数的小数时,* * 更有效。