给定一个数字列表,您可以通过几种不同的方式将它们加在一起以获得总和S?

时间:2019-02-27 02:08:01

标签: python combinations permutation

给出一个数字列表,您可以通过几种不同的方式将它们加在一起以获得总和S?

示例:

列表= [1,2]

S = 5

1)1 + 1 + 1 + 1 + 1 = 5

2)1 + 1 + 1 + 2 = 5

3)1 + 2 + 2 = 5

4)2 + 1 + 1 + 1 = 5

5)2 + 2 + 1 = 5

6)1 + 2 + 1 + 1 = 5

7)1 + 1 + 2 + 1 = 5

8)2 + 1 + 2 = 5

答案= 8

这是我尝试过的方法,但仅输出3作为答案

lst = [1, 2]
i = 1
result = 0
while i <= 5:
    s_list = [sum(comb) for comb in combinations_with_replacement(lst, i)]
    for val in s_list:
        if val == 5:
            result += 1
    i+= 1

print(result)

但是,这将输出三个。我相信它会输出三个,因为它没有考虑您可以添加数字的顺序。有关如何解决此问题的任何想法。

该问题应该适用于更大的数据:但是,我给出了一个简单的例子来给出一般的想法。

5 个答案:

答案 0 :(得分:1)

同时使用itertools.combinations_with_replacementpermutations

import itertools

l = [1,2]
s = 5

res = []
for i in range(1, s+1):
    for tup in itertools.combinations_with_replacement(l, i):
        if sum(tup) == s:
            res.extend(list(itertools.permutations(tup, i)))
res = list(set(res))

print(res)
[(1, 2, 2),
 (2, 2, 1),
 (1, 1, 2, 1),
 (1, 2, 1, 1),
 (2, 1, 1, 1),
 (1, 1, 1, 2),
 (2, 1, 2),
 (1, 1, 1, 1, 1)]

print(len(res))
# 8

答案 1 :(得分:1)

如何使用动态编程?我相信它更容易理解并且可以轻松实现。

def cal(target, choices, record):

    min_choice = min(choices)
    if min_choice > target:
        return False

    for i in range(0, target+1):
        if i == 0:
            record.append(1)
        elif i < min_choice:
            record.append(0)
        elif i == min_choice:
            record.append(1)
        else:
            num_solution = 0
            j = 0
            while j < len(choices) and i-choices[j] >= 0:
                num_solution += record[i-choices[j]]
                j += 1
            record.append(num_solution)


choices = [1, 2]
record = []
cal(5, choices, record)
print(record)
print(f"Answer:{record[-1]}")

这里的核心思想是使用一个额外的record数组来记录可以找到多少种方法来获取当前数字,例如record[2] = 2意味着我们可以习惯于获取21+12)的总和。

我们有record[target] = sum(record[target-choices[i]]),其中i遍历选择。试想一下,获取sum=5的方式必须与获取sum=4的方式相关,依此类推。

答案 2 :(得分:1)

使用Dynamic Programming

我们假设您的列表由[1,2,5]组成,因此我们具有以下递归函数:

f(n,[1,2,5]) = f(n-1,[1,2,5]) + f(n-2,[1,2,5]) + f(n-5,[1,2,5])

因为如果总和中的第一个数字为1,那么其余的您就有f(n-1,[1,2,5])选项;如果为2,那么其他的您有f(n-2,[1,2,5])选项,因此在...

因此从f(1)开始,然后逐步进行动态编程。在最坏的情况下,此解决方案是O(n^2),当您的列表中有O(n)个项目时就会发生这种情况。

解决方案是这样的:

answers = []
lst = [1,2]
number = 5
def f(target):
  val = 0
  for i in lst:               #O(lst.count())
    current = target - i
    if current > 0:
      val += answers[current-1]
  if lst.__contains__(target): #O(lst.count())
    val += 1
  answers.insert(target,val)

j = 1;
while j<=number:  #O(n) for while loop
  f(j)
  j+=1

print(answers[number-1])

here是有效版本。

答案 3 :(得分:0)

您希望使用递归遍历加法每个阶段的每种可能性,并在达到等于期望值的数字时将使用的数字传回。

def find_addend_combinations(sum_value, addend_choices, base=0, history=None):
    if history is None: history = []

    if base == sum_value:
        return tuple(history)
    elif base > sum_value:
        return None
    else:
        results = []
        for v in addend_choices:
            r = find_addend_combinations(sum_value, addend_choices, base + v,
                                         history + [v])
            if isinstance(r, tuple):
                results.append(r)
            elif isinstance(r, list):
                results.extend(r)
        return results

您可以将列表理解写在最后一部分,但是我认为这种方式更清楚。

答案 4 :(得分:0)

以不同顺序的元素组合被认为是等效的。例如,如果您仅谈论组合,则求和列表中的#3和#5被认为是等效的。

相反,如果排列由相同的元素以不同的顺序组成,则排列将认为这两个集合是唯一的。

要获得所需的答案,您需要将两个概念结合起来。

  1. 首先,使用您的技术来找到符合条件的组合
  2. 接下来,从组合中置换数字集合
  3. 最后,将生成的排列收集到一个集合中以删除重复项。
[ins] In [01]: def combination_generator(numbers, k, target):
          ...:     assert k > 0, "Must be a positive number; 'k = {}".format(k)
          ...:     assert len(numbers) > 0, "List of numbers must have at least one element"
          ...:
          ...:     for candidate in (
          ...:         {'numbers': combination, 'sum': sum(combination)}
          ...:         for num_elements in range(1, k + 1)
          ...:         for combination in itertools.combinations_with_replacement(numbers, num_elements)
          ...:     ):
          ...:         if candidate['sum'] != target:
          ...:             continue
          ...:         for permuted_candidate in itertools.permutations(candidate['numbers']):
          ...:             yield permuted_candidate
          ...:

[ins] In [02]: {candidate for candidate in combination_generator([1, 2], 5, 5)}
Out[02]:
{(1, 1, 1, 1, 1),
 (1, 1, 1, 2),
 (1, 1, 2, 1),
 (1, 2, 1, 1),
 (1, 2, 2),
 (2, 1, 1, 1),
 (2, 1, 2),
 (2, 2, 1)}