简体   繁体   English

0-1 背包:在空间优化实施中寻找解决方案集

[英]0-1 Knapsack: Find Solution Set in Space-Optimised Implementation

I want to solve a 0-1 Knapsack problem with a maximum weight of ~ 200k and over 100k elements and eventual determination of the item set rather than only the optimal weight.我想解决一个 0-1 背包问题,最大重量为 ~ 200k 和超过 100k 元素,并最终确定项目集,而不仅仅是最佳重量。

Researching 0-1 Knapsack, I read that a common way to solve this problem is via dynamic programming and creating a table containing optimal solutions for subproblems, thus splitting up the original problem into smaller parts and later backtracing on the table to determine the item set.研究 0-1 Knapsack,我读到解决这个问题的常用方法是通过动态规划并创建一个包含子问题最优解的表格,从而将原始问题分成更小的部分,然后在表格上回溯以确定项目集. The maximum profit, without regard for the items taken, can be calculated in a memory efficient manner (as outlined here ).最大的利润,而不考虑所采取的项目,对于可以在存储效率的方式来计算(如概述这里)。

The obvious issue here is that for the dimensions I have in mind, this approach would consume more memory than is feasible (requiring O(n*W) space, with n being the number of elements and W being the maximum capacity).这里明显的问题是,对于我想到的维度,这种方法会消耗比可行更多的内存(需要O(n*W)空间, n是元素数, W是最大容量)。 Researching further I found mention ( here for example, also see "Knapsack Problems" by Kellerer, Pferschy and Pisinger) of a memory efficient way to solve 0-1 Knapsack.进一步研究发现我提到( 这里例如,参见“背包问题”的Kellerer,Pferschy和Pisinger)的内存使用效率的方式来解决0-1背包。

We start by splitting up the item set up into two subsets, roughly equal in size.我们首先将设置的项目分成大小大致相等的两个子集。 We treat both subsets as their own knapsack problem given the original maximum weight W and determine the last row of the maximum profit calculation for both subsets in the memory-efficient way (detailed above).给定原始最大权重W ,我们将两个子集视为它们自己的背包问题,并以内存高效的方式(上文详述)确定两个子集的最大利润计算的最后一行。

The next step is to find out where to optimally split the two subsets.下一步是找出最佳分割两个子集的位置。 To do this, we determine the maximum profit for weight w1 and w2 of the two rows.为此,我们确定两行重量w1w2的最大利润。 As I understand, it is critical to maintain w1 + w2 = W , so I iterate through the first row and take the index on the opposite end of the current index.据我了解,维护w1 + w2 = W至关重要,因此我遍历第一行并获取当前索引另一端的索引。 My current implementation for this step looks like this:我目前对这一步的实现如下所示:

def split(weights, values, n, w, i):
    # s1 is the bigger subset size if n is not even
    s1 = n // 2 + (n&1)
    s2 = n // 2

    row1 = maximum_profit(weights, values, s1, w)
    row2 = maximum_profit(weights[s1:], values[s1:], s2, w)

    max_profits_for_capacity = [x + y for x, y in zip(row1, row2[::-1])]
    max_profits = max(max_profits_for_capacity)                           
    optimal_weight_index = max_profits_for_capacity.index(max_value)

    c1 = row1[optimal_weight_index]
    c2 = row2[w-optimal_weight_index-1]

c1 and c2 are the maximum profits for each of the subsets then while maintaining c1 + c2 = W . c1c2是每个子集的最大利润,然后保持c1 + c2 = W With these values we recurse into each of the subsets:使用这些值,我们递归到每个子集:

split(weights[:s1], values[:s1], s1, c1, i)      
split(weights[s1:], values[s1:], s2, c2, i+s1)

This is where the descriptions lose me.这就是描述让我迷失的地方。 Eventually this code will recurse to n == 1 with a value of w .最终这段代码将递归到n == 1 ,值为w How do I determine if an element is included given an item index i and a maximum (local) capacity w ?在给定项目索引i和最大(本地)容量w如何确定是否包含元素?

I can provide a small example data set to illustrate the workings of my code in detail and where it goes wrong.我可以提供一个小的示例数据集来详细说明我的代码的工作原理以及哪里出错了。 Thank you very much.非常感谢。

First, I guess you have a mistake saying about c , w and their relation as capacity, but getting c1 and c2 from profit lists.首先,我猜你有一个错误的说法是关于cw以及它们作为容量的关系,而是从利润列表中获取c1c2

To the question, by the returning value of your split function you can define what type of question you are answering.对于问题,通过 split 函数的返回值,您可以定义要回答的问题类型。

As you take the split direct to n == 1 point and you want to get the indices of the picked items into knapsack, you can simply at this step return the value consisting of [0] or [1] as the output:当您将拆分直接到n == 1点并且您希望将拾取的物品的索引放入背包时,您可以简单地在此步骤返回由[0][1]组成的值作为输出:

if n == 1:
  if weights[0] < w:
    return [1]
  return [0]
  • [1] means picking the item into resulting set [1]表示将项目挑选到结果集中
  • [0] otherwise [0]否则

then concatenate them into one during other steps of recurse of your split function like:然后在split函数的其他递归步骤中将它们连接成一个,例如:

def split(..):
  ..
  # since it is lists concatenation
  return split(weights[:s1], values[:s1], s1, c1, i) + split(weights[s1:], values[s1:], s2, c2, i+s1)

In result you will get list of size n (for the number of items you make splits) with zeroes and ones.结果,您将获得大小为n (对于您进行拆分的项目数)的列表,其中包含零和一。

Total complexity would be:总复杂度为:

  • O(nWlogn) for time, since we make splits till the n == 1 step O(nWlogn)时间,因为我们进行拆分直到n == 1
  • O(W) for memory, since we store always only a part of the resulting list when recurse O(W)用于内存,因为我们在递归时总是只存储结果列表的一部分

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM