有没有办法在不使用numpy的情况下重复出现?
使用np.add
与out
关键字一起使用dtype="int"
import numpy as np
N = 100
fib = np.zeros(N, dtype=np.int)
fib[:2] = 1.
np.add(fib[:-2], fib[1:-1], out=fib[2:])
print(fib[:10])
输出:[ 1 1 2 3 5 8 13 21 34 55]
但是,如果dtype
更改为np.float
import numpy as np
N = 100
fib = np.zeros(N, dtype=np.float)
fib[:2] = 1.
np.add(fib[:-2], fib[1:-1], out=fib[2:])
print(fib[:10])
输出:[ 1. 1. 2. 1. 3. 1. 4. 1. 5. 1.]
有人可以告诉我为什么吗?或任何其他方式进行递归?
答案 0 :(得分:0)
一个可能不是超级高效但有趣的解决方案就是滥用scipy.linalg.solve_banded
这样
import numpy as np
from scipy import linalg
N = 50
a = np.zeros((N,)) + np.array([[1, -1, -1]]).T
b = np.zeros((N,))
b[0] = 1
linalg.solve_banded((2, 0), a, b)
# array([ 1.00000000e+00, 1.00000000e+00, 2.00000000e+00,
# 3.00000000e+00, 5.00000000e+00, 8.00000000e+00,
# 1.30000000e+01, 2.10000000e+01, 3.40000000e+01,
# 5.50000000e+01, 8.90000000e+01, 1.44000000e+02,
# 2.33000000e+02, 3.77000000e+02, 6.10000000e+02,
# 9.87000000e+02, 1.59700000e+03, 2.58400000e+03,
# 4.18100000e+03, 6.76500000e+03, 1.09460000e+04,
# 1.77110000e+04, 2.86570000e+04, 4.63680000e+04,
# 7.50250000e+04, 1.21393000e+05, 1.96418000e+05,
# 3.17811000e+05, 5.14229000e+05, 8.32040000e+05,
# 1.34626900e+06, 2.17830900e+06, 3.52457800e+06,
# 5.70288700e+06, 9.22746500e+06, 1.49303520e+07,
# 2.41578170e+07, 3.90881690e+07, 6.32459860e+07,
# 1.02334155e+08, 1.65580141e+08, 2.67914296e+08,
# 4.33494437e+08, 7.01408733e+08, 1.13490317e+09,
# 1.83631190e+09, 2.97121507e+09, 4.80752698e+09,
# 7.77874205e+09, 1.25862690e+10])
这是如何工作的,我们写F_0,F_1,F_2 ......作为一个向量F和身份-F_ {i-1} -F_i + F {i + 1} = 0作为一个很好的带状矩阵然后解决F.请注意,这可以适应其他类似的重现。
答案 1 :(得分:0)
这是一个使用scipy过滤器的合理有效的方法:
import numpy as np
from scipy import signal
def scipy_fib(n):
x = np.zeros(n, dtype=np.uint8)
x[0] = 1
sos = signal.tf2sos([1], [1, -1, -1])
return signal.sosfilt(sos, x)
(注意我们不能使用lfilter
因为在信号处理方面这是一个不稳定的过滤器,它使Python解释器崩溃,所以我们必须转换为二阶段形式。)
过滤方法的问题在于我不确定它是否可以推广到解决ODE的实际问题。
另一种解决方案是简单地在Python中编写循环并使用JIT compiler加速它:
import numba as nb
@nb.jit(nopython=True)
def numba_fib(n):
y = np.empty(n)
y[:2] = 1
for i in range(2, n):
y[i] = y[i-1] + y[i-2]
return y
JIT方法的优点是你可以实现各种花哨的东西,但如果你坚持使用简单的循环和分支而不调用任何(非JITted)Python函数,它的效果最好。
计时结果:
# Baseline without JIT:
%timeit numba_fib(10000) # 100 loops, best of 3: 5.47 ms per loop
%timeit scipy_fib(10000) # 1000 loops, best of 3: 719 µs per loop
%timeit numba_fib(10000) # 10000 loops, best of 3: 33.8 µs per loop
# For fun, this is how Paul Panzer's solve_banded approach compares:
%timeit banded_fib(10000) # 1000 loops, best of 3: 1.33 ms per loop
scipy过滤器方法比纯Python循环快,但比JITted循环慢。我想过滤器函数是相对通用的,我们在这里不需要它们,而JITted循环编译成一个非常小的循环。
答案 2 :(得分:0)
由于线性递归具有解析解(Fibonacci的here),因此另一种快速scipy方法是:
def fib_scipy2(N):
r5=math.sqrt(5)
phi=(1+r5)/2
a= (-phi*ones(N)).cumprod()
return (np.abs(a)-1/a)/r5
运行:
In [413]: fib_scipy2(8)
Out[413]: array([ 1., 1., 2., 3., 5., 8., 13., 21.])
In [414]: %timeit fib(10**4)
103 µs ± 888 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
您可以将其调整为任何线性方程式。
答案 3 :(得分:0)
您的add
适用于此计算,但必须重复应用,以便传播非零值。我不知道你的计算是如何生成的[ 1. 1. 2. 1. 3. 1. 4. 1. 5. 1.]
。
In [619]: fib=np.zeros(10,int);fib[:2]=1
In [620]: for _ in range(10):
...: np.add(fib[:-2], fib[1:-1], out=fib[2:])
...: print(fib)
...:
[1 1 2 1 0 0 0 0 0 0] # **
[1 1 2 3 3 1 0 0 0 0]
[1 1 2 3 5 6 4 1 0 0]
[ 1 1 2 3 5 8 11 10 5 1]
[ 1 1 2 3 5 8 13 19 21 15]
...
(编辑 -
请注意,第一个np.add
就像完全缓冲一样。将**的结果与object和float数组进行比较。我在Py3上使用的是1.13.1版本。)
或者突出每个阶段的好价值
In [623]: fib=np.zeros(20,int);fib[:2]=1
In [624]: for i in range(10):
...: np.add(fib[:-2], fib[1:-1], out=fib[2:])
...: print(fib[:(i+2)])
...:
[1 1]
[1 1 2]
[1 1 2 3]
[1 1 2 3 5]
[1 1 2 3 5 8]
[ 1 1 2 3 5 8 13]
[ 1 1 2 3 5 8 13 21]
[ 1 1 2 3 5 8 13 21 34]
[ 1 1 2 3 5 8 13 21 34 55]
[ 1 1 2 3 5 8 13 21 34 55 89]
fib[2:] = fib[:-2]+fib[1:-1]
做同样的事情。
正如ufunc.at
的文档中所讨论的,np.add
等操作使用缓冲,即使使用out
参数也是如此。因此,虽然他们确实在C
级代码中进行了交互,但他们并没有积累;也就是说,ith
步骤中未使用i+1
步骤的结果。
add.at
可用于执行无缓冲的a[i] += b
。当索引包含重复时,这很方便。但它不允许从已更改的a
值到b
的反馈。所以这里没用。
这也是add.accumulate
(又名np.cumsum
),对于某些迭代定义可以很方便。但在一般情况下很难应用。
cumprod
(乘法累加)可以使用qmatrix
方法
Numpy's matrix_power function giving wrong results for large exponents
使用np.matrix
定义qmatrix
,以便*
乘法表示矩阵乘积(而不是元素):
In [647]: qmatrix = numpy.matrix([[1, 1], [1, 0]])
使用副本(实际指针)填充对象矩阵。
In [648]: M = np.empty(10, object)
In [649]: M[:] = [qmatrix for _ in range(10)]
应用cumprod
,并从每个矩阵中提取一个元素。
In [650]: m1=np.cumprod(M)
In [651]: m1
Out[651]:
array([matrix([[1, 1],
[1, 0]]), matrix([[2, 1],
[1, 1]]),
matrix([[3, 2],
[2, 1]]), matrix([[5, 3],
[3, 2]]),
matrix([[8, 5],
[5, 3]]),
matrix([[13, 8],
[ 8, 5]]),
matrix([[21, 13],
[13, 8]]),
matrix([[34, 21],
[21, 13]]),
matrix([[55, 34],
[34, 21]]),
matrix([[89, 55],
[55, 34]])], dtype=object)
In [652]: [x[0,1] for x in m1]
Out[652]: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
链接的答案使用numpy.linalg.matrix_power
,它也会重复矩阵产品。对于单一功率,matrix_power
更快,但对于整个功率范围,cumprod
更快。