你正试图这样做:
for i in range(N):
y[i+1] = (y[i] * 7 + x[i+1]) / 8
所以我很确定将其称为“Python 级别的循环”是值得的。来自Introduction of numpy vectorized operations:
NumPy 提供了高度优化的函数,用于对数字数组执行数学运算。在 Python 中执行大量迭代(例如,通过“for-loops”)以执行重复的数学计算几乎总是应该由在数组上使用向量化函数来代替。这为 NumPy 的整个设计范式提供了信息。
在您的情况下,没有直接的矢量化方法。至少,您可以尝试数学方法,因为这种关系广为人知
A * y[i+1] + B * y[i] == f(i) 形式的关系称为线性一阶非齐次差分递推方程
为了更进一步,您可以将其重新排列为:
y[n+1] = (7/8) * y[n] + x[n+1]/8
根据这个公式,解决方案的剩余部分如下所示:
y[1] = (7/8) * y[0] + x[1]/8
y[2] = 7/8 * [(7/8) * y[0] + x[1]/8] + x[2]/8 = (7/8)**2*y[0] + 7/8*x[1]/8 + x[2]/8
...
所以你可以得出结论
y[n] = (7/8)**n*y[0] + x[n]/8 + (7/8)*x[n-1]/8 + (7/8)**2*x[n-2]/8 + ... + (7/8)**(n-1)*x[1]
因此不再需要查找上一个项目 y[i-1] 以查找 y[i]。现在让我们根据general numpy broadcasting rules的公式,一口气找到[y[1], y[2], ... y[n]]。
您需要添加Y =
y[0]*(7/8)
y[0]*(7/8)**2
y[0]*(7/8)**3
...
y[0]*(7/8)**(n
带有产品V =的结果
1 0 0 0 ... 0
1 7/8 0 0 ... 0
1 7/8 (7/8)**2 0 ... 0
1 7/8 (7/8)**2 (7/8)**3 ... 0
. . . . .
· · · · ·
' ' ' ' '
1 7/8 (7/8)**2 (7/8)**3 ... (7/8)**(n-1)
和X =
1 0 0 0 0 0 0 0 0 0 ...
2 1 0 0 0 0 0 0 0 0 ...
3 2 1 0 0 0 0 0 0 0 ...
4 3 2 1 0 0 0 0 0 0 ...
5 4 3 2 1 0 0 0 0 0 ...
6 5 4 3 2 1 0 0 0 0 ...
...
我会为此提供一个numpy 解决方案,不是最好看的,但你可以重构它:
n = 10
y = np.empty(n+1)
y[0] = 4
R = np.repeat([1, 7/8], [1, n])
Y = np.cumprod(R[1:])
V = np.tril(np.vander(R, increasing=True))[:-1,:-1]
X = np.lib.stride_tricks.sliding_window_view(np.r_[np.zeros(n-1), np.arange(1, n+1)], n)[:, ::-1]
T1, T2 = (Y*y[0], np.sum(V*X/8, axis=1))
y[1:] = T1 + T2
>>> y
[4. 3.625 3.421875 3.36914062 3.44799805 3.64199829
3.9367485 4.31965494 4.77969807 5.30723581 5.89383134]
一般来说它是O(n²),而初始方法是O(n)。如果您知道np.sum(V*X, axis=1) 的术语公式,则可以达到O(n) 并优于第一种方式。