带约束的Python置换

带约束的Python置换,python,permutation,itertools,Python,Permutation,Itertools,我使用的是Python3,我试图找到一种方法来获取列表的所有排列,同时强制执行一些约束 例如,我有一个列表L=[1,2,3,4,5,6,7] 我想找到所有的排列。然而,我的限制是: 1应该总是在2之前 3应该在4之前,4应该在5之前 最后,6应该在7之前 当然,我可以生成所有置换,并忽略那些不遵循这些约束的置换,但我想这不会有效。一种方法是采用一种交换算法,当您要将元素交换到其最终位置时,请检查其顺序是否正确。下面的代码就是这样做的 但首先让我展示一下它的用法: L = [1, 2, 3,

我使用的是Python3,我试图找到一种方法来获取列表的所有排列,同时强制执行一些约束

例如,我有一个列表
L=[1,2,3,4,5,6,7]

我想找到所有的排列。然而,我的限制是:

  • 1应该总是在2之前
  • 3应该在4之前,4应该在5之前
  • 最后,6应该在7之前

当然,我可以生成所有置换,并忽略那些不遵循这些约束的置换,但我想这不会有效。

一种方法是采用一种交换算法,当您要将元素交换到其最终位置时,请检查其顺序是否正确。下面的代码就是这样做的

但首先让我展示一下它的用法:

L = [1, 2, 3, 4, 5, 6, 7]
constraints = [[1, 2], [3, 4, 5], [6, 7]]

A = list(p[:] for p in constrained_permutations(L, constraints)) # copy the permutation if you want to keep it
print(len(A))
print(["".join(map(str, p)) for p in A[:50]])
以及输出:

210
['1234567', '1234657', '1234675', '1236457', '1236475', '1236745', '1263457', '1263475', '1263745', '1267345', '1324567', '1324657', '1324675', '1326457', '1326475', '1326745', '1342567', '1342657', '1342675', '1345267', '1345627', '1345672', '1346527', '1346572', '1346257', '1346275', '1346725', '1346752', '1364527', '1364572', '1364257', '1364275', '1364725', '1364752', '1362457', '1362475', '1362745', '1367245', '1367425', '1367452', '1634527', '1634572', '1634257', '1634275', '1634725', '1634752', '1632457', '1632475', '1632745', '1637245']
但现在代码:

def _permute(L, nexts, numbers, begin, end):
    if end == begin + 1:
        yield L
    else:
        for i in range(begin, end):
            c = L[i]
            if nexts[c][0] == numbers[c]:
                nexts[c][0] += 1
                L[begin], L[i] = L[i], L[begin]
                for p in _permute(L, nexts, numbers, begin + 1, end):
                    yield p
                L[begin], L[i] = L[i], L[begin]
                nexts[c][0] -= 1


def constrained_permutations(L, constraints):
    # warning: assumes that L has unique, hashable elements
    # constraints is a list of constraints, where each constraint is a list of elements which should appear in the permatation in that order
    # warning: constraints may not overlap!
    nexts = dict((a, [0]) for a in L)
    numbers = dict.fromkeys(L, 0) # number of each element in its constraint
    for constraint in constraints:
        for i, pos in enumerate(constraint):
            nexts[pos] = nexts[constraint[0]]
            numbers[pos] = i

    for p in _permute(L, nexts, numbers, 0, len(L)):
        yield p

这种方法使用简单的过滤器过滤置换

import itertools

groups = [(1,2),(3,4,5),(6,7)]
groupdxs = [i for i, group in enumerate(groups) for j in range(len(group))]
old_combo = []
for dx_combo in itertools.permutations(groupdxs):
    if dx_combo <= old_combo: # as simple filter
        continue
    old_combo = dx_combo
    iters = [iter(group) for group in groups]
    print [next(iters[i]) for i in dx_combo]
导入itertools
组=[(1,2)、(3,4,5)、(6,7)]
groupdxs=[i代表i,枚举(组)中的组代表范围(len(组))中的j]
旧组合=[]
对于itertools中的dx_组合。置换(groupdxs):

如果你说“before”时是dx_组合,你的意思是直接在“before”之前吗?i、 e.
[3 1 2…]
是有效的,但
[1 3 2…]
不是(“1应该总是在2之前”)。不,它不必直接在2之前。(1,3,2)也是有效的(你不需要第二个参数来
排列
。这也很好!)这不是(以一种非常优雅的方式)仍然会生成所有的排列,然后过滤它们吗?(itertools.permutations正在生成与给定[1,2,3,4,5,6,7]一样多的不同顺序。)@andrew cooke:我想是的<代码>列表(itertools.permutations((0,0))
->
[(0,0),(0,0)]
itertools.permutations()
在任何情况下都会生成
N!
置换。@安德鲁库克:是的,它只是过滤置换,效率不高。但是它比直接测试更复杂的约束更容易过滤。还有额外的
。如果你使用C++ <代码> STD::NExtPyPrimeType()/Cuffe,那么你不需要过滤,例如,你能给出一个它应该如何运行的例子(我不理解代码和<代码>列表(PultAll置换((1,2),(3,4,5),(6,7))< /COD>给出<代码> []/COD>)。在2.7.2中,它对我很有效。方法很简单:通过尝试每组的第一个数字,然后是所有其他数字的排列(保持它们以相同的方式分组),递归地生成排列。
def partial_permutations(*groups):
    groups = list(filter(None, groups)) # remove empties.
    # Since we iterate over 'groups' twice, we need to
    # make an explicit copy for 3.x for this approach to work.
    if not groups:
        yield []
        return
    for group in groups:
        for pp in partial_permutations(*(
             g[1:] if g == group else g
             for g in groups
        )):
            yield [group[0]] + pp