如何在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