如何在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]
此解決方案實現了一個生成器,以免將全部排列保留在內存中: 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
從Python 2.6開始 (若是您使用的是Python 3),您能夠使用一個標準庫工具: itertools.permutations
。 rest
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.product
: ip
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)
在Python 2.6及更高版本中: 內存
import itertools itertools.permutations([1,2,3])
(做爲生成器返回。使用list(permutations(l))
做爲列表返回。) ci
正如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
),所以還能夠提升內存效率。
如下代碼是給定列表的就地排列,實現爲生成器。 因爲僅返回對列表的引用,所以不該在生成器外部修改列表。 該解決方案是非遞歸的,所以使用低內存。 輸入列表中元素的多個副本也能夠很好地工做。
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