我有一个2D成本矩阵M,也许是400x400,我正在尝试计算通过它的最佳路径。因此,我有一个类似的功能:

M[i,j] = M[i,j] + min(M[i-1,j-1],M[i-1,j]+P1,M[i,j-1]+P1)


这显然是递归的。 P1是一些加法常数。我的代码或多或少地起作用:

def optimalcost(cost, P1=10):
    width1,width2 = cost.shape
    M = array(cost)
    for i in range(0,width1):
       for j in range(0,width2):
          try:
              M[i,j] = M[i,j] + min(M[i-1,j-1],M[i-1,j]+P1,M[i,j-1]+P1)
          except:
              M[i,j] = inf
    return M


现在我知道在Numpy中循环是一个可怕的主意,对于诸如初始成本矩阵的计算之类的事情,我已经找到了缩短时间的捷径。但是,由于我可能需要评估整个矩阵,因此我不确定该如何做。在我的机器上,每次通话大约需要3秒钟的时间,并且必须将其应用于这些费用矩阵中的300左右。我不确定这段时间是从哪里来的,因为分析表明200,000次调用min仅花费0.1s-也许是内存访问?

有没有办法以某种方式并行执行此操作?我认为可能存在,但是在我看来,除非有一种更聪明的方式来记忆事物,否则每次迭代都是依赖的。

这个问题有很多相似之处:Can I avoid Python loop overhead on dynamic programming with numpy?

如果有必要,我很高兴切换到C语言,但是我喜欢Python的灵活性,可以进行快速测试,并且缺少文件IO的支持。我的头顶上,下面的代码可能会更快吗?

#define P1 10
void optimalcost(double** costin, double** costout){
    /*
        We assume that costout is initially
        filled with costin's values.
    */
    float a,b,c,prevcost;

    for(i=0;i<400;i++){
        for(j=0;j<400;j++){
            a = prevcost+P1;
            b = costout[i][j-1]+P1;
            c = costout[i-1][j-1];
            costout[i][j] += min(prevcost,min(b,c));
            prevcost = costout[i][j];
        }
    }
}

return;


更新:

我在Mac上,并且不想安装全新的Python工具链,因此我使用了Homebrew

> brew install llvm --rtti
> LLVM_CONFIG_PATH=/usr/local/opt/llvm/bin/llvm-config pip install llvmpy
> pip install numba


新的“数字”代码:

from numba import autojit, jit
import time
import numpy as np

@autojit
def cost(left, right):
    height,width = left.shape
    cost = np.zeros((height,width,width))

    for row in range(height):
        for x in range(width):
            for y in range(width):
                cost[row,x,y] = abs(left[row,x]-right[row,y])

    return cost

@autojit
def optimalcosts(initcost):
    costs = zeros_like(initcost)
    for row in range(height):
        costs[row,:,:] = optimalcost(initcost[row])
    return costs

@autojit
def optimalcost(cost):
    width1,width2 = cost.shape
    P1=10
    prevcost = 0.0
    M = np.array(cost)
    for i in range(1,width1):
        for j in range(1,width2):
            M[i,j] += min(M[i-1,j-1],prevcost+P1,M[i,j-1]+P1)
            prevcost = M[i,j]
    return M

prob_size = 400
left = np.random.rand(prob_size,prob_size)
right = np.random.rand(prob_size,prob_size)

print '---------- Numba Time ----------'
t =  time.time()
c = cost(left,right)
optimalcost(c[100])
print time.time()-t

print '---------- Native python Time --'
t =  time.time()
c = cost.py_func(left,right)
optimalcost.py_func(c[100])
print time.time()-t


用Python编写非常有趣的代码是非Pythonic的。请注意,对于有兴趣编写Numba代码的任何人,您都需要在代码中明确表示循环。以前,我有整齐的Numpy单缸纸

abs(left[row,:][:,newaxis] - right[row,:])


计算成本。用Numba花了大约7秒钟。正确写出循环可得到0.5s。

将其与本机Python代码进行比较是不公平的比较,因为Numpy可以很快完成,但是:

Numba编译:0.509318113327s

本地的:172.70626092s

数字和转换的简单程度都给我留下了深刻的印象。

最佳答案

如果您不难切换到Python的Anaconda发行版,则可以尝试使用Numba,对于这种特殊的简单动态算法,它可能会提供很多加速而不会离开Python。

07-26 00:30
查看更多