繁体   English   中英

Python 中的高效求和

[英]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

我是如何到达那里的:

  1. 将内和重写为众所周知的x*(x+1)//2 所以整个事情变成sum(x**2 * x*(x+1)//2 for x in range(n+1))
  2. 重写sum(x**4 + x**3 for x in range(n+1)) // 2
  3. 查找sum(x**4)sum(x**3)的公式。
  4. 将产生的混乱简化为(12*n**5 + 45*n**4 + 50*n**3 + 15*n**2 - 2*n) // 120
  5. 霍纳它。

公式是否发生变化,或者您想有效地计算这个特定的总和?

对于后者,您可以使用高斯公式。

使用这个公式,你可以剪一个循环。 使算法运行时间为 O(n) 而不是 O(n^2)

在快速 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM