Python 创建对每个元素具有限制的组合,而不进行回顾性筛选

Python 创建对每个元素具有限制的组合,而不进行回顾性筛选,python,combinations,itertools,combinatorics,Python,Combinations,Itertools,Combinatorics,我需要创建一个列表的所有组合,但每个元素都有一个上下限(每个数字增加+2) 例如,n=4时: 从[0,1,2,3](下边界)到[0,2,4,6](上边界)的所有组合应导致: [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 2, 5], [0, 1, 2, 6], [0, 1, 3, 4], [0, 1, 3, 5], [0, 1, 3, 6], [0, 1, 4, 5], [0, 1, 4, 6], [0, 2, 3, 4], [0, 2, 3, 5]

我需要创建一个列表的所有组合,但每个元素都有一个上下限(每个数字增加+2)

例如,n=4时: 从[0,1,2,3](下边界)到[0,2,4,6](上边界)的所有组合应导致:

[[0, 1, 2, 3],
 [0, 1, 2, 4],
 [0, 1, 2, 5],
 [0, 1, 2, 6],
 [0, 1, 3, 4],
 [0, 1, 3, 5],
 [0, 1, 3, 6],
 [0, 1, 4, 5],
 [0, 1, 4, 6],
 [0, 2, 3, 4],
 [0, 2, 3, 5],
 [0, 2, 3, 6],
 [0, 2, 4, 5],
 [0, 2, 4, 6]]
直接的解决方案是使用
itertools.组合(范围(2*n-1),n)
,然后过滤掉所有无效的。但这首先会产生大量无效的组合,然后通过一个贯穿所有组合的过滤器进一步降低速度。在我的例子中,这对于大n来说太低效了

我需要一个解决方案,它甚至不尝试循环高于可能的每个数字,只在每个数字的边界内创建组合


我确信有一个简单的解决方案,可以使用嵌套循环或回溯,但我找不到它。

这里有一个使用递归的解决方案:

def n_increasing(n, start=0, end=0): 
    if n == 0: 
        yield [] 
        return 
    for choice in range(start, end+1): 
        for remaining in n_increasing(n-1, choice+1, end+2):
            yield [choice, *remaining] 
用法:

>>> list(n_increasing(4))
[[0, 1, 2, 3],
 [0, 1, 2, 4],
 [0, 1, 2, 5],
 [0, 1, 2, 6],
 [0, 1, 3, 4],
 [0, 1, 3, 5],
 [0, 1, 3, 6],
 [0, 1, 4, 5],
 [0, 1, 4, 6],
 [0, 2, 3, 4],
 [0, 2, 3, 5],
 [0, 2, 3, 6],
 [0, 2, 4, 5],
 [0, 2, 4, 6]]

下面是一个更一般问题的解决方案,您可以将下界和上界序列作为参数传递:

def约束的_组合(下部、上部):
下,上=列表(下),列表(上)
n=长度(较低)
如果len(上)!=n:
raise VALUERROR('下限和上限序列必须具有相同的长度')
#消除不可能的选择
对于范围(1,n)内的i:
下[i]=最大值(下[i],下[i-1]+1)
上[-i-1]=min(上[-i-1],上[-i]-1)
如果有(低>高表示低,高表示拉链(下、上)):
return()#无解决方案
def助手(t,i):
如果i==n:
产量t
其他:
a、 b=下[i],上[i]
如果t:a=max(a,t[-1]+1)
对于范围(a,b+1)内的j:
辅助者的收益率(t+(j),i+1)
返回帮助程序((),0)
例如:

>gen=受约束的组合([0,1,2,3],[0,2,4,6])
>>>列表(gen)
[(0, 1, 2, 3),
(0, 1, 2, 4),
(0, 1, 2, 5),
(0, 1, 2, 6),
(0, 1, 3, 4),
(0, 1, 3, 5),
(0, 1, 3, 6),
(0, 1, 4, 5),
(0, 1, 4, 6),
(0, 2, 3, 4),
(0, 2, 3, 5),
(0, 2, 3, 6),
(0, 2, 4, 5),
(0, 2, 4, 6)]

您可以对生成器使用递归:

a, b = [0, 1, 2, 3], [0, 2, 4, 6]
def combos(d, c = []):
  if not d:
     yield c
  else:
     yield from [b for i in range(*d[0]) for b in combos(d[1:], c+[i]) if not c or i > c[-1]]

print(list(combos(list(zip(a, [i+1 for i in b])))))
输出:

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

为什么当第三个位置的上限为4(
[0,2,4,6]
)时,
[0,1,5,6]
会出现在您的列表中?还是我误解了你的上限?谢谢!你是对的,这是我的错误:编辑并更正。例如,
[0,2,2,3]
它是否也有效?问题很好,但在本例中没有。组合中没有重复项,就像itertools.combines解释它一样-但没有不必要的重复项。