【问题标题】:Python itertools make combinations with sumPython itertools 与 sum 组合
【发布时间】:2019-05-17 13:06:15
【问题描述】:

我的一位客户是一家生产不同(不同尺寸)木地板的公司。他们有一个问题,当有人购买 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) 更多一次。我认为一种方法是在使用一块之后停止尝试制作新的组合。

我怎样才能实现它?

【问题讨论】:

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


    【解决方案1】:

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

    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
    

    请注意,这不一定给出最佳解决方案。尽管每次迭代都在进行暴力搜索以寻找最佳组合,但它本质上仍然是一个贪婪算法。无法保证每次选择最佳组合都可以让您获得总松弛最小的组合集。为此,您必须在这组片段的分区的全局空间内进行搜索。

    【讨论】:

    • 看来你准确地预测了我需要什么!这是最好的答案!!即使没有使用 itertools,我也得到了答案!谢谢你,兄弟!干杯!
    【解决方案2】:

    使用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)
    

    【讨论】:

      【解决方案3】:

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

      这类问题可以作为优化问题来处理,称为Knapsack problem

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

      我希望这能让您朝着正确的方向开始,以获得最佳的代码解决方案。

      【讨论】:

        猜你喜欢
        • 2015-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多