Python:怎么这么快?

时间:2010-01-23 23:21:10

标签: python largenumber computation

模块random中使用的Mersenne Twister的时期是(我被告知)2 ** 19937 - 1.作为二进制数,即19937'连续1(如果我不是错误)。 Python将它快速转换为十进制:

$ python -m timeit '2**19937'
10000000 loops, best of 3: 0.0271 usec per loop

$ python -m timeit -s 'result = 0' 'result += 2**19937'
100000 loops, best of 3: 2.09 usec per loop

我猜第二个版本是需要转换的版本吗?

而且它不仅仅是二元的。这也很快。 (而不是显示数字,我显示转换为字符串的小数的长度):

>>> import math
>>> N = 1000
>>> s = str((int(N*math.e))**(int(N*math.pi)))
>>> len(s)
10787
>>> N = 5000
>>> s = str((int(N*math.e))**(int(N*math.pi)))
>>> len(s)
64921

定时:

python -m timeit -s 'import math' -s 'N=1000' 's = str((int(N*math.e))**(int(N*math.pi)))'
10 loops, best of 3: 51.2 msec per loop

问题是:这是如何实际完成的?

我只是天真地留下深刻的印象?我发现Python shell在瞬间产生了大约5000个左右的位置,真是太壮观了。

编辑:

@ dalke和@truppo建议的其他时间

$ python -m timeit 'x=2' 'x**19937'
1000 loops, best of 3: 230 usec per loop
$ python -m timeit 'x=2' 'int(x**19937)'
1000 loops, best of 3: 232 usec per loop
$ python -m timeit 'x=2' 'str(x**19937)'
100 loops, best of 3: 16.6 msec per loop

$ python -m timeit -s 'result = 0' 'x = 2' 'result += x**19937'
1000 loops, best of 3: 237 usec per loop
$ python -m timeit -s 'result = 0' 'x = 2' 'result += x**19937' 'int(result)'
1000 loops, best of 3: 238 usec per loop
$ python -m timeit -s 'result = 0' 'x = 2' 'result += x**19937' 'str(result)'
100 loops, best of 3: 16.6 msec per loop

所以我认为result = 0; result += 2**19937可能会强制进行转换。

4 个答案:

答案 0 :(得分:6)

你的游行讨厌下雨,但之所以这么快是因为数学模块实际上没有用Python实现。

Python支持加载导出Python API的共享库,但是以其他语言实现。 math.so,它提供了从import math获得的模块,恰好是其中之一(_random.so也是如此)。

答案 1 :(得分:5)

编译为字节代码时,2**19937等常量表达式将优化为单个常量:

>>> def foo(): return 2**10
... 
>>> import dis
>>> dis.dis(foo)
  1           0 LOAD_CONST               3 (1024)
              3 RETURN_VALUE        
>>> 

改为考虑:

[~] python -m timeit 'x=2' 'x**19937'
1000 loops, best of 3: 210 usec per loop

答案 2 :(得分:4)

  

Python将它快速转换为十进制。

我不知道Python,但不,它不需要那样做。 2 ^ 19937不需要计算,它只是一个带有19937的二进制移位(“<<”),所以它非常快。只有当你以十进制打印时才需要实际的转换,而且速度要慢得多。

编辑: 如果数字基数与指数基数相同,则指数可以与移位(=移动点)相同。

10 ^ -1 = 0.1 10 ^ 0 = 1
10 ^ 1 = 10
10 ^ 2 = 100
10 ^ 3 = 1000
10 ^ n = 1(n个零)

你看到用指数n取10的取幂只是改变了数字。现在计算机大多使用内部基数2(位),因此计算2 ^ 19937在位置19937设置一个位(从零开始计数位位置)。
作为附加信息:转换为十进制通常通过征服除法算法实现,该算法连续地将数字除以10的幂。正如你看到的, 实际转换速度减慢了50万。

第二个例子更有意思:当你用大整数m来计算m ^ n时,n最快的方法是连续乘以它并存储临时结果。

示例:10 ^ 345

a = 10 ^ 2
b = a a = 10 ^ 4
c = b
b = 10 ^ 16
d = c * c = 10 ^ 256

result = d c c c c c c c c b b *表10

(可以进一步优化:参见Knuth,Seminumerical Algorithms)

所以你只需要很长的乘法,它们可以很好地计算 有效。

编辑:乘法的确切实现取决于:除了正常的学校乘法Karatsuba,Tom-Cooke和Schoenhagen-Strasse(FFT)乘法是 使用

答案 3 :(得分:0)

我对在Python中实现实际的方式几乎一无所知,但鉴于这基本上是原始的乘法和对数,即使数量非常大,我也不会感到非常惊讶

有一些任意精度数学库,例如GMP,它们以非常有效的方式实现了各种各样的操作,并在装配中进行了优化,仅用于此目的。