[英]What's the fastest, most efficient, and pythonic way to perform a mathematical sigma sum?
Let's say that I want to perform a mathematical summation, say the Madhava–Leibniz formula for π , in Python: 假设我想在Python中执行数学求和,比如说π的Madhava-Leibniz公式 :
Within a function called Leibniz_pi(), I could create a loop to calculate the n th partial sum, such as: 在一个名为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
I'm assuming it would be faster to use something like xrange() instead of range(). 我假设使用像xrange()而不是range()这样的东西会更快。 Would it be even faster to use numpy and its built in numpy.sum() method?
使用numpy及其内置的numpy.sum()方法会更快吗? What would such an example look like?
这样的例子会是什么样的?
I guess most people will define the fastest solution by @zero using only numpy as the most pythonic, but it is certainly not the fastest. 我想大多数人都会定义@zero最快的解决方案,只使用numpy作为最pythonic,但它肯定不是最快的。 With some additional optimizations you can beat the already fast numpy implementation by a factor of 50.
通过一些额外的优化,您可以将已经快速的numpy实现击败50倍。
Using only Numpy (@zero) 仅使用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)
Make use of numexpr 利用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)
Compile your function using Numba 使用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)
Edit, optimizing away the costly (-1)**n 编辑,优化昂贵的(-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)
3 suggestions (with speed computation): 3条建议(带速度计算):
define the Leibniz point not the cumulative sum: 定义莱布尼兹点而不是累积总和:
def Leibniz_point(n):
val = (-1)**n / (2*n + 1)
return val
1) sum a list comprehension 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) standard for loop 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) use a numpy array (suggested) 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)
In general, for operations involving collections of more than a few elements, numpy
will be faster. 通常,对于涉及多个元素集合的操作,
numpy
会更快。 A simple numpy
implementation could be something like this: 一个简单的
numpy
实现可能是这样的:
def leibniz(n):
a = np.arange(n + 1)
return (((-1.0) ** a) / (2 * a + 1)).sum()
Note that you must specify that the numerator is a float
with 1.0
on Python 2. On Python 3, 1
will be fine. 请注意,您必须在Python 2上指定分子是一个带有
1.0
的float
。在Python 3上, 1
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.