如何在Python中生成列表的全部排列

如何在Python中生成列表的全部排列,而與列表中元素的類型無關? 函數

例如: 工具

permutations([])
[]

permutations([1])
[1]

permutations([1, 2])
[1, 2]
[2, 1]

permutations([1, 2, 3])
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

#1樓

此解決方案實現了一個生成器,以免將全部排列保留在內存中: spa

def permutations (orig_list):
    if not isinstance(orig_list, list):
        orig_list = list(orig_list)

    yield orig_list

    if len(orig_list) == 1:
        return

    for n in sorted(orig_list):
        new_list = orig_list[:]
        pos = new_list.index(n)
        del(new_list[pos])
        new_list.insert(0, n)
        for resto in permutations(new_list[1:]):
            if new_list[:1] + resto <> orig_list:
                yield new_list[:1] + resto

#2樓

從Python 2.6開始 (若是您使用的是Python 3),您能夠使用一個標準庫工具: itertools.permutationsrest

import itertools
list(itertools.permutations([1, 2, 3]))

若是您出於某種緣由使用舊版本的Python(<2.6) ,或者只是想知道它的工做原理,那麼這是一種不錯的方法,摘自http://code.activestate.com/recipes/252178/code

def all_perms(elements):
    if len(elements) <=1:
        yield elements
    else:
        for perm in all_perms(elements[1:]):
            for i in range(len(elements)):
                # nb elements[0:1] works in both string and list contexts
                yield perm[:i] + elements[0:1] + perm[i:]

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

另外一個基於itertools.productip

def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)

#3樓

Python 2.6及更高版本中: 內存

import itertools
itertools.permutations([1,2,3])

(做爲生成器返回。使用list(permutations(l))做爲列表返回。) ci


#4樓

正如tzwenn的回答,確實能夠迭代每一個排列的第一個元素。 我更喜歡這樣寫這個解決方案: element

def all_perms(elements):
    if len(elements) <= 1:
        yield elements  # Only permutation possible = no permutation
    else:
        # Iteration over the first element in the result permutation:
        for (index, first_elmt) in enumerate(elements):
            other_elmts = elements[:index]+elements[index+1:]
            for permutation in all_perms(other_elmts): 
                yield [first_elmt] + permutation

該解決方案的速度提升了約30%,這顯然歸功於遞歸以len(elements) <= 1而不是0 。 就像Riccardo Reyes的解決方案同樣,它使用生成器函數(經過yield ),所以還能夠提升內存效率。


#5樓

如下代碼是給定列表的就地排列,實現爲生成器。 因爲僅返回對列表的引用,所以不該在生成器外部修改列表。 該解決方案是非遞歸的,所以使用低內存。 輸入列表中元素的多個副本也能夠很好地工做。

def permute_in_place(a):
    a.sort()
    yield list(a)

    if len(a) <= 1:
        return

    first = 0
    last = len(a)
    while 1:
        i = last - 1

        while 1:
            i = i - 1
            if a[i] < a[i+1]:
                j = last - 1
                while not (a[i] < a[j]):
                    j = j - 1
                a[i], a[j] = a[j], a[i] # swap the values
                r = a[i+1:last]
                r.reverse()
                a[i+1:last] = r
                yield list(a)
                break
            if i == first:
                a.reverse()
                return

if __name__ == '__main__':
    for n in range(5):
        for a in permute_in_place(range(1, n+1)):
            print a
        print

    for a in permute_in_place([0, 0, 1, 1, 1]):
        print a
    print
相關文章
相關標籤/搜索