Python itertools与sum组合

时间:2019-05-17 13:06:15

标签: python python-3.x sum combinations itertools

我的客户是一家生产不同块(不同尺寸)木地板的公司。他们的问题是,当有人在木地板上购买XXm²时,他们必须将其装箱。

此盒子最大可容纳220厘米。片段从30厘米开始,上升5厘米,直到完整的220厘米为止。

我要达到什么目的?箱子中不同部分和大小的最佳组合至少要有一个大块的底部,否则箱子会“刹车”。那是因为盒子需要坚固的底部才能处理上面的其他物件。

是的。让我们去看看代码吧!

客户输入作品,诸如此类:

[220, 170, 150, 145, 130, 125, 125, 115, 110, 110, 105, 95, 95, 90,
 90, 90, 75, 70, 70, 60, 60, 50, 50, 50, 50, 50, 50, 40]

最好的输出之一是:

['220', '170,50', '150,70', '145,75', '130,90',
 '125,95', '125,95', '115,105', '110,110', '90,90,40',
 '70,50,50,50', '60,60,50,50',]

我正在尝试使用itertools:

from itertools import combinations
for i in range(1, 5): # Maximum 4 pieces
    for comb in combinations(customer_input, i):
        if sum(comb) <= 220 and sum(comb) >= 195:
            print(comb)

第一张照片是:

(220,)
(170, 50)
(170, 50)
(170, 50)
(170, 50)
(170, 50)
(170, 50)

看起来好像正确,但是,它一次输出组合(170, 50)。我认为,一种方法是在使用了一部分后停止尝试进行新的组合。

我该如何实现?

3 个答案:

答案 0 :(得分:2)

这是一种做我认为您正在尝试的方法:

from collections import Counter

# Generate all combinations up to n elements without repetitions
# with sum up to max_sum
def combs(p, n, max_sum):
    p = sorted(p, reverse=True)
    return _combs_rec(p, n, max_sum, 0, [], 0)

def _combs_rec(p, n, max_sum, idx, current, current_sum):
    if len(current) > 0:
        yield current_sum, tuple(current)
    prev = None
    if n > 0:
        for i in range(idx, len(p)):
            if p[i] != prev and  current_sum + p[i] <= max_sum:
                current.append(p[i])
                yield from _combs_rec(p, n - 1, max_sum, i + 1, current, current_sum + p[i])
                current.pop()
            prev = p[i]

# Input data
pieces = [220, 170, 150, 145, 130, 125, 125, 115, 110, 110, 105, 95, 95, 90,
          90, 90, 75, 70, 70, 60, 60, 50, 50, 50, 50, 50, 50, 40]
max_group = 4
max_sum = 220

# Get maximum combinations
c = Counter(pieces)
while sum(c.values()) > 0:
    next_sum, next_comb = max(combs(c.elements(), max_group, max_sum))
    c.subtract(next_comb)
    print(f'{next_comb} Sum: {next_sum}')

输出:

(220,) Sum: 220
(170, 50) Sum: 220
(150, 70) Sum: 220
(145, 75) Sum: 220
(130, 90) Sum: 220
(125, 95) Sum: 220
(125, 95) Sum: 220
(115, 105) Sum: 220
(110, 110) Sum: 220
(90, 90, 40) Sum: 220
(70, 50, 50, 50) Sum: 220
(60, 60, 50, 50) Sum: 220

请注意,这不一定能提供最佳解决方案。即使每次迭代都在进行蛮力搜索以寻求最佳组合,但它仍然是一个贪婪的算法。无法保证每次选择最佳组合都会使您获得总松弛度最小的组合组。为此,您将必须在该组片段的分区的全局空间内进行搜索。

答案 1 :(得分:0)

使用set是避免重复的简单解决方案:

customer_input = [220, 170, 150, 145, 130, 125, 125, 115, 110, 110, 105, 95, 95, 90,
 90, 90, 75, 70, 70, 60, 60, 50, 50, 50, 50, 50, 50, 40]

from itertools import combinations
combs = set()
for i in range(1, 5): # Maximum 4 pieces
    for comb in combinations(customer_input, i):
        if sum(comb) <= 220 and sum(comb) >= 195 and comb not in combs:
            print(comb)
            combs.add(comb)

# part of output:
# (220,)
# (170, 50)
# (170, 40)
# (150, 70)
# (150, 60)
# (150, 50)
# (145, 75)

答案 2 :(得分:0)

这不是代码答案,而是一种拓宽视野的想法。

这种问题可以称为优化问题,称为Knapsack problem

解决背包问题的一种方法可能是使用动态编程(我链接的维基百科页面上有关于这一部分的内容)。关于如何进行类似编程的教程也很多。 例如,这里有一个freecodecamp tutorial用于动态解决背包问题;它不是在Python中,但重要的部分是了解如何解决此类问题。

我希望您可以从正确的方向开始,以获得最佳的代码解决方案。