【问题标题】:Knapsack - save time and memory背包 - 节省时间和记忆
【发布时间】:2014-07-09 17:53:28
【问题描述】:

根据我经历过的维基百科和其他来源,您需要矩阵m[n][W]n - 物品数量和W - 背包的总容量。这个矩阵变得非常大,有时太大而无法在 C 程序中处理。我知道动态编程是基于节省内存的时间,但是,有什么解决方案可以节省时间和内存吗?

Knapsack problem: 的伪代码

// Input:
// Values (stored in array v)
// Weights (stored in array w)
// Number of distinct items (n)
// Knapsack capacity (W)
for j from 0 to W do
  m[0, j] := 0
end for 
for i from 1 to n do
  for j from 0 to W do
    if w[i] <= j then
      m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i])
    else
      m[i, j] := m[i-1, j]
    end if
  end for
end for

假设 W = 123456789 和 n = 100。在这种情况下,我们得到非常大的矩阵 m[100][123456789]。我在考虑如何实现这一点,但我脑海中最好的只是保存用一位(0/1)选择的项目。这可能吗?或者有没有其他方法可以解决这个问题?

int32 -> 32 * 123456789 * 100 bits
one_bit -> 1 * 123456789 * 100 bits

我希望这不是愚蠢的问题,并感谢您的努力。

编辑 - 工作 C 代码:

    long int i, j;
    long int *m[2];
    m[0] = (long int *) malloc(sizeof(long int)*(W+1));
    m[1] = (long int *) malloc(sizeof(long int)*(W+1));
    for(i = 0; i <= W; i++){
        m[0][i] = 0;
    }

    int read = 0;
    int write = 1;
    int tmp;

    long int percent = (W+1)*(n)/100;
    long int counter = 0;

    for(i = 1; i <= n; i++){
        for(j = 0; j <= W; j++){
            if(w[i-1] <= j){
                m[write][j] = max(m[read][j],(v[i-1]) + m[read][j-(w[i-1])]);
            }else{
                m[write][j] = m[read][j];
            }
            counter++;
            if(counter == percent){
                printf(".");    //printing dot (.) for each percent
                fflush(stdout);
                counter = 0;
            }
        }
        tmp = read;
        read = write;
        write = tmp;
    }

    printf("\n%ld\n", m[read][W]);

    free(m[0]);
    free(m[1]);

【问题讨论】:

    标签: c algorithm dynamic-programming pseudocode knapsack-problem


    【解决方案1】:

    背包问题可以使用O(W)空格解决。
    在迭代的每个步骤中,您只需要 2 行 - 数组 m[i]m[i + 1] 的当前状态。

    current = 1
    int m[2][W]
    set NONE for all elements of m # that means we are not able to process this state
    m[0][0] = 0 # this is our start point, initially empty knapsack
    
    FOR i in [1..n] do
        next = 3 - current; /// just use 1 or 2 based on the current index
        for j in [0...W] do
           m[next][j] = m[current][j]
        FOR j in [w[i]..W] do
           if m[current][j - w[i]] is not NONE then  # process only reachable positions
               m[next][j] = max(m[next][j], m[current][j - w[i]] + v[i]);
        current = next; /// swap current state and the produced one
    


    也可以只使用 1 个数组。这是伪代码

    FOR i in [1..n] do
        FOR j in [w[i]..W] do
           m[j] = max(m[j], m[j - w[i]] + v[i]);
    

    【讨论】:

    • @TheDubleM 你到底是什么意思?
    • 我在c程序中使用了这个伪代码但是得到了段错误,这是整个算法吗?维基百科的基本算法工作正常,但如果 40 * 123456789 程序需要大约 8 秒才能找到解决方案。
    • @TheDubleM 好的,C 中的索引从 0 开始编号,所以当前应该是 0,下一个 = 1 - 当前。代码也需要一些初始化——我现在就更新我的 sn-p。
    【解决方案2】:

    您可以通过以下观察将空间使用从 m[100][123456789] 减少到 m[2][123456789]:

    看这部分代码,任何时候只需要引用矩阵i和i-1的两行

    if w[i] <= j then
      m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i])
    else
      m[i, j] := m[i-1, j]
    end if
    

    你可以使用这个技巧:

    int current = 1;
    
    
    //.........
    if w[i] <= j then
      m[current, j] := max(m[1 - current, j], m[1 - current, j-w[i]] + v[i])
    else
      m[i, j] := m[1 - current, j]
    end if
    current = 1 - current;
    

    【讨论】:

    • 填充第二行后是否需要将这两行向上移动?
    猜你喜欢
    • 1970-01-01
    • 2020-08-10
    • 1970-01-01
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    相关资源
    最近更新 更多