哪个是遗传算法和动态规划之间解决经典0-1背包的最佳方法?

时间:2013-02-13 07:58:06

标签: algorithm dynamic genetic knapsack-problem

假设我有这样的问题:

  • 背包容量= 2000万
  • 项目数量= 500
  • 每个项目的重量是100到2000万之间的随机数
  • 每个项目的利润是1到10之间的随机数

那么哪个方法最适合我的问题? GA或动态编程?

请给我一个简单的解释,因为我是新手..

4 个答案:

答案 0 :(得分:4)

动态编程(DP):

  • 精确算法 - 查找全局最优解决方案
  • 运行时间长
  • 使用大量内存
  • 实施起来非常简单

遗传算法(GA):

  • 估算 - 不一定能找到全局最优解决方案
  • 运行时间短
  • 内存使用量取决于个人数量,但通常是可管理的
  • 解决方案的质量取决于选择有效的表示方式+让它运行得足够长
  • 实施起来相当简单,设计决策可能会稍微复杂一些,特别是如果您没有使用GA的重要经验

登山:

  • 估算 - 不一定能找到全局最优解。虽然有办法减少发生这种情况的可能性,但local optimum比GA更容易停止
  • 运行时间短
  • 内存使用率非常低
  • 实施起来非常简单

DP(或NP完全问题的任何精确算法)通常只是一个相当小的问题的好主意,或者如果找到全局最优是最重要的事情。

DP有两种方法:(有一个相当简单的优化,你只存储2行,我的内存使用分析假设使用了这种优化)

  • 具有项目x权重矩阵,单元格值为最大值

      

    矩阵大小= 500 x 20 000 000

         

    运行时间= O(500 * 20 000 000)= O(10 000 000 000)

         

    记忆=最大10 * 500 - > 5 000 - >短= 2个字节 - > 2 * 20 000 000 * 2 = 80 000 000< 80 MB

         

    说明:下面的[i,j]表示从权重小于或等于 j的元素1到i的任何子集可获得的最佳(最高)值。下面的更新规则意味着 - 找到不包括当前元素(因此权重和值保持不变)或包含它的最佳值(因此查找(当前权重减去当前项目的权重)的最佳值并添加当前项目的价值)。然后返回A [500,20000000],它表示可从背包大小的最大权重的所有元素的任何子集获得的最高值。

         

    算法:

    A[0, 0..20000000] = 0
    for i = 1:500
    for x = 0:20000000
      A[i, x] = max(A[i-1, x], value(i) + A[i-1, x-weight(i)])
    // ignore value(i) + A[i-1, x-weight(i)] when weight(i) > x
    return A[500, 20000000]
    
  • 具有项目x值的矩阵,单元格值为最小权重

      

    矩阵大小= 500 x 10 * 500

         

    运行时间= O(500 * 10 * 500)= O(2 500 000)

         

    记忆=最多20 000 000 - &gt; int = 4字节 - &gt; 2 * 500 * 4 = 4 000 < 4 KB

         

    说明:下面的[i,j]表示从元素1到i的任何子集可获得的最低权重,其值等于 j。下面的更新规则意味着 - 找到不包括当前元素之间的最佳值(因此权重和值保持不变)或包括它(因此查找(当前值减去当前项的值)的最佳值并添加当前项目的重量)。任何单元格的值都是产生该值的子集的精确权重,因此我们需要查看所有单元格A [500,x],它代表任何值的元素的最小权重子集X

         

    算法:

    A[0, 0] = 0
    A[0, 1..5000] = ∞
    for i = 1:500
    for x = 0:5000
      A[i, x] = min(A[i-1, x], weight(i) + A[i-1, x-value(i)])
    // interpret A[i-1, x-value(i)] as 0 when value(i) > x
    return largest x that A[500, x] <= 20000000
    

所以,是的,复杂性几乎说明了自己,你会在第一种方式等待几个小时,但是第二种情况只需要几秒钟,而且内存使用量也存在类似的差异(尽管80 MB仍然几乎可以忽略不计)(请注意,这是规则中的 FAR ,每个案例都需要自行分析。)

答案 1 :(得分:3)

动态编程可以在O(numItems * knapsackCapacity)O(knapsackCapacity)内存中运行。这意味着,根据您的规格,您将拥有:

  • 关于20.000.000 x 500 = 10.000.000.000操作 - 可能会在几个小时内完成执行,具体取决于您的机器;
  • 由于一件商品的利润最多为10件,而您最多可以有500件商品,这意味着您的最大理论利润不能超过10 x 500 = 5000。这可以用2个字节(短)表示。因此,您还需要2 x 20.000.000 / 1024 / 1024 ~= 38 MB内存来存储DP阵列。再一次,没那么多。

其他人已经提到了每种方法的优缺点。 GA将更快完成,但DP解决方案也应在几个小时内完成,它将为您提供准确的答案。

就个人而言,如果等待几个小时,我会选择DP,因为解决方案不是问题。

注意:这是与O(knapsackCapacity)内存一起运行的DP:

dp[i] = maximum profit obtainable for weight i
for i = 1 to numItems do
  for j = knapsackCapacity down to items[i].weight do
    dp[j] = max(dp[j], dp[j - items[i].weight] + items[i].profit)

答案 2 :(得分:0)

没有“最佳”方法,每种方法都有其优点和缺点 在这种情况下,权衡取决于找到的解决方案的最优性 - GA不以任何方式保证找到最佳解决方案 计算解决方案所需的时间/空间 - 使用动态编程将为您节省一些冗余计算,但您仍需要至少计算一次所有可能的组合以找到最佳解决方案(或者甚至可能是任何解决方案)。

答案 3 :(得分:0)

首先,您需要将动态编程视为一种精确的算法,可以保证答案将成为最佳答案。另一方面,GA是一种启发式算法,通常会收敛到局部最优。 对于背包有动态编程解决方案,如果它们是整数,则是伪线性的(O(capacity * number_of_items))。在您的情况下,您需要大约1e10的操作和内存。这对单台计算机来说是可行的。因此,如果找到最佳答案对您来说很重要并且您有足够的时间(大约几个小时),您可以使用动态编程方法。否则,您可能更喜欢使用启发式算法,例如GA。