我有一个包装尺寸的清单。最多会有大约5种不同的大小,并且可能会出现几次(<50)。
packages = [5,5,5,5,5,5,10,11]
我需要将它们打包到固定数量的垃圾箱中,例如3。
number_of_bins = 3
垃圾箱的大小(包装好的包装大小的总和)可能在0到2之间变化(也就是说,垃圾箱中包装大小的总和之差必须等于或几乎相等) )。因此,将垃圾桶与[1,2]
(= 3)和[2]
(= 2)(差为1)很好,将垃圾桶与[10]
(= 10)和[5]
( = 5)(差异为5)不是。
有可能不会将所有包裹都分类到垃圾箱中,但是我想要一种解决方案,在该解决方案中,最少包裹数仍未打开。
所以这种情况下(我认为)的最佳解决方案是
bins = [11,5],[10,5],[5,5,5]
remaining = [5]
可能有背包或装箱算法可以做到这一点,但我还没有找到。我蛮力强行的,但我不确定这样做的有效方法。
是否有任何有效的方法可以轻松地做到这一点?我只是错过了相关的搜索词来找到它吗?
另一个例子:
packages = [5,10,12]
number_of_bins = 2
导致
bins = [12],[10]
remaining = [5]
因为
bins = [12],[10,5]
具有12和15的纸槽大小,相差超过2。
模拟:
packages = [2,10,12]
number_of_bins = 3
导致
bins = [2],[],[]
remaining = [12,10]
答案 0 :(得分:2)
这是使用pulp的解决方案:
from pulp import *
packages = [18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 65, 65, 65]
number_of_bins = 3
bins = range(1, number_of_bins + 1)
items = range(0, len(packages))
x = LpVariable.dicts('x',[(i,b) for i in items for b in bins],0,1,LpBinary)
y = LpVariable('y', 0, 2, LpInteger)
prob=LpProblem("bin_packing",LpMinimize)
#maximize items placed in bins
prob.setObjective(LpAffineExpression([(x[i,b], -3) for i in items for b in bins] + [(y, 1)]))
#every item is placed in at most 1 bin
for i in items:
prob+= lpSum([x[i,b] for b in bins]) <= 1
for b in bins:
if b != 1: # bin 1 is the one with lowest sum
prob+= LpAffineExpression([(x[i,b], packages[i]) for i in items] + [(x[i,1], -packages[i]) for i in items]) >= 0
if b != number_of_bins: # last bin is the one with highest
prob+= LpAffineExpression([(x[i,number_of_bins], packages[i]) for i in items] + [(x[i,b], -packages[i]) for i in items]) >= 0
#highest sum - lowest sum <= 2 so every difference of bin sums must be under 2
prob += LpAffineExpression([(x[i,number_of_bins], packages[i]) for i in items] + [(x[i,1], -packages[i]) for i in items]) <= 2
prob += LpAffineExpression([(x[i,number_of_bins], packages[i]) for i in items] + [(x[i,1], -packages[i]) for i in items]) == y
prob.solve()
print(LpStatus[prob.status])
for b in bins:
print(b,':',', '.join([str(packages[i]) for i in items if value(x[i,b]) !=0 ]))
print('left out: ', ', '.join([str(packages[i]) for i in items if sum(value(x[i,b]) for b in bins) ==0 ]))
答案 1 :(得分:0)
一个棘手的人,真的不确定最佳解决方案。下面是一个仅在第一个解决方案中迭代所有可能的组并暂停的解决方案。这应该是最少剩余的解决方案,因为我们首先遍历所有解决方案而没有剩余。
它还会迭代解决方案,将其作为第一个容器中的所有内容,可以将其排除在外,以实现更快的结果。
import numpy as np
def int_to_base_list(x, base, length):
""" create a list of length length that expresses a base-10 integer
e.g. binary: int2list(101, 2, 10) returns array([0, 0, 0, 1, 1, 0, 0, 1, 0, 1])
"""
placeholder = np.array([0] * length) # will contain the actual answer
for i in reversed(range(length)):
# standard base mathematics, see http://www.oxfordmathcenter.com/drupal7/node/18
placeholder[i] = x % base
x //= base
return placeholder
def get_groups(packages, max_diff_sum, number_of_bins):
""" Get number_of_bins packaging groups that differ no more than max_diff_sum
e.g.
[5, 5, 5, 5, 5, 5, 10, 11] with 2, 3 gives [5,5,5], [10,5], [11,5]
[5, 10, 12] with 2, 2 gives [10], [12]
[2, 6, 12] with 2, 3 gives [2], [], []
We approach the problem by iterating over group indices, so the first
example above has solution [0 0 0 1 2 3 1 2] with the highest number being
the 'remainder' group.
"""
length = len(packages)
for i in range((number_of_bins + 1)**length - 1): # All possible arrangements in groups
index = int_to_base_list(i, number_of_bins + 1, length) # Get the corresponding indices
sums_of_bins = [np.sum(packages[index==ii]) for ii in range(number_of_bins)]
if max(sums_of_bins) - min(sums_of_bins) <= max_diff_sum: # the actual requirement
# print(index)
break
groups = [packages[index==ii] for ii in range(number_of_bins)]
# remainder = packages[index==number_of_bins+1]
return groups
在您的示例中:
packages = np.array([5, 5, 5, 5, 5, 5, 10, 11])
max_diff_sum = 2
number_of_bins = 3
get_groups(packages, max_diff_sum, number_of_bins)
>> [array([5, 5, 5]), array([ 5, 10]), array([ 5, 11])]
还有
packages = np.array([5,10,12])
max_diff_sum = 2
number_of_bins = 2
get_groups(packages, max_diff_sum, number_of_bins)
>> [array([10]), array([12])]