How to generate permutations of a list without “reverse duplicates” in Python using generators

后端 未结 10 945
情歌与酒
情歌与酒 2020-12-03 12:09

This is related to question How to generate all permutations of a list in Python

How to generate all permutations that match following criteria: if

相关标签:
10条回答
  • 2020-12-03 12:41

    How about this:

    from itertools import permutations
    
    def rev_generator(plist):
        reversed_elements = set()
        for i in permutations(plist):
            if i not in reversed_elements:
                reversed_i = tuple(reversed(i))
                reversed_elements.add(reversed_i)
                yield i
    
    >>> list(rev_generator([1,2,3]))
    [(1, 2, 3), (1, 3, 2), (2, 1, 3)]
    

    Also, if the return value must be a list, you could just change the yield i to yield list(i), but for iteration purposes, the tuples will work just fine.

    0 讨论(0)
  • 2020-12-03 12:42

    EDIT: changed completely to keep everything as a generator (never the whole list in memory). Should fulfill the requirements (only calculates half of the possible permutations (not the reverse ones). EDIT2: added shorter (and simpler) factorial function from here.

    EDIT3:: (see comments) - a version with improvements can be found in bwopah's version.

    def fac(x): 
        return (1 if x==0 else x * fac(x-1))
    
    def all_permutations(plist):
        global counter
    
        if len(plist) <=1:
            yield plist
        else:
            for perm in all_permutations(plist[1:]):
                for i in xrange(len(perm)+1):
                    if len(perm[:i] + plist[0:1] + perm[i:]) == lenplist:
                            if counter == limit:
                                 raise StopIteration
                            else:
                                 counter = counter + 1
                    yield perm[:i] + plist[0:1] + perm[i:]
    
    counter = 0
    plist = ['a','b','c']
    lenplist = len(plist)
    limit = fac(lenplist) / 2
    
    all_permutations_gen = all_permutations(plist)
    print all_permutations_gen
    print list(all_permutations_gen)
    
    0 讨论(0)
  • 2020-12-03 12:51

    Some setup code first:

    try:
        from itertools import permutations
    except ImportError:
        # straight from http://docs.python.org/library/itertools.html#itertools.permutations
        def permutations(iterable, r=None):
            # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
            # permutations(range(3)) --> 012 021 102 120 201 210
            pool = tuple(iterable)
            n = len(pool)
            r = n if r is None else r
            if r > n:
                return
            indices = range(n)
            cycles = range(n, n-r, -1)
            yield tuple(pool[i] for i in indices[:r])
            while n:
                for i in reversed(range(r)):
                    cycles[i] -= 1
                    if cycles[i] == 0:
                        indices[i:] = indices[i+1:] + indices[i:i+1]
                        cycles[i] = n - i
                    else:
                        j = cycles[i]
                        indices[i], indices[-j] = indices[-j], indices[i]
                        yield tuple(pool[i] for i in indices[:r])
                        break
                else:
                    return
    
    def non_reversed_permutations(iterable):
        "Return non-reversed permutations for an iterable with unique items"
        for permutation in permutations(iterable):
            if permutation[0] < permutation[-1]:
                yield permutation
    
    0 讨论(0)
  • 2020-12-03 12:53

    Here is code that does the trick. To get rid of the dups I noticed that for your list if the value of the first location is greater than the value of the last location then it will be a dup. I create a map to keep track of where each item was in the list to start with and then use that map to do the test. The code also does not use recursion so it keeps its memory footprint small. Also the list can be of any type items not just numbers see the last two test cases.

    Here is the code.

    class Permutation:
        def __init__(self, justalist):
            self._data = justalist[:]
            self._len=len(self._data)
            self._s=[]
            self._nfact=1
            self._map ={}
            i=0
            for elem in self._data:
                self._s.append(elem)
                self._map[str(elem)]=i
                i+=1
                self._nfact*=i
            if i != 0:
                self._nfact2=self._nfact//i
    
        def __iter__(self):
            for k in range(self._nfact):
                for i in range(self._len):
                    self._s[i]=self._data[i]
                s=self._s
                factorial=self._nfact2
                for i in range(self._len-1):
                    tempi = (k // factorial) % (self._len - i)
                    temp = s[i + tempi]
                    for j in range(i + tempi,i,-1):
                        s[j] = s[j-1]
                    s[i] = temp
                    factorial //= (self._len - (i + 1))
    
                if self._map[str(s[0])] < self._map[str(s[-1])]:
                    yield s
    
    
    
    
    s=[1,2]
    print("_"*25)
    print("input list:",s)
    for sx in Permutation(s):
        print(sx)
    
    s=[1,2,3]
    print("_"*25)
    print("input list:",s)
    for sx in Permutation(s):
        print(sx)
    
    s=[1,2,3,4]
    print("_"*25)
    print("input list:",s)
    for sx in Permutation(s):
        print(sx)
    
    s=[3,2,1]
    print("_"*25)
    print("input list:",s)
    for sx in Permutation(s):
        print(sx)
    
    s=["Apple","Pear","Orange"]
    print("_"*25)
    print("input list:",s)
    for sx in Permutation(s):
        print(sx)
    
    s=[[1,4,5],"Pear",(1,6,9),Permutation([])]
    print("_"*25)
    print("input list:",s)
    for sx in Permutation(s):
        print(sx)
    

    and here is the output for my test cases.

    _________________________
    input list: [1, 2]
    [1, 2]
    _________________________
    input list: [1, 2, 3]
    [1, 2, 3]
    [1, 3, 2]
    [2, 1, 3]
    _________________________
    input list: [1, 2, 3, 4]
    [1, 2, 3, 4]
    [1, 2, 4, 3]
    [1, 3, 2, 4]
    [1, 3, 4, 2]
    [1, 4, 2, 3]
    [1, 4, 3, 2]
    [2, 1, 3, 4]
    [2, 1, 4, 3]
    [2, 3, 1, 4]
    [2, 4, 1, 3]
    [3, 1, 2, 4]
    [3, 2, 1, 4]
    _________________________
    input list: [3, 2, 1]
    [3, 2, 1]
    [3, 1, 2]
    [2, 3, 1]
    _________________________
    input list: ['Apple', 'Pear', 'Orange']
    ['Apple', 'Pear', 'Orange']
    ['Apple', 'Orange', 'Pear']
    ['Pear', 'Apple', 'Orange']
    _________________________
    input list: [[1, 4, 5], 'Pear', (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>]
    [[1, 4, 5], 'Pear', (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>]
    [[1, 4, 5], 'Pear', <__main__.Permutation object at 0x0142DEF0>, (1, 6, 9)]
    [[1, 4, 5], (1, 6, 9), 'Pear', <__main__.Permutation object at 0x0142DEF0>]
    [[1, 4, 5], (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>, 'Pear']
    [[1, 4, 5], <__main__.Permutation object at 0x0142DEF0>, 'Pear', (1, 6, 9)]
    [[1, 4, 5], <__main__.Permutation object at 0x0142DEF0>, (1, 6, 9), 'Pear']
    ['Pear', [1, 4, 5], (1, 6, 9), <__main__.Permutation object at 0x0142DEF0>]
    ['Pear', [1, 4, 5], <__main__.Permutation object at 0x0142DEF0>, (1, 6, 9)]
    ['Pear', (1, 6, 9), [1, 4, 5], <__main__.Permutation object at 0x0142DEF0>]
    ['Pear', <__main__.Permutation object at 0x0142DEF0>, [1, 4, 5], (1, 6, 9)]
    [(1, 6, 9), [1, 4, 5], 'Pear', <__main__.Permutation object at 0x0142DEF0>]
    [(1, 6, 9), 'Pear', [1, 4, 5], <__main__.Permutation object at 0x0142DEF0>]
    
    0 讨论(0)
提交回复
热议问题