如何提高估计 Pi 的性能 Python

How to increase the performance for estimating `Pi`in Python

为了估算Pi的值,我在Python中写了下面的代码。它被称为 Monte Carlo 方法。显然,通过增加样本数量,代码会变慢,我假设代码中最慢的部分在采样部分。 我怎样才能让它更快?

from __future__ import division
import numpy as np

a = 1
n = 1000000

s1 = np.random.uniform(0,a,n)
s2 = np.random.uniform(0,a,n)

ii=0
jj=0

for item in range(n):
    if ((s1[item])**2 + (s2[item])**2) < 1:
        ii = ii + 1

print float(ii*4/(n))

您是否建议其他(可能更快)代码?

这里的瓶颈实际上是你的 for 循环。 Python for 循环相对较慢,因此如果您需要迭代超过一百万个项目,则可以通过完全避免它们来提高速度。在这种情况下,这很容易。而不是这个:

for item in range(n):
    if ((s1[item])**2 + (s2[item])**2) < 1:
        ii = ii + 1

这样做:

ii = ((s1 ** 2 + s2 ** 2) < 1).sum()

之所以有效,是因为 numpy 内置了对优化数组操作的支持。循环发生在 c 而不是 python,所以它要快得多。我做了一个快速测试,所以你可以看到不同之处:

>>> def estimate_pi_loop(x, y):
...     total = 0
...     for i in xrange(len(x)):
...         if x[i] ** 2 + y[i] ** 2 < 1:
...             total += 1
...     return total * 4.0 / len(x)
... 
>>> def estimate_pi_numpy(x, y):
...     return ((x ** 2 + y ** 2) < 1).sum()
... 
>>> %timeit estimate_pi_loop(x, y)
1 loops, best of 3: 3.33 s per loop
>>> %timeit estimate_pi_numpy(x, y)
100 loops, best of 3: 10.4 ms per loop

这里有一些可能的操作类型示例,只是为了让您了解其工作原理。

对数组求平方:

>>> a = numpy.arange(5)
>>> a ** 2
array([ 0,  1,  4,  9, 16])

添加数组:

>>> a + a
array([0, 2, 4, 6, 8])

比较数组:

>>> a > 2
array([False, False, False,  True,  True], dtype=bool)

求和布尔值:

>>> (a > 2).sum()
2

正如您可能意识到的那样,有更快的方法来估计 Pi,但我承认我一直很欣赏这种方法的简单性和有效性。

您已经分配了 numpy 数组,因此您应该利用它们。

for item in range(n):
    if ((s1[item])**2 + (s2[item])**2) < 1:
        ii = ii + 1

变成

s1sqr = s1*s1
s2sqr = s2*s2
s_sum = s1sqr + s2sqr
s_sum_bool = s_sum < 1
ii = s_sum_bool.sum()
print float(ii*4/(n))

对数组进行平方,求和,检查总和是否小于 1,然后对布尔值求和(false = 0,true = 1)以获得符合条件的总数。

我赞成 senderle 的回答,但如果您不想过多更改代码:

numba 是为此目的而设计的库。

只需将您的算法定义为一个函数,并添加 @jit 装饰器:

 from __future__ import division
 import numpy as np
 from numba import jit

 a = 1
 n = 1000000

 s1 = np.random.uniform(0,a,n)
 s2 = np.random.uniform(0,a,n)

 @jit
 def estimate_pi(s1, s2):
     ii = 0
     for item in range(n):
         if ((s1[item])**2 + (s2[item])**2) < 1:
             ii = ii + 1
     return float(ii*4/(n))

 print estimate_pi(s1, s2)

在我的笔记本电脑上,n = 100000000 的速度提高了大约 20 倍,n = 1000000 的速度提高了 3 倍。