Knapsack prob, but allow over-filling

别来无恙 提交于 2019-12-08 01:57:14

问题


Lets say I have 5 items (name, size, value) as follows:

("ITEM01", 100, 10000)
("ITEM02", 24, 576)
("ITEM03", 24, 576)
("ITEM04", 51, 2500)
("ITEM05", 155, 25)

and I have to get the closest match to a total size of 150 (each item can only be added once).

This is very similar to the knapsack problem, but not quite since in this case my preferable solution would be ITEM01, ITEM04 giving a total size of 151 (the knapsack problem would stop me going over size = 150 and hence give ITEM01, ITEM02 and ITEM03 with a total size of 148).

Does this problem have a name? (Is it still combinatorial optimisation)? I'm looking for a python solution, but it would help if I knew the name of what I am looking for.


回答1:


You can try to do it using dynamic programming.

Let dp[k] be equal to a list of items, with the sum of size equal to k. Initially d[0] = [] and dp[k] = None for k > 0. The size of the list may be bounded by the sum of sizes of all elements, let's call it S.

What the algorithm does is for each item it goes from i = S down to i = 0 and it checks if dp[i] != None, which means we know we are able to select items with sum of sizes equal to i. These items are on the list dp[i]. Let's observe that we can add the current item to that list and have a set of items with sum equal to i + item.size. So we assign dp[i + item.size] = dp[i] + [item]. Having processed all items we just have to start at the desired sum of sizes and go both directions to find the closest match.

Code:

items = [("ITEM01", 100, 10000), ("ITEM02", 24, 576), \
    ("ITEM03", 24, 576), ("ITEM04", 51, 2500), ("ITEM05", 155, 25)]
S = sum([item[1] for item in items])
dp = [None for i in xrange(S + 1)]
dp[0] = []

for item in items:
    for i in xrange(S, -1, -1):
        if dp[i] is not None and i + item[1] <= S:
            dp[i + item[1]] = dp[i] + [item]

desired_sum = 150
i = j = desired_sum

while i >= 0 and j <= S:
    if dp[i] is not None:
        print dp[i]
        break
    elif dp[j] is not None:
        print dp[j]
        break
    else:
        i -= 1
        j += 1

output:

[('ITEM01', 100, 10000), ('ITEM04', 51, 2500)]

However the complexity of this solution is O(n*S) where n is the number of items and S is the sum of sizes, so it may be too slow for some purposes. What can be improved in this solution is the S constant. For example you can set S to 2 * desired_sum because we have guarantee that we can take a set of items with sum of sizes in [0, 2 * desired_sum] (possibly an empty set with sum 0). If you want to take at least one item you can take S = max(min_item_size, 2 * desired_sum - min_item_size) where min_item_size is the minimum of sizes of all items.

EDIT:

Oh, you also wanted get maximise value when two combinations are equally close to desired_size. Then you have to alternate the code a bit to keep the best combinations for each sum of sizes.

Namely:

if dp[i] is not None and i + item[1] <= S:

should be:

if dp[i] is not None and i + item[1] <= S and \
    (
        dp[i + item[1]] is None
        or
        sum(set_item[2] for set_item in dp[i]) + item[2]
            > sum(set_item[2] for set_item in dp[i + item[1]])
    ):

(a bit ugly, but I don't know how to break lines to make it look better)

Of course you can keep these sums to avoid calculating them each time.




回答2:


Assuming you have a working knapsack-solver and a lot of time:

Set value of each item to the weight of each item and solve the knapsack problem with capacity 150. This will give you the maximum size smaller than the target (148 in your example). So the maximum size to consider is 150 + (150 - 148) = 152

Now solve it again for each integer between 150 and 152. If you find a smaller difference (151 in your example) stop, use that and solve for value using the original item values. If the range is big you could also try binary search.

Otherwise solve the original knapsack problem with capacity 148 and 152 and pick the solution with the greatest value.



来源:https://stackoverflow.com/questions/15501558/knapsack-prob-but-allow-over-filling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!