Python,两个列表之间元素加法的所有组合,带有约束

时间:2019-03-04 16:39:24

标签: python list loops recursion itertools

我有两个列表:

list1 = [1, 2, 3]
list2 = [0.5, 1]

任务是通过将第二个列表中的变量添加到其元素来从原始列表中创建所有可能的组合:

list1 = [1+0.5, 2, 3]
list2 = [1, 2+0.5, 3]
list3 = [1, 2, 3+0.5]
list4 = [1+0.5, 2+0.5, 3]
list5 = [1, 2+0.5, 3+0.5]
list6 = [1+0.5, 2, 3+0.5]
list7 = [1+0.5, 2+0.5, 3+0.5]

list8 = [1+1, 2, 3]
list9 = [1,2+1,3]
list10 = [1,2,3+1]
list 11 = [1+1,2+1,3]
...
list = [1+0.5, 2+1, 3]
list = [1+0.5, 2, 3+1]
...

给问题增加了复杂性,我想做上述事情,但是有一个约束:例如,新列表中所有元素的总和应小于8。

有没有很好的方法可以做到这一点?可能递归地给出我的初始列表将包含约30个元素,而第二个列表则包含约5个元素?

谢谢。

2 个答案:

答案 0 :(得分:0)

您可以将递归与生成器一起使用,从而提供一个条件来检查累积和的运行值:

具有演示字符串表示形式的解决方案:

def pairs(a, b, _l, _c = []):
  if len(_c) == _l:
    yield _c
  else:
     if a:
       for i in b:
         yield from pairs(a[1:], b, _l, _c=_c+[f'{a[0]}+{i}'])
         yield from pairs(a[1:], b, _l, _c= _c+[a[0]])

print(list(pairs([1, 2, 3], [0.5, 1], 3)))

输出:

[['1+0.5', '2+0.5', '3+0.5'], ['1+0.5', '2+0.5', 3], ['1+0.5', '2+0.5', '3+1'], ['1+0.5', '2+0.5', 3], ['1+0.5', 2, '3+0.5'], ['1+0.5', 2, 3], ['1+0.5', 2, '3+1'], ['1+0.5', 2, 3], ['1+0.5', '2+1', '3+0.5'], ['1+0.5', '2+1', 3], ['1+0.5', '2+1', '3+1'], ['1+0.5', '2+1', 3], ['1+0.5', 2, '3+0.5'], ['1+0.5', 2, 3], ['1+0.5', 2, '3+1'], ['1+0.5', 2, 3], [1, '2+0.5', '3+0.5'], [1, '2+0.5', 3], [1, '2+0.5', '3+1'], [1, '2+0.5', 3], [1, 2, '3+0.5'], [1, 2, 3], [1, 2, '3+1'], [1, 2, 3], [1, '2+1', '3+0.5'], [1, '2+1', 3], [1, '2+1', '3+1'], [1, '2+1', 3], [1, 2, '3+0.5'], [1, 2, 3], [1, 2, '3+1'], [1, 2, 3], ['1+1', '2+0.5', '3+0.5'], ['1+1', '2+0.5', 3], ['1+1', '2+0.5', '3+1'], ['1+1', '2+0.5', 3], ['1+1', 2, '3+0.5'], ['1+1', 2, 3], ['1+1', 2, '3+1'], ['1+1', 2, 3], ['1+1', '2+1', '3+0.5'], ['1+1', '2+1', 3], ['1+1', '2+1', '3+1'], ['1+1', '2+1', 3], ['1+1', 2, '3+0.5'], ['1+1', 2, 3], ['1+1', 2, '3+1'], ['1+1', 2, 3], [1, '2+0.5', '3+0.5'], [1, '2+0.5', 3], [1, '2+0.5', '3+1'], [1, '2+0.5', 3], [1, 2, '3+0.5'], [1, 2, 3], [1, 2, '3+1'], [1, 2, 3], [1, '2+1', '3+0.5'], [1, '2+1', 3], [1, '2+1', '3+1'], [1, '2+1', 3], [1, 2, '3+0.5'], [1, 2, 3], [1, 2, '3+1'], [1, 2, 3]]

添加和修剪的解决方案:

def pairs(a, b, _l, _c = [], _sum=0):
  if len(_c) == _l:
    yield _c
  else:
    if a:
      for i in b:
        if a[0]+i+_sum < 8:
          yield from pairs(a[1:], b, _l, _c=_c+[a[0]+i], _sum=_sum+a[0]+i)
        if a[0]+_sum < 8:
          yield from pairs(a[1:], b, _l, _c= _c+[a[0]], _sum=_sum+a[0])

print(list(pairs([1, 2, 3], [0.5, 1], 3, _sum=0)))

输出:

[[1.5, 2.5, 3.5], [1.5, 2.5, 3], [1.5, 2.5, 3], [1.5, 2, 3.5], [1.5, 2, 3], [1.5, 2, 4], [1.5, 2, 3], [1.5, 3, 3], [1.5, 3, 3], [1.5, 2, 3.5], [1.5, 2, 3], [1.5, 2, 4], [1.5, 2, 3], [1, 2.5, 3.5], [1, 2.5, 3], [1, 2.5, 4], [1, 2.5, 3], [1, 2, 3.5], [1, 2, 3], [1, 2, 4], [1, 2, 3], [1, 3, 3.5], [1, 3, 3], [1, 3, 3], [1, 2, 3.5], [1, 2, 3], [1, 2, 4], [1, 2, 3], [2, 2.5, 3], [2, 2.5, 3], [2, 2, 3.5], [2, 2, 3], [2, 2, 3], [2, 2, 3.5], [2, 2, 3], [2, 2, 3], [1, 2.5, 3.5], [1, 2.5, 3], [1, 2.5, 4], [1, 2.5, 3], [1, 2, 3.5], [1, 2, 3], [1, 2, 4], [1, 2, 3], [1, 3, 3.5], [1, 3, 3], [1, 3, 3], [1, 2, 3.5], [1, 2, 3], [1, 2, 4], [1, 2, 3]]

编辑:指定下限:

def pairs(a, b, _l, _c = [], _sum=0):
  if len(_c) == _l:
    if _sum > 2:
      yield _c
  else:
    if a:
      for i in b:
        if a[0]+i+_sum < 8:
          yield from pairs(a[1:], b, _l, _c=_c+[a[0]+i], _sum=_sum+a[0]+i)
        if a[0]+_sum < 8:
          yield from pairs(a[1:], b, _l, _c= _c+[a[0]], _sum=_sum+a[0])

print(list(pairs([1, 2, 3], [-1, 1], 3, _sum=0)))

输出:

[[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 1, 3], [0, 2, 2], [0, 2, 3], [0, 2, 4], [0, 2, 3], [0, 3, 2], [0, 3, 3], [0, 3, 4], [0, 3, 3], [0, 2, 2], [0, 2, 3], [0, 2, 4], [0, 2, 3], [1, 1, 2], [1, 1, 3], [1, 1, 4], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 2, 4], [1, 2, 3], [1, 3, 2], [1, 3, 3], [1, 3, 3], [1, 2, 2], [1, 2, 3], [1, 2, 4], [1, 2, 3], [2, 1, 2], [2, 1, 3], [2, 1, 4], [2, 1, 3], [2, 2, 2], [2, 2, 3], [2, 2, 3], [2, 3, 2], [2, 2, 2], [2, 2, 3], [2, 2, 3], [1, 1, 2], [1, 1, 3], [1, 1, 4], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 2, 4], [1, 2, 3], [1, 3, 2], [1, 3, 3], [1, 3, 3], [1, 2, 2], [1, 2, 3], [1, 2, 4], [1, 2, 3]]

答案 1 :(得分:0)

您要描述的内容没有很多严格的约束是不可能的(即使那时也可能是不可能的)。

让我们只看一组list的。想象一下,您正在尝试将15个索引加到30的情况,并且所有五个可能的值都在起作用。这是5 15 (超过3000万种)方式来填充15个选定的索引,而30种选择15套索引来填充(超过1.55亿个),合计4,733,811,035,156,250,000(4.7亿)。可能会少一些,因为您将不得不至少使用五个值中的每一个值(否则您将使用较小的一组选定值重新计算相同的list),但是仍然会疯。这仅适用于15个索引,五个值的情况;其他的则要小一些,但是大多数人仍然无法在人类的生命周期中进行计算,更不用说总的计算了。

您可以尝试通过抢先计算大输入list的总和,并跳过任何保证超过最大累积值的值的组合,并抢先减小来进行过滤任何会导致您超出上限的价值的多样性。但这闻起来很an XY problem;每当您考虑这种组合性精神错乱时,您可能都有更好的方法来完成任务(和/或不可能完成任务)。

为简单起见,您只需结合使用itertools工具即可:

from itertools import combinations, product

def make_lists(list1, list2, limit):
    maxvalues = limit - sum(list1)
    minlist2 = min(list2)
    for numindices in range(1, len(list1)+1):
        if minlist2 * numindices >= maxvalues:
            continue
        for indices in combinations(range(len(list1)), numindices):
            for values in product([x for x in list2 if x < maxvalues], repeat=numindices):
                if sum(values) >= maxvalues:
                    continue
                newlist = list1[:]
                for i, v in zip(indices, values):
                    newlist[i] += v
                yield newlist

但是,对于任何有意义的输入长度,这都将花费“宇宙的热死”时间。递归解决方案可以更有效地过滤掉无效输出,但是除非限制非常严格,否则您仍然会在程序完成前死亡。