[英]Efficient summation in Python
我试图在 Python 中有效地计算求和的总和:
WolframAlpha能够计算出过高的 n 值: sum of sum 。
我有两种方法: for循环方法和 np.sum 方法。 我认为 np.sum 方法会更快。 然而,它们是相同的,直到一个大的 n,之后 np.sum 有溢出错误并给出错误的结果。
我试图找到计算这个总和的最快方法。
import numpy as np
import time
def summation(start,end,func):
sum=0
for i in range(start,end+1):
sum+=func(i)
return sum
def x(y):
return y
def x2(y):
return y**2
def mysum(y):
return x2(y)*summation(0, y, x)
n=100
# method #1
start=time.time()
summation(0,n,mysum)
print('Slow method:',time.time()-start)
# method #2
start=time.time()
w=np.arange(0,n+1)
(w**2*np.cumsum(w)).sum()
print('Fast method:',time.time()-start)
这是一个非常快速的方法:
result = ((((12 * n + 45) * n + 50) * n + 15) * n - 2) * n // 120
我是如何到达那里的:
x*(x+1)//2
。 所以整个事情变成sum(x**2 * x*(x+1)//2 for x in range(n+1))
。sum(x**4 + x**3 for x in range(n+1)) // 2
。sum(x**4)
和sum(x**3)
的公式。(12*n**5 + 45*n**4 + 50*n**3 + 15*n**2 - 2*n) // 120
。在快速 numpy 方法中,您需要指定dtype=np.object
以便 numpy 不会将 Python int
转换为它自己的 dtypes( np.int64
或其他)。 它现在会给你正确的结果(检查它高达 N=100000)。
# method #2
start=time.time()
w=np.arange(0, n+1, dtype=np.object)
result2 = (w**2*np.cumsum(w)).sum()
print('Fast method:', time.time()-start)
您的快速解决方案比慢速解决方案快得多。 是的,对于较大的 N,但在 N=100 时,它的速度要快 8 倍:
start=time.time()
for i in range(100):
result1 = summation(0, n, mysum)
print('Slow method:', time.time()-start)
# method #2
start=time.time()
for i in range(100):
w=np.arange(0, n+1, dtype=np.object)
result2 = (w**2*np.cumsum(w)).sum()
print('Fast method:', time.time()-start)
Slow method: 0.06906533241271973
Fast method: 0.008007287979125977
像这样将 Python 与 WolframAlpha 进行比较是不公平的,因为 Wolfram 会在计算之前简化方程式。
@Kelly Bundy 的回答很棒,但是如果您像我一样使用计算器来计算2 + 2
,那么您会喜欢 SymPy。 ❤
from sympy import summation
from sympy import symbols
n, x, y = symbols("n,x,y")
eq = summation(x ** 2 * summation(y, (y, 0, x)), (x, 0, n))
eq.evalf(subs={"n": 1000})
会给你预期的结果: 100375416791650
。
请注意 SymPy 如何为您简化表达式:
eq
所有答案都使用数学来简化或实现 python 中的循环,试图使 cpu 最优,但它们不是内存最优的。
这是一个简单的实现,不使用任何内存高效的数学简化
def function5():
inner_sum = np.float()
result = np.float()
for x in range(0, n + 1):
inner_sum += x
result += x ** 2 * inner_sum
return result
与 dankal444 的其他解决方案相比,速度相当慢:
method 2 | 31 µs ± 2.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
method 3 | 116 µs ± 538 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
method 4 | 91 µs ± 356 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
function 5 | 217 µs ± 1.14 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
顺便说一下,如果你用 numba jit 函数(可能有更好的选择):
from numba import jit
function5 = jit(nopython=True)(function5)
你得到
59.8 ns ± 0.209 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
在评论中,您提到它实际上是 f(x) 和 g(y) 而不是 x 2和 y。 如果您只需要该和的近似值,您可以假设这些和是中点黎曼和,这样您的和就可以用二重积分近似 ∫ -.5 n+.5 f(x) ∫ -.5 x+.5 g(y) dy dx。
使用原始 f(x)=x 2和 g(y)=y,这简化为 n 5 /10+3n 4 /8+n 3 /2+5n 2 /16+3n/32+1/160,即与正确结果相差 n 3 /12+3n 2 /16+53n/480+1/160。
基于此,我怀疑 (actual-integral)/actual 将是 max(f'',g'')*O(n -2 ),但我无法证明。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.