Python permutations with constraints

前端 未结 3 1956
忘了有多久
忘了有多久 2020-12-24 15:51

I am using python 3 and I am trying to find a way to get all the permutations of a list while enforcing some constraints.

For instance, I have a list L=[1, 2,

相关标签:
3条回答
  • 2020-12-24 16:22

    One way is to take one of the swapping algorithms, and when you are about to swap an element to its final position, check if it's in the right order. The code below does just this.

    But first let me show its usage:

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

    and the output:

    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']
    

    But now the code:

    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
    
    0 讨论(0)
  • 2020-12-24 16:23
    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
    
    0 讨论(0)
  • 2020-12-24 16:33

    This approach filters permutations using a simple filter.

    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]
    

    What we are doing here is finding permutations of a multiset. (In this case the multiset is groupdxs.) Here's a paper that details an O(1) algorithm for this.

    0 讨论(0)
提交回复
热议问题