什么是最快,最有效和最Python方式执行数学西格玛和的方式?

时间:2019-05-07 04:41:56

标签: python loops numpy math sum

假设我想用Python执行数学求和,例如Madhava–Leibniz formula for π

generated by latex.codecogs.com/gif.latex?%5Cfrac%7B%5Cpi%7D%7B4%7D%5Capprox%20%5Csum_%7Bk%3D0%7D%5E%7Bn%7D%20%5Cfrac%7B%28-1%29%5Ek%7D%7B2k+1%7D

在名为Leibniz_pi()的函数中,我可以创建一个循环来计算第n个 部分和,例如:

def Leibniz_pi(n):
    nth_partial_sum = 0       #initialize the variable
    for i in range(n+1):
        nth_partial_sum += ((-1)**i)/(2*i + 1)
    return nth_partial_sum

我假设使用xrange()代替range()会更快。使用numpy及其内置的numpy.sum()方法会更快吗?这样的例子是什么样的?

3 个答案:

答案 0 :(得分:4)

我想大多数人将仅使用numpy作为最大的pythonic定义@zero最快的解决方案,但它肯定不是最快的。通过一些其他优化,您可以将已经快的numpy实现击败50倍。

仅使用Numpy(@zero)

import numpy as np
import numexpr as ne
import numba as nb

def Leibniz_point(n):
    val = (-1)**n / (2*n + 1)
    return val

%timeit Leibniz_point(np.arange(1000)).sum()
33.8 µs ± 203 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

使用numexpr

n=np.arange(1000)
%timeit ne.evaluate("sum((-1)**n / (2*n + 1))")
21 µs ± 354 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

使用Numba编译函数

# with error_model="numpy", turns off division-by-zero checks 
@nb.njit(error_model="numpy",cache=True)
def Leibniz_pi(n):
  nth_partial_sum = 0.       #initialize the variable as float64
  for i in range(n+1):
      nth_partial_sum += ((-1)**i)/(2*i + 1)
  return nth_partial_sum

%timeit Leibniz_pi(999)
6.48 µs ± 38.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

编辑,优化代价高昂的(-1)** n

import numba as nb
import numpy as np

#replacement for the much more costly (-1)**n
@nb.njit()
def sgn(i):
    if i%2>0:
        return -1.
    else:
        return 1.

# with error_model="numpy", turns off the division-by-zero checks
#
# fastmath=True makes SIMD-vectorization in this case possible
# floating point math is in general not commutative
# e.g. calculating four times sgn(i)/(2*i + 1) at once and then the sum 
# is not exactly the same as doing this sequentially, therefore you have to
# explicitly allow the compiler to make the optimizations

@nb.njit(fastmath=True,error_model="numpy",cache=True)
def Leibniz_pi(n):
    nth_partial_sum = 0.       #initialize the variable
    for i in range(n+1):
        nth_partial_sum += sgn(i)/(2*i + 1)
    return nth_partial_sum

%timeit Leibniz_pi(999)
777 ns ± 5.36 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

答案 1 :(得分:3)

3条建议(使用速度计算):

定义莱布尼兹,而不是累计和:

def Leibniz_point(n):
    val = (-1)**n / (2*n + 1)
    return val

1)对列表理解求和

%timeit sum([Leibniz_point(n) for n in range(100)])
58.8 µs ± 825 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit sum([Leibniz_point(n) for n in range(1000)])
667 µs ± 3.41 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

2)循环标准

%%timeit
sum = 0
for n in range(100):
    sum += Leibniz_point(n)
61.8 µs ± 4.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%%timeit
sum = 0
for n in range(1000):
    sum += Leibniz_point(n)
    729 µs ± 43.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

3)使用numpy数组(建议)

%timeit Leibniz_point(np.arange(100)).sum()
11.5 µs ± 866 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit Leibniz_point(np.arange(1000)).sum()
61.8 µs ± 3.69 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

答案 2 :(得分:2)

通常,对于涉及多个元素集合的操作,numpy会更快。一个简单的numpy实现可能是这样的:

def leibniz(n):
    a = np.arange(n + 1)
    return (((-1.0) ** a) / (2 * a + 1)).sum()

请注意,您必须在Python 2上指定分子为float的{​​{1}}。在Python 3上,1.0会很好。