简体   繁体   English

查找唯一求和为一组数字的所有数字组合

[英]Find All Combinations of Numbers That Uniquely Sum to a Set of Numbers

I have a list of numbers (source set), and someone took some or all of those numbers and summed them randomly to make a smaller set of numbers (target set). 我有一个数字列表(源集),有人拿走了部分或全部这些数字,然后将它们随机求和,以得出较小的一组数字(目标集)。 I have the source set and the target set, but I need to figure out what combination was used. 我有源集和目标集,但是我需要弄清楚使用了哪种组合。 Difficulty: I can't guarantee that all numbers in the source set were used to make the target set, and I can't guarantee that all numbers in the target set came from the source set. 难度:我不能保证将源集中的所有数字都用作目标集,也不能保证目标集中的所有数字都来自源集合。 Here's a simple example: 这是一个简单的例子:

  • Source Set = 1,2,3,4,5 源集= 1,2,3,4,5
  • Target Set = 3,8 目标设定= 3,8
  • Result: 结果:
    • ResultSet1: Sum([1,2])=3 Sum([3,5])=8 NotFound([]) NotUsed([4]) ResultSet1:Sum([1,2])= 3 Sum([3,5])= 8 NotFound([])NotUsed([4])
    • ResultSet2: Sum([3])=3 Sum([1,2,5])=8 NotFound([]) NotUsed([4]) ResultSet2:Sum([3])= 3 Sum([1,2,5])= 8 NotFound([])NotUsed([4])
    • ResultSet3: Sum([1,3,4])=8 NotFound([3]) NotUsed([2,5]) ResultSet3:Sum([1,3,4])= 8 NotFound([3])NotUsed([2,5])
  • Invalid answers: 无效的答案:
    • InvalidSet1: Sum([1,2])=3 Sum([3])=3 Sum([3,5])=8 NotFound([]) NotUsed:[4] InvalidSet1:Sum([1,2])= 3 Sum([3])= 3 Sum([3,5])= 8 NotFound([])未使用:[4]
      • Reason: each number from the source set may only be used once to create any target in a given result set 原因:源集中的每个数字只能用于一次在给定结果集中创建任何目标

I have found some great examples for deriving the above given a single target value, but I can't find a way to do it with an array of target values rather than a single value (and my coding skills are unfortunately not up to the task). 我已经找到了一些很好的示例,可以在给定单个目标值的情况下推导上述值,但是我找不到一种方法来对目标值数组(而不是单个值)进行处理(很遗憾,我的编码技能无法胜任该任务)。 The best start I have is this question , with the code below (note that I removed the s >= target check to accommodate my data): 我最好的开始就是这个问题 ,下面的代码(请注意,我删除了s> = target检查以容纳我的数据):

def subset_sum(numbers, target, partial=[]):
    s = sum(partial)

    # check if the partial sum is equals to target
    if s == target: 
        print "sum(%s)=%s" % (partial, target)
    #if s >= target:
    #    return  # if we reach the number why bother to continue

    for i in range(len(numbers)):
        n = numbers[i]
        remaining = numbers[i+1:]
        subset_sum(remaining, target, partial + [n]) 

if __name__ == "__main__":
    subset_sum([1,2,3,4,5],4)

This will output: 这将输出:

sum([1, 3])=4
sum([4])=4

I've made some valiant attempts at adding a second layer of recursion to support an array for the target, but I won't embarrass myself by putting the failures here. 我已经做了一些英勇的尝试,添加了第二层递归以支持目标数组,但是我不会因为失败而感到尴尬。

I understand that the above code has limitations, and I'm open to solutions based on this code, or completely new code, and output in just about any logical format. 我知道上面的代码有局限性,并且我愿意接受基于此代码或全新代码的解决方案,并以几乎任何逻辑格式输出。 Python is strongly preferred, but I would entertain just about anything (note: java gives me hives). 强烈推荐使用Python,但是我几乎会接受所有事情(请注意:java给了我荨麻疹)。

You mean something like: 您的意思是:

In []:
import itertools as it

source = [1, 2, 3, 4, 5]
targets = [3, 8]

p = [[a for n in range(1, len(source)) for a in it.combinations(source, n) if sum(a) == t] for t in targets]
[dict(zip(targets, a)) for a in it.product(*p) if len(sum(a, tuple())) == len(set(sum(a, tuple())))]

Out[]:
[{3: (3,), 8: (1, 2, 5)}, {3: (1, 2), 8: (3, 5)}]

The only way I found is quite inefficient, and I'm pretty sure there has to be a smarter way, but it works. 我发现的唯一方法是效率很低,而且我敢肯定必须有一种更聪明的方法,但是它可以工作。

The idea is to get all combinations, get the ones for the first number, go through all of them, remove the used numbers from the list, generate all combinations, get those that match the second number and iterate. 想法是获取所有组合,获取第一个数字的组合,遍历所有组合,从列表中删除使用的数字,生成所有组合,获取与第二个数字匹配的组合并进行迭代。

Here's the code. 这是代码。 Again, quite ugly but it does the job: 同样,这很丑陋,但确实可以做到:

from collections import defaultdict
import itertools as it

def get_all_combinations(l):
    return [a for n in range(1, len(l)) for a in it.combinations(l, n)]

def get_combinations_for_target(combinations, target):
    if combinations is None:
        return []
    return [combination for combination in combinations if sum(combination) == target]

def get_list_without_used_numbers(l, combination):
    used_numbers = []
    for item in combination:
        used_numbers.append(item)

    diff_list = list(set(l) - set(used_numbers))
    return diff_list

source = [1, 2, 3, 4, 5]
combinations = get_all_combinations(source)
combinations_first =  get_combinations_for_target(combinations, 3)

combinations_both = defaultdict(dict)

for combination in combinations_first:
    partial_list = get_list_without_used_numbers(source, combination)
    partial_combinations = get_all_combinations(partial_list)
    combinations_both[combination] = get_combinations_for_target(partial_combinations, 8)

print(combinations_both)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM