【问题标题】:Find all the combinations from the given set of numbers that apply to some rules从给定的一组数字中找出适用于某些规则的所有组合
【发布时间】:2020-06-23 15:57:12
【问题描述】:

感谢您对以下问题的帮助:

我有一个数字列表和一本字典:

my_list = [400, 200, 100, 50, 25]
my_dict = {400: 8, 200:4, 100: 2, 50: 2, 25: 2}

我需要从 my_list 中的数字中找出所有可能的组合,当它们应该应用以下规则时:

  1. 组合中可能包含相同的数字([400, 400, 400] 是有效组合)
  2. 顺序无关紧要(我的[400, 200, 100] 与[100, 200, 400] 相同)
  3. 组合中的数字之和不应超过1200
  4. my_dict[my_list_item] 的总和不应超过 24(例如,[400, 400, 400, 100] 无效,因为my_dict[400]+my_dict[400]+my_dict[400]+my_dict[100] = 8+8+8+2 = 26,因此它不是有效的组合)

python算法解决这个问题的任何想法。 谢谢

【问题讨论】:

  • 你会如何手工完成?
  • 另外,这部分不是很清楚 - 输出“组合”可以多长时间?

标签: python algorithm statistics


【解决方案1】:

这里是使用迭代器递归执行此操作以避免在内存中占用过多的代码。

#! /usr/bin/env python3

def combs (numbers, value_of, max_numbers=1200, max_value=24, min_i=0):
    if len(numbers) <= min_i:
        yield []
    else:
        for comb in combs(numbers, value_of, max_numbers, max_value, min_i+1):
            yield comb
            comb_copy = [n for n in comb] # Copy to avoid sharing bugs.
            numbers_sum = sum(comb)
            values_sum = sum([value_of[i] for i in comb])
            while True:
                comb_copy.append(numbers[min_i])
                numbers_sum += numbers[min_i]
                if max_numbers < numbers_sum:
                    break
                values_sum += value_of[numbers[min_i]]
                if max_value < values_sum:
                    break
                yield comb_copy


my_list = [400, 200, 100, 50, 25]
my_dict = {400: 8, 200:4, 100: 2, 50: 2, 25: 2}

for c in combs(my_list, my_dict):
    print(c)

【讨论】:

  • 很棒的代码 :) 但是当涉及到这样的问题时,使用递归是否很好......?我有一种感觉,直接计算组合会减少所有递归引起的不必要的计算(即相同组合有很多重复)
  • @KentaNomoto 如果你被要求计算一个计数,像这样的递归是可怕的,因为输出可能非常大。但在这里我们被要求实际找到组合。递归没有很多好的替代方案 - 您能做的最好的事情就是限制在产生大量输出时使用多少内存。
  • 哦,我现在明白了。是的。递归可能是您可以在这里采用的最有效的方法。正如您所说,与其他方法相比,非常大的输入使用的内存要低得多。
  • @KentaNomoto 请注意,诀窍不仅仅是递归。它是递归和迭代器。这样你只需要保持你在内存中生成组合的位置,而不是整个数据结构。仅仅递归地生成完整的数据集是行不通的。
【解决方案2】:

假设您想要“列表与重复的每个组合”的列表,我编写了一个计算它的代码。

它使用一个 while 循环,每次都会增加组合长度计数器。 由于组合的长度可以无限增长,因此该指示器用于停止循环,该循环在没有找到单个组合时激活。

from itertools import combinations_with_replacement

my_list = [400, 200, 100, 50, 25]
my_dict = {400: 8, 200:4, 100: 2, 50: 2, 25: 2}

final_list = []

combinationLength = 1
while True:
    c = list(combinations_with_replacement(my_list, combinationLength)) #generate every combination of length combinationLength
    countNextCombination = False #indicator to break the while loop

    for combination in c: 
        if (sum(combination) <= 1200):
            dicsum=0
            for j in combination:
                dicsum+=my_dict[j]
            if (dicsum<=24):
                final_list.append(list(combination))
                countNextCombination = True

    if countNextCombination == False: #if not a single combination was valid, finish the loop
        break
    combinationLength += 1

print (final_list)

【讨论】:

  • 问题是“找到组合”而不是“找到多少组合”。因此,您正在回答一个不同的问题。
  • @btilly 你是什么意思? “final_list”变量是所有组合的列表。编辑:哦,我现在明白了,combinationLength 变量命名可能有点误导。它不是总组合的长度,而是保存组合长度的变量(例如 [200,100,100] 是 3 的组合长度)
  • 没错。我的错。在这种情况下,您最终可能会保留在内存中的内存量非常大,以至于您的代码根本无法运行。
【解决方案3】:

使用 itertools,您将不需要递归解决方案。 combination_with_replacement 迭代器将为您提供组合,您只需检查您的条件。根据您的要求,您将需要一个组合的power set(即所有尺寸的组合):

例如:

from itertools import combinations_with_replacement as combine

my_list = [400, 200, 100, 50, 25]
my_dict = {400: 8, 200:4, 100: 2, 50: 2, 25: 2}

for size in range(2,len(my_list)+1):
    for combo in combine(my_list, size):
        if sum(combo) <= 1200 \
        and sum(my_dict[c] for c in combo) <= 24:
            print(combo)

如果你打算以某种方式处理组合,你可以将它放在一个函数中并使用yield combo 而不是print(combo)

输出:

(400, 400)
(400, 200)
(400, 100)
(400, 50)
(400, 25)
...
(50, 50, 50, 50, 50)
(50, 50, 50, 50, 25)
(50, 50, 50, 25, 25)
(50, 50, 25, 25, 25)
(50, 25, 25, 25, 25)
(25, 25, 25, 25, 25)

请注意,这是一种未经优化的蛮力方法,可能适用于小型数据集,但随着您拥有更多数据,需要的时间会呈指数级增长。有更高级的方法(使用递归)可以通过检查部分和/或使用 meoization 来跳过整个组合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-12
    • 2016-03-12
    • 2019-03-06
    相关资源
    最近更新 更多