Python 为什么是麻木堂';我不能提高背包功能的速度吗?

Python 为什么是麻木堂';我不能提高背包功能的速度吗?,python,python-3.x,performance,jit,numba,Python,Python 3.x,Performance,Jit,Numba,我试着用numba来加速我的代码,但它似乎不起作用。该程序与@jit、@njit或纯python的时间相同(约10秒)。然而,我使用numpy而不是list或dict 这是我的代码: import numpy as np from numba import njit import random import line_profiler import atexit profile = line_profiler.LineProfiler() atexit.register(profile.prin

我试着用numba来加速我的代码,但它似乎不起作用。该程序与
@jit
@njit
或纯python的时间相同(约10秒)。然而,我使用numpy而不是list或dict

这是我的代码:

import numpy as np
from numba import njit
import random
import line_profiler
import atexit
profile = line_profiler.LineProfiler()
atexit.register(profile.print_stats)

@njit
def knapSack(W, wt, val, n):
    K = np.full((n+1,W+1),0)
    N =  np.full((n+1,W+1,W+1),0)
    M =  np.full((n+1,W+1),0)

    for i in range(n+1):
        for w in range(W+1):
            if i==0 or w==0:
                K[i][w] = 0
            elif wt[i-1] <= w:
                if(val[i-1] + K[i-1][w-wt[i-1]] >  K[i-1][w]):
                    K[i][w] = val[i-1] + K[i-1][w-wt[i-1]]
                    c = N[i-1][w-wt[i-1]]
                    c[i] = i
                    N[i][w] = c
                else:
                    K[i][w] = K[i-1][w]
                    N[i][w] = N[i-1][w]
            else:
                K[i][w] = K[i-1][w]
    N[n][W][0] = K[n][W]
    return N[n][W]

@profile
def main():

    size = 1000
    val = [random.randint(1, size) for i in range(0, size)]
    wt = [random.randint(1, size) for i in range(0, size)]
    W = 1000
    n = len(val)
    a = knapSack(W, wt, val, n)
main()

将numpy导入为np
来自numba import njit
随机输入
导入测线分析器
进口退欧
profile=line_profiler.LineProfiler()
atexit.register(profile.print_stats)
@njit
def背包(W、wt、val、n):
K=np.满((n+1,W+1),0)
N=np.满((N+1,W+1,W+1),0)
M=np.满((n+1,W+1),0)
对于范围(n+1)内的i:
对于范围内的w(w+1):
如果i==0或w==0:
K[i][w]=0
elif wt[i-1]K[i-1][w]):
K[i][w]=val[i-1]+K[i-1][w-wt[i-1]]
c=N[i-1][w-wt[i-1]]
c[i]=i
N[i][w]=c
其他:
K[i][w]=K[i-1][w]
N[i][w]=N[i-1][w]
其他:
K[i][w]=K[i-1][w]
N[N][W][0]=K[N][W]
返回N[N][W]
@侧面图
def main():
尺寸=1000
val=[random.randint(1,大小)表示范围(0,大小)中的i]
wt=[random.randint(1,大小)表示范围(0,大小)中的i]
W=1000
n=len(val)
a=背包(W、wt、val、n)
main()

事实上,如果不改变方法本身,就不可能真正提高当前算法的性能

您的
N
数组包含大约10亿个对象(
1001*1001*1001
)。您需要设置每个元素,这样您至少有10亿个操作。为了得到一个下限,我们假设设置一个数组元素需要一纳秒(实际上需要更多的时间)。10亿次操作,每一次需要1纳秒意味着需要1秒才能完成。正如我所说的,每个操作可能需要比1纳秒多一点的时间,所以让我们假设它需要10纳秒(可能有点高,但比1纳秒更现实),这意味着算法总共需要10秒

因此,输入的预期运行时间将在1秒到10秒之间。因此,如果您的Python版本需要10秒,那么它可能已经达到了您所选择的方法所能达到的极限,并且没有任何工具(显著)能够改进运行时


有一件事可以让它快一点,那就是使用
np.zero
而不是
np.full

K = np.zeros((n+1, W+1), dtype=int)
N = np.zeros((n+1, W+1, W+1), dtype=int)
不要创建
M
,因为你不打算使用它


由于您已经使用了line profiler,我决定看一看,结果如下:

Line#每次命中的命中次数%时间行内容
==============================================================
3个def背包(W、wt、val、n):
4 1 19137.0 19137.0 0 0.0 K=np.满((n+1,W+1),0)
5119408592.019408592.028.1N=np.满((N+1,W+1,W+1),0)
6.
7 1002 6412.0 6.4 0.0适用于范围(n+1)内的i:
8 1003002 4186311.0 4.2 6.1适用于范围内的w(w+1):
9 1002001 4644031.0 4.6 6.7如果i==0或w==0:
10 2001 19663.0 9.8 0.0 K[i][w]=0
11 1000000 5474080.0 5.5 7.9 elif wt[i-1]K[i-1][w]):
13 52596 902030.0 17.2 1.3 K[i][w]=val[i-1]+K[i-1][w-wt[i-1]]
14 52596 578740.0 11.0 0 0.8 c=N[i-1][w-wt[i-1]]
1552596295980.056.40C[i]=i
16525961239792.023.611.8N[i][w]=c
17其他:
18 445769 5100917.0 11.4 7.4 K[i][w]=K[i-1][w]
19445769 11677683.0 26.2 16.9 N[i][w]=N[i-1][w]
20其他:
21 501635 5801328.0 11.6 8.4 K[i][w]=K[i-1][w]
22 1 16.0 16.0 0 0.0 N[N][W][0]=K[N][W]
23 1 14.0 14.0 0 0.0返回N[N][W]
这表明瓶颈是
np.full
N[i][w]=N[i-1][w]
,以及
if(val[i-1]+K[i-1][w-wt[i-1]]>K[i-1][w])
。Numba不会改进前两个,因为他们已经使用了高度优化的NumPy代码,更有可能的是,Numba在这些方面会更慢。如果(val[i-1]+K[i-1][w-wt[i-1]]>K[i-1][w]),Numba可能会改善
,但这可能不会引起注意

如果
np.full
np.zero
替换,则配置文件会发生轻微变化:

Line#每次命中的命中次数%时间行内容
==============================================================
3个def背包(W、wt、val、n):
4 1 747.0 747.0 0 0.0 K=np.零((n+1,W+1),dtype=int)
5 1 109592.0 109592.0 0.2 N=np.零((N+1,W+1,W+1),dtype=int)
6.
7 1002 4230.0 4.2 0.0适用于范围(n+1)内的i:
8 1003002 4414071.0 4.4 7.0适用于范围内的w(w+1):
9 1002001 4836807.0 4.8 7.7如果i==0或w==0:
10 2001 22282.0 11.1 0.0 K[i][w]=0
11 1000000 5646859.0 5.6 8.9 elif wt[i-1]K[i-1][w]:
1347579784563.016.511.2k[i][w]=val[i-1]+K[i-1][w-wt[i-1]]
14 47579 509056.0 10.7 0.8 c=N[i-1][w-wt[i-1]]
15     4757
import numpy as np

def knapSack(W, wt, val, n):
    K = np.full((n+1,W+1),0)
    N = np.full((n+1,W+1,W+1),0)

    for i in range(n+1):
        for w in range(W+1):
            if i==0 or w==0:
                K[i][w] = 0
            elif wt[i-1] <= w:
                if(val[i-1] + K[i-1][w-wt[i-1]] >  K[i-1][w]):
                    K[i][w] = val[i-1] + K[i-1][w-wt[i-1]]
                    c = N[i-1][w-wt[i-1]]
                    c[i] = i
                    N[i][w] = c
                else:
                    K[i][w] = K[i-1][w]
                    N[i][w] = N[i-1][w]
            else:
                K[i][w] = K[i-1][w]
    N[n][W][0] = K[n][W]
    return N[n][W]

import random
size = 1000
val = [random.randint(1, size) for i in range(0, size)]
wt = [random.randint(1, size) for i in range(0, size)]
W = 1000
n = len(val)

%lprun -f knapSack knapSack(W, wt, val, n)
 @njit
    def knapSack(W, wt, val, n):

        K = np.zeros((n + 1, W + 1),dtype=np.int32)
        # In fact we must only save the previous combinations and the current, 
        # not all :) So N is considerably reduce
        N = np.zeros((2, W + 1, W + 1),dtype=np.int32)

        for i in range(n + 1):
            for w in range(W + 1):
                if i == 0 or w == 0:
                    K[i][w] = 0
                elif wt[i - 1] <= w:
                    if val[i - 1] + K[i - 1][w - wt[i - 1]] > K[i - 1][w]:
                        K[i][w] = val[i - 1] + K[i - 1][w - wt[i - 1]]
                        N[i%2][w] = np.copy(N[(i - 1)%2][w - wt[i - 1]])
                        N[i%2][w][i] = i
                    else:
                        K[i][w] = K[i - 1][w]
                        N[i%2][w] = N[(i - 1)%2][w]
                else:
                    K[i][w] = K[i - 1][w]
        N[(n)%2][W][0] = K[n][W]
        return N[(n)%2][W]