How to generate all permutations of a list in Python
如何在python中独立于列表中的元素类型生成列表的所有排列?
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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] |
从python 2.6开始(如果你在python 3上的话),你有一个标准的库工具:
1 2 | import itertools list(itertools.permutations([1, 2, 3])) |
号
如果您出于某种原因使用了一个旧的python(<2.6),或者只是想知道它是如何工作的,下面是一个不错的方法,摘自http://code.activestate.com/recipes/252178/:
1 2 3 4 5 6 7 8 | 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:] |
在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 |
。
另一个,基于
1 2 3 4 5 6 7 | 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以后的版本中:
1 2 | import itertools itertools.permutations([1,2,3]) |
。
(作为发电机返回。使用
以下代码仅用于python 2.6及更高版本
一是进口
1 | import itertools |
排列(顺序事项):
1 2 3 4 5 | print list(itertools.permutations([1,2,3,4], 2)) [(1, 2), (1, 3), (1, 4), (2, 1), (2, 3), (2, 4), (3, 1), (3, 2), (3, 4), (4, 1), (4, 2), (4, 3)] |
。组合(顺序不重要):
1 2 | print list(itertools.combinations('123', 2)) [('1', '2'), ('1', '3'), ('2', '3')] |
笛卡尔积(有几个iterables):
1 2 3 4 | print list(itertools.product([1,2,3], [4,5,6])) [(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)] |
。笛卡尔积(有一个不可数及其本身):
1 2 3 | print list(itertools.product([1,2], repeat=3)) [(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2)] |
号
1 2 3 4 5 | def permutations(head, tail=''): if len(head) == 0: print tail else: for i in range(len(head)): permutations(head[0:i] + head[i+1:], tail+head[i]) |
号
称为:
1 | permutations('abc') |
号
1 2 3 4 5 6 7 8 9 10 11 12 | #!/usr/bin/env python def perm(a, k=0): if k == len(a): print a else: for i in xrange(k, len(a)): a[k], a[i] = a[i] ,a[k] perm(a, k+1) a[k], a[i] = a[i], a[k] perm([1,2,3]) |
号
输出:
1 2 3 4 5 6 | [1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 2, 1] [3, 1, 2] |
号
当我交换列表的内容时,需要一个可变的序列类型作为输入。例如,
这种python实现是受到Horowitz、Sahni和Rajasekeran在《计算机算法》一书中提出的算法的启发。
此解决方案实现了一个生成器,以避免在内存中保留所有排列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
以下代码是给定列表的就地排列,作为生成器实现。因为它只返回对列表的引用,所以不应在生成器外部修改列表。解决方案是非递归的,因此使用的内存很低。还可以很好地处理输入列表中元素的多个副本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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 for a in permute_in_place([0, 0, 1, 1, 1]): print a |
。
在我看来,一个相当明显的方法可能是:
1 2 3 4 5 6 7 8 9 10 | def permutList(l): if not l: return [[]] res = [] for e in l: temp = l[:] temp.remove(e) res.extend([[e] + r for r in permutList(temp)]) return res |
号
功能性风格
1 2 3 4 5 6 7 8 9 | def addperm(x,l): return [ l[0:i] + [x] + l[i:] for i in range(len(l)+1) ] def perm(l): if len(l) == 0: return [[]] return [x for y in perm(l[1:]) for x in addperm(l[0],y) ] print perm([ i for i in range(3)]) |
结果是:
1 | [[0, 1, 2], [1, 0, 2], [1, 2, 0], [0, 2, 1], [2, 0, 1], [2, 1, 0]] |
1 2 3 4 5 6 7 8 | list2Perm = [1, 2.0, 'three'] listPerm = [[a, b, c] for a in list2Perm for b in list2Perm for c in list2Perm if ( a != b and b != c and a != c ) ] print listPerm |
号
输出:
1 2 3 4 5 6 7 8 | [ [1, 2.0, 'three'], [1, 'three', 2.0], [2.0, 1, 'three'], [2.0, 'three', 1], ['three', 1, 2.0], ['three', 2.0, 1] ] |
号
我使用了一种基于阶乘数系统的算法——对于长度为n的列表,您可以逐项组装每个排列,从每个阶段留下的项中进行选择。第一个项目有N个选项,第二个项目有N-1个选项,最后一个项目只有一个选项,所以可以使用阶乘数字系统中数字的数字作为索引。这样数字从0到n!-1对应于词典编纂顺序中所有可能的排列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from math import factorial def permutations(l): permutations=[] length=len(l) for x in xrange(factorial(length)): available=list(l) newPermutation=[] for radix in xrange(length, 0, -1): placeValue=factorial(radix-1) index=x/placeValue newPermutation.append(available.pop(index)) x-=index*placeValue permutations.append(newPermutation) return permutations permutations(range(3)) |
输出:
1 | [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]] |
号
此方法是非递归的,但在我的计算机上速度稍慢,当n时xrange会产生错误!太大,无法转换为C长整数(n=13)。当我需要它的时候,它已经足够了,但它不是iTertools。
注意,该算法的时间复杂度为
打印跑步结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | global result result = [] def permutation(li): if li == [] or li == None: return if len(li) == 1: result.append(li[0]) print result result.pop() return for i in range(0,len(li)): result.append(li[i]) permutation(li[:i] + li[i+1:]) result.pop() |
例子:
1 | permutation([1,2,3]) |
。
输出:
1 2 3 4 5 6 | [1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 1, 2] [3, 2, 1] |
号
我们确实可以迭代每个排列的第一个元素,就像Tzwenn的答案一样;我更喜欢用这种方式编写这个解决方案:
1 2 3 4 5 6 7 8 9 | 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%,显然是由于递归以
这是由使用列表理解的haskell实现所启发的:
1 2 3 4 5 6 7 8 9 10 | def permutation(list): if len(list) == 0: return [[]] else: return [[x] + ys for x in list for ys in permutation(delete(list, x))] def delete(list, item): lc = list[:] lc.remove(item) return lc |
号
为了提高性能,Knuth(第22页)提出了一个麻木的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from numpy import empty, uint8 from math import factorial def perms(n): f = 1 p = empty((2*n-1, factorial(n)), uint8) for i in range(n): p[i, :f] = i p[i+1:2*i+1, :f] = p[:i, :f] # constitution de blocs for j in range(i): p[:i+1, f*(j+1):f*(j+2)] = p[j+1:j+i+2, :f] # copie de blocs f = f*(i+1) return p[:n, :] |
号
复制大块内存节省时间-比
1 2 3 4 5 | In [1]: %timeit -n10 list(permutations(range(10))) 10 loops, best of 3: 815 ms per loop In [2]: %timeit -n100 perms(10) 100 loops, best of 3: 40 ms per loop |
号
递归之美:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | >>> import copy >>> def perm(prefix,rest): ... for e in rest: ... new_rest=copy.copy(rest) ... new_prefix=copy.copy(prefix) ... new_prefix.append(e) ... new_rest.remove(e) ... if len(new_rest) == 0: ... print new_prefix + new_rest ... continue ... perm(new_prefix,new_rest) ... >>> perm([],['a','b','c','d']) ['a', 'b', 'c', 'd'] ['a', 'b', 'd', 'c'] ['a', 'c', 'b', 'd'] ['a', 'c', 'd', 'b'] ['a', 'd', 'b', 'c'] ['a', 'd', 'c', 'b'] ['b', 'a', 'c', 'd'] ['b', 'a', 'd', 'c'] ['b', 'c', 'a', 'd'] ['b', 'c', 'd', 'a'] ['b', 'd', 'a', 'c'] ['b', 'd', 'c', 'a'] ['c', 'a', 'b', 'd'] ['c', 'a', 'd', 'b'] ['c', 'b', 'a', 'd'] ['c', 'b', 'd', 'a'] ['c', 'd', 'a', 'b'] ['c', 'd', 'b', 'a'] ['d', 'a', 'b', 'c'] ['d', 'a', 'c', 'b'] ['d', 'b', 'a', 'c'] ['d', 'b', 'c', 'a'] ['d', 'c', 'a', 'b'] ['d', 'c', 'b', 'a'] |
该算法是最有效的算法,避免了递归调用中的数组传递和操作,适用于python 2,3:
1 2 3 4 5 6 7 8 9 10 11 12 13 | def permute(items): length = len(items) def inner(ix=[]): do_yield = len(ix) == length - 1 for i in range(0, length): if i in ix: #avoid duplicates continue if do_yield: yield tuple([items[y] for y in ix + [i]]) else: for p in inner(ix + [i]): yield p return inner() |
号
用途:
1 2 3 4 5 6 7 8 9 | for p in permute((1,2,3)): print(p) (1, 2, 3) (1, 3, 2) (2, 1, 3) (2, 3, 1) (3, 1, 2) (3, 2, 1) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def pzip(c, seq): result = [] for item in seq: for i in range(len(item)+1): result.append(item[i:]+c+item[:i]) return result def perm(line): seq = [c for c in line] if len(seq) <=1 : return seq else: return pzip(seq[0], perm(seq[1:])) |
号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | from __future__ import print_function def perm(n): p = [] for i in range(0,n+1): p.append(i) while True: for i in range(1,n+1): print(p[i], end=' ') print("") i = n - 1 found = 0 while (not found and i>0): if p[i]<p[i+1]: found = 1 else: i = i - 1 k = n while p[i]>p[k]: k = k - 1 aux = p[i] p[i] = p[k] p[k] = aux for j in range(1,(n-i)/2+1): aux = p[i+j] p[i+j] = p[n-j+1] p[n-j+1] = aux if not found: break perm(5) |
号
这里有一个算法可以在列表上工作,而不创建类似于ber解决方案的新中间列表,网址为https://stackoverflow.com/a/108651/184528。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def permute(xs, low=0): if low + 1 >= len(xs): yield xs else: for p in permute(xs, low + 1): yield p for i in range(low + 1, len(xs)): xs[low], xs[i] = xs[i], xs[low] for p in permute(xs, low + 1): yield p xs[low], xs[i] = xs[i], xs[low] for p in permute([1, 2, 3, 4]): print p |
您可以在这里自己尝试代码:http://repl.it/j9v
生成所有可能的排列
我用的是python3.4:
1 2 3 4 5 6 7 8 9 10 11 12 | def calcperm(arr, size): result = set([()]) for dummy_idx in range(size): temp = set() for dummy_lst in result: for dummy_outcome in arr: if dummy_outcome not in dummy_lst: new_seq = list(dummy_lst) new_seq.append(dummy_outcome) temp.add(tuple(new_seq)) result = temp return result |
号
测试用例:
1 2 3 4 5 6 | lst = [1, 2, 3, 4] #lst = ["yellow","magenta","white","blue"] seq = 2 final = calcperm(lst, seq) print(len(final)) print(final) |
为了给大家节省可能的搜索和实验时间,下面是Python中的非递归排列解决方案,它也适用于numba(从0.41版开始):
1 2 3 4 5 6 7 8 | @numba.njit() def permutations(A, k): r = [[i for i in range(0)]] for i in range(k): r = [[a] + b for a in A for b in r if (a in b)==False] return r permutations([1,2,3],3) [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] |
。
要给人留下关于表演的印象:
1 2 3 4 5 6 7 8 | %timeit permutations(np.arange(5),5) 243 μs ± 11.1 μs per loop (mean ± std. dev. of 7 runs, 1 loop each) time: 406 ms %timeit list(itertools.permutations(np.arange(5),5)) 15.9 μs ± 8.61 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) time: 12.9 s |
因此,只有在必须从Njitted函数调用它时才使用此版本,否则更喜欢ITertools实现。
另一个解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 | def permutation(flag, k =1 ): N = len(flag) for i in xrange(0, N): if flag[i] != 0: continue flag[i] = k if k == N: print flag permutation(flag, k+1) flag[i] = 0 permutation([0, 0, 0]) |
。
我看到在这些递归函数中有很多迭代,而不是纯粹的递归…
所以对于那些甚至不能遵守一个循环的人来说,这里有一个粗俗的,完全不必要的完全递归的解决方案
1 2 3 4 5 6 7 8 9 10 11 | def all_insert(x, e, i=0): return [x[0:i]+[e]+x[i:]] + all_insert(x,e,i+1) if i<len(x)+1 else [] def for_each(X, e): return all_insert(X[0], e) + for_each(X[1:],e) if X else [] def permute(x): return [x] if len(x) < 2 else for_each( permute(x[1:]) , x[0]) perms = permute([1,2,3]) |
。
另一种方法(无LIB)
1 2 3 4 5 6 7 8 9 10 11 12 | def permutation(input): if len(input) == 1: return input if isinstance(input, list) else [input] result = [] for i in range(len(input)): first = input[i] rest = input[:i] + input[i + 1:] rest_permutation = permutation(rest) for p in rest_permutation: result.append(first + p) return result |
号
输入可以是字符串或列表
1 2 | print(permutation('abcd')) print(permutation(['a', 'b', 'c', 'd'])) |
号
我的python解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def permutes(input,offset): if( len(input) == offset ): return [''.join(input)] result=[] for i in range( offset, len(input) ): input[offset], input[i] = input[i], input[offset] result = result + permutes(input,offset+1) input[offset], input[i] = input[i], input[offset] return result # input is a"string" # return value is a list of strings def permutations(input): return permutes( list(input), 0 ) # Main Program print( permutations("wxyz") ) |
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | def permutation(word, first_char=None): if word == None or len(word) == 0: return [] if len(word) == 1: return [word] result = [] first_char = word[0] for sub_word in permutation(word[1:], first_char): result += insert(first_char, sub_word) return sorted(result) def insert(ch, sub_word): arr = [ch + sub_word] for i in range(len(sub_word)): arr.append(sub_word[i:] + ch + sub_word[:i]) return arr assert permutation(None) == [] assert permutation('') == [] assert permutation('1') == ['1'] assert permutation('12') == ['12', '21'] print permutation('abc') |
输出:''abc'、'acb'、'bac'、'bca'、'cab'、'cba']
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from collections import Counter def permutations(nums): ans = [[]] cache = Counter(nums) for idx, x in enumerate(nums): result = [] for items in ans: cache1 = Counter(items) for id, n in enumerate(nums): if cache[n] != cache1[n] and items + [n] not in result: result.append(items + [n]) ans = result return ans permutations([1, 2, 2]) > [[1, 2, 2], [2, 1, 2], [2, 2, 1]] |
。
这种方法比我看到的其他方法更好,看看吧。
1 2 3 4 5 6 | def permutations(arr): if not arr: return print arr for idx, val in enumerate(arr): permutations(arr[:idx]+arr[idx+1:]) |
对于python,我们可以使用itertools并导入排列和组合来解决您的问题。
1 2 3 | from itertools import product, permutations A = ([1,2,3]) print (list(permutations(sorted(A),2))) |