Python 中的指数:x**y 与 math.pow(x, y)
- 2025-04-17 09:02:00
- admin 原创
- 18
问题描述:
math.pow
使用或运算符哪一个效率更高**
?我什么时候应该使用其中一个?
到目前为止,我知道x**y
可以返回一个int
或一个,float
如果你使用小数,函数pow
将返回一个浮点数
import math
print( math.pow(10, 2) )
print( 10. ** 2 )
解决方案 1:
使用幂运算符**
会更快,因为它不会产生函数调用的开销。如果你反汇编一下 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
需要确保此属性。
让我们举个例子:我们有两个数字,i
和j
,并且不知道它们是浮点数还是整数。但我们想要得到浮点数的结果i^j
。那么我们有什么选择呢?
我们可以将至少一个参数转换为浮点数,然后执行
i ** j
。我们可以执行
i ** j
并将结果转换为浮点数(当i
或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()
。
此外,值得注意的是,可以通过为自定义类型实现特殊的(and ) 方法来覆盖**
and的行为。所以,如果你不想这样做(无论出于何种原因),使用 using是不会实现这一点的。pow
`__pow____rpow__
math.pow`
解决方案 2:
pow() 函数允许您添加第三个参数作为模数。
例如:我最近在执行以下任务时遇到了内存错误:
2**23375247598357347582% 23375247598357347583
相反,我这样做了:
pow(2, 23375247598357347582, 23375247598357347583)
这仅需几毫秒即可返回结果,而不像普通指数那样需要耗费大量的时间和内存。因此,在处理大数和并行模数时,pow() 效率更高;然而,在处理不带模数的较小数时,** 效率更高。
解决方案 3:
仅针对协议:该运算符相当于内置函数**
的双参数版本,如果前两个参数是整数,则该函数接受可选的第三个参数(模数)。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 是奇数(因此它的所有整数幂都是奇数)。该版本使用了IEEE-754双精度math.pow
的有限精度(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
函数。
一些在线示例
http://ideone.com/qaDWRd(余数错误
math.pow
)http://ideone.com/g7J9Un(int
pow
值的性能较低)http://ideone.com/KnEtXj
pow
(浮点值性能略低)
更新(不可避免的修正): 我删除了和
的时间比较,因为它会给出错误的结果,而例如和之间的比较本来是公平的(尽管这不符合-module 函数的实际用途)。我添加了一个更好的比较,并指出了导致 限制的细节。math.pow(2,100)
`pow(2,100)math.pow
pow(2,50)math.pow(2,50)
math`math.pow
解决方案 4:
**
确实比 更快math.pow()
,但如果您想要像示例中那样的简单二次函数,那么使用乘积会更快。
10.*10.
会更快
10.**2
对于一次操作(使用),差异并不大且不明显timeit
,但是对于大量操作,差异可能会很大。
解决方案 5:
嗯,它们确实用于不同的任务。
当您需要整数运算时, 使用pow
(相当于带有两个参数)。x ** y
math.pow
如果任一参数都是浮点数,并且您想要浮点输出, 则使用。
pow
有关和之间的差异的讨论math.pow
,请参阅此问题。
解决方案 6:
运算符**
(与相同pow()
)可用于计算非常大的整数。
>>> 2 ** 12345
164171010688258216356020741663906501410127235530735881272116103087925094171390144280159034536439457734870419127140401667195510331085657185332721089236401193044493457116299768844344303479235489462...
>>> math.pow(2, 12345)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: math range error
扫码咨询,免费领取项目管理大礼包!