替换NumPy中的迭代

时间:2015-11-05 17:34:26

标签: python arrays numpy recursion vectorization

我正在尝试重写以下例程(a,b,c,d和e都是数组):

def generate_a(b, c, e):
    a = np.zeros_like(b)
    d = np.zeros_like(b)
    for i in range(a.size):
        a[i] = (b[i] / c[i]) + d[i-1]
        d[i] = a[i] * e[i]
    return a

不使用'for'循环,因为例程需要运行数百万次。这是一个小技巧,因为d中任何给定单元格的值取决于为a中的单元格计算的结果,而该结果又取决于为d计算的最后一个值。关于如何在没有迭代的情况下完成这项工作的任何想法?

4 个答案:

答案 0 :(得分:5)

受此solution的启发,如果递归可以跟踪,则此帖子显示递归为vectorizable 。正如在链接解决方案中所述,同样不应该是现在的快速,但是要利用并行处理器,这种方法可以给出一个机会。因此,请将此帖作为如何跟踪和矢量化递归的指导。这是实施 -

def generate_a_vectorized(b, c, e):

    K = (b/c)*e
    N = e.size

    mask = np.linspace(N,1,N,dtype=int)[:,None] <= np.arange(N)

    Pn = np.tile(e[None],(N,1))
    Pn[mask] = 1
    En = Pn[:,::-1].cumprod(1)[:,::-1]
    En[mask] = 0
    An = np.append(0,K[:-1])

    d_out = En.dot(An)[::-1] + K
    return (b/c) + np.append(0,d_out[:-1])

样品运行&amp;验证输出 -

In [279]: M = 50
     ...: b = np.random.rand(M)
     ...: c = np.random.rand(M)
     ...: e = np.random.rand(M)
     ...: 

In [280]: np.allclose(generate_a(b, c, e),generate_a_vectorized(b, c, e))
Out[280]: True

In [281]: %timeit generate_a(b,c,e)
10000 loops, best of 3: 79.4 µs per loop

In [282]: %timeit generate_a_vectorized(b,c,e)
10000 loops, best of 3: 157 µs per loop

答案 1 :(得分:3)

由于a[i]取决于d[i-1]d[i-1]取决于a[i-1]a[i]也取决于a[0], a[1], ... a[i-1]而你赢了能够并行计算并行化。您需要接受该循环或找出一种封闭的形式来进行计算。

答案 2 :(得分:1)

您可以使用map和lambda。

尝试:

import numpy as np

def func(i,l):
    l[0][i] = (l[1][i] / l[2][i]) + l[3][i-1]
    l[3][i] = l[0][i] * l[4][i]

    return l[0][i]

def generate_a(b, c, e):
    a = np.zeros_like(b)
    d = np.zeros_like(b)

    a = map(lambda i:func(i,[a, b, c, d, e]), xrange(a.size))

    return a

print generate_a([5, 6, 7],[1,2,3],[8,9,12])

答案 3 :(得分:1)

使用Mathematica快速了解(因为b[i]/c[i]与计算无关,我在下面称之为bc[i]):

Wolfram Language (Raspberry Pi Pilot Release)
Copyright 1988-2015 Wolfram Research
Information & help: wolfram.com/raspi

In[1]:= RSolve[ { a[i]==bc[i] + a[i-1]*e[i-1], a[-1]==0 }, a[i], i]                    

Out[1]= {{a[i] -> 

>      Product[e[K[1]], {K[1], 1, -1 + i}] 

                      bc[1 + K[2]]
>       Sum[---------------------------------, {K[2], -1, -1 + i}]}}
            Product[e[K[1]], {K[1], 1, K[2]}]

正如Randy C先前所说的那样,除非先前的计算会导致更简单的事情,否则我强烈怀疑你能比现有的更好。 您可以获得更好的代码,但可能没有更高效的代码。