Python中的时序模块化指数:语法与函数

时间:2016-06-07 17:47:01

标签: python python-2.7 python-3.x

在Python中,如果内置pow()函数与3个参数一起使用,则最后一个用作取幂的模数,从而导致Modular exponentiation操作。

换句话说,pow(x, y, z)相当于(x ** y) % z,但相应于Python帮助,pow()可能效率更高。

当我对两个版本进行计时时,我得到了相反的结果,pow()版本似乎比等效语法慢:

Python 2.7:

>>> import sys
>>> print sys.version
2.7.11 (default, May  2 2016, 12:45:05) 
[GCC 4.9.3]
>>> 
>>> help(pow)

Help on built-in function pow in module __builtin__:  <F2> Show Source 

pow(...)
    pow(x, y[, z]) -> number

    With two arguments, equivalent to x**y.  With three arguments,
    equivalent to (x**y) % z, but may be more efficient (e.g. for longs).

>>> 
>>> import timeit
>>> st_expmod = '( 65537 ** 767587 ) % 14971787'
>>> st_pow    = 'pow(65537, 767587, 14971787)'
>>> 
>>> timeit.timeit(st_expmod)
0.016651153564453125
>>> timeit.timeit(st_expmod)
0.016621112823486328
>>> timeit.timeit(st_expmod)
0.016611099243164062
>>> 
>>> timeit.timeit(st_pow)
0.8393168449401855
>>> timeit.timeit(st_pow)
0.8449611663818359
>>> timeit.timeit(st_pow)
0.8767969608306885
>>> 

Python 3.4:

>>> import sys
>>> print(sys.version)
3.4.3 (default, May  2 2016, 12:47:35) 
[GCC 4.9.3]
>>> 
>>> help(pow)

Help on built-in function pow in module builtins:

pow(...)
    pow(x, y[, z]) -> number

    With two arguments, equivalent to x**y.  With three arguments,
    equivalent to (x**y) % z, but may be more efficient (e.g. for ints).

>>> 
>>> import timeit
>>> st_expmod = '( 65537 ** 767587 ) % 14971787'
>>> st_pow    = 'pow(65537, 767587, 14971787)'
>>> 
>>> timeit.timeit(st_expmod)
0.014722830994287506
>>> timeit.timeit(st_expmod)
0.01443593599833548
>>> timeit.timeit(st_expmod)
0.01485627400688827
>>> 
>>> timeit.timeit(st_pow)
3.3412855619972106
>>> timeit.timeit(st_pow)
3.2800855879904702
>>> timeit.timeit(st_pow)
3.323372773011215
>>>

Python 3.5:

>>> import sys
>>> print(sys.version)
3.5.1 (default, May  2 2016, 14:34:13) 
[GCC 4.9.3
>>> 
>>> help(pow)

Help on built-in function pow in module builtins:

pow(x, y, z=None, /)
    Equivalent to x**y (with two arguments) or x**y % z (with three arguments)

    Some types, such as ints, are able to use a more efficient algorithm when
    invoked using the three argument form.

>>> 
>>> import timeit
>>> st_expmod = '( 65537 ** 767587 ) % 14971787'
>>> st_pow    = 'pow(65537, 767587, 14971787)'
>>> 
>>> timeit.timeit(st_expmod)
0.014827249979134649
>>> timeit.timeit(st_expmod)
0.014763347018742934
>>> timeit.timeit(st_expmod)
0.014756042015505955
>>> 
>>> timeit.timeit(st_pow)
3.6817933860002086
>>> timeit.timeit(st_pow)
3.6238356370013207
>>> timeit.timeit(st_pow)
3.7061628740048036
>>> 

上述数字的解释是什么?

修改

在答案之后我看到在st_expmod版本中,计算没有在运行时执行,而是由解析器和表达式变为常量..

使用Python2中@ user2357112建议的修复:

>>> timeit.timeit('(a**b) % c', setup='a=65537; b=767587; c=14971787', number=150)
370.9698350429535
>>> timeit.timeit('pow(a, b, c)', setup='a=65537; b=767587; c=14971787', number=150)
0.00013303756713867188

2 个答案:

答案 0 :(得分:7)

您实际上并未使用**%对计算进行计时,因为字节码编译器会对结果进行常量折叠。避免这样做:

timeit.timeit('(a**b) % c', setup='a=65537; b=767587; c=14971787')

并且pow版本将获胜。

答案 1 :(得分:4)

@ user2357112正确回答的一些问题:

首先,如果您手动调用两个字符串eval(),很明显st_expmod速度非常慢。

其次,它有时(就像在这种情况下)付费查看生成的代码。像这样:

>>> def f():
...     return ( 65537 ** 767587 ) % 14971787

>>> from dis import dis
>>> dis(f)
  2           0 LOAD_CONST               5 (10686982)
              3 RETURN_VALUE
>>>

你真的不需要了解Python的字节代码,看看编译的代码只是加载答案(10686982)并返回它 - 所有的实际工作都是在编译代码时完成的