找到与一组数字唯一求和的数字的所有组合

时间:2018-04-19 02:24:32

标签: python algorithm search combinations

我有一个数字列表(源集),有人拿了部分或全部这些数字并随机求和,制作一组较小的数字(目标集)。我有源集和目标集,但我需要弄清楚使用了什么组合。难度:我无法保证源集中的所有数字都用于制作目标集,并且我不能保证目标集中的所有数字都来自源集。这是一个简单的例子:

  • 来源集= 1,2,3,4,5
  • 目标集= 3,8
  • 结果:
    • ResultSet1:Sum([1,2])= 3 Sum([3,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])
  • 答案无效:
    • InvalidSet1:Sum([1,2])= 3 Sum([3])= 3 Sum([3,5])= 8 NotFound([])NotUsed:[4]
      • 原因:源集中的每个数字只能用一次来创建给定结果集中的任何目标

我找到了一些很好的例子来推导上面给出的单个目标值,但我找不到用目标值数组而不是单个值来实现它的方法(遗憾的是我的编码技能不高到任务)。我有一个最好的开始是this question,代码如下(注意我删除了 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)

这将输出:

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

我已经做了一些勇敢的尝试来添加第二层递归来支持目标的数组,但我不会因为把失败放在这里而使自己难堪。

我知道上面的代码有局限性,我对基于此代码的解决方案或全新代码持开放态度,并以几乎任何逻辑格式输出。 Python是首选,但我会接受任何事情(注意:java给我荨麻疹)。

2 个答案:

答案 0 :(得分:0)

你的意思是:

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)}]

答案 1 :(得分:0)

我找到的唯一方法是效率很低,而且我很确定必须采用更聪明的方式,但它确实有效。

我们的想法是获取所有组合,获取第一个数字的组合,遍历所有组合,从列表中删除使用过的数字,生成所有组合,获取与第二个数字匹配的组合并迭代。

这是代码。再次,非常丑陋,但它完成了工作:

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)