numpy复发

时间:2017-11-22 05:45:11

标签: python numpy

有没有办法在不使用numpy的情况下重复出现?

使用np.addout关键字一起使用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.]

有人可以告诉我为什么吗?或任何其他方式进行递归?

4 个答案:

答案 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更快。