背包的运行时间分析

时间:2013-09-29 04:43:41

标签: algorithm complexity-theory

我想在theta表示法中找到以下背包自顶向下算法的运行时间

KNAPSACK-TOP-DOWN(T, U )
     if U < 0 then return −∞
     else if T = ∅ or U = 0 then return 0
     else
         Let t = v, w be the last entry in T
         m1 ← KNAPSACK-TOP-DOWN(T − {t}, U )
         m2 ← v + KNAPSACK-TOP-DOWN(T − {t}, U − w)
         return max{m1 , m2 }

我认为这个问题的上限是O(2 ^ n)。如何找到渐近紧束缚?

1 个答案:

答案 0 :(得分:4)

你的伪代码代表了背包问题的天真解决方案。看它的结构很像天真的Fibonacci实现,O(2^n)。事实上,背包的天真实施是指数级的。请注意,Unbounded knapsack problem可以通过动态编程在O(uT)中解决。

至于为什么这个实现是指数级的,首先要记住,随着TU的增加,递归调用的数量也会增加。然后注意算法的每次迭代将递归地调用KNAPSACK-TOP-DOWN两次,在最坏的情况下,将两次调用KNAPSACK-TOP-DOWN,依此类推。

                              (T, U)                                        1
                            /        \
                           /          \
                          /            \
                         /              \
                        /                \
              (T-{t},U)                    (T-{t},U-w)                      2
            /           \                /             \
           /             \              /               \
(T-{t}-{t'},U) (T-{t}-{t'},U-w)  (T-{t}-{t'},U-w) (T-{t}-{t'},U-w-w')       4
                                                                            8
                                                                            16
                                                                            ...

正如您所看到的,递归调用的数量会根据背包的项目数量和容量呈指数级增长。另请注意,T中的新元素将在最坏的情况下引入一个全新的“级别”,其计算量是前一级别的两倍......当然,只有在有足够的容量时才会发生这种情况。留在背包里,所以T和U都会影响计算次数。看看上面的树,显然天真的实现确实是O(2^n)

但是,如果你仔细观察“堆栈跟踪”,你会注意到(T-{t}-{t'},U-w)(因此,它的所有子问题)被调用了两次。这就是动态编程的用武之地。我们可以将计算结果存储在某种数据结构中,并进一步查找它们,以便不需要计算相同的子问题两次。当使用这种方法时,事情会急剧加快,整个问题可以按照与背包的容量*要考虑的项目数量成比例的时间计算,或者用符号表示,O(uT)

This article提供了幼稚和O(uT)版本的C实现。如果您的问题与家庭作业有关,那么您应该明确地看一下它。最后,搜索“背包问题”将教会你很多关于NP完全,NP-Hard,伪多项式时间等等。