在Python中首先列举具有最小数字的3个排列

时间:2017-10-02 08:22:13

标签: python math permutation combinatorics itertools

这适用于Python,以显示[0, 1, 2, 3, 4]的所有3-permutations

import itertools
N = 5
for p in itertools.permutations(range(N), r=3):
    print p

#(0, 1, 2)
#(0, 1, 3)
#(0, 1, 4)
#(0, 2, 1)
#(0, 2, 3)
#(0, 2, 4)
#(0, 3, 1)
#...

但是我希望按照这个顺序列举它们:最低的数字第一,即:

#display 3-permutations of [0]
# (none)

#display 3-permutations of [0, 1] that haven't been displayed before
# (none)

#display 3-permutations of [0, 1, 2] that haven't been displayed before
(0, 1, 2)
(0, 2, 1)
(1, 0, 2)
(1, 2, 0)
(2, 0, 1)
(2, 1, 0)

#display 3-permutations of [0, 1, 2, 3] that haven't been displayed before
(0, 1, 3)
(0, 2, 3)
(0, 3, 1)
(0, 3, 2)
...

#display remaining 3-permutations of [0, 1, 2, 3, 4] that haven't been displayed before
...

有没有办法用这个顺序快速枚举[0,...,N-1]的3个排列?

注意:在我的用例N > 2000中,所以它必须很快(我正在使用Cython进行其他计算以使其快速,但这是另一个主题)。

编辑(感谢@RoryDaulton):每个组中的顺序无关紧要,我只关心分组。

6 个答案:

答案 0 :(得分:2)

这是一种非常快速且几乎不使用额外内存的算法。

首先,使用itertools枚举[0, 1, 2]的3-permuations。

然后,枚举[0, 1, 2]的2个排列,并且在产生每个排列之前,在结尾处插入3。然后再次枚举这些2-permutations并在中间位置插入3。然后再次枚举它们并在开始位置插入3

然后枚举[0, 1, 2, 3]的2个排列并在末尾插入4。然后再次枚举这些2-permutations并在中间位置插入4。然后...

你明白了。您可以通过在第一代之后保存2个排列来节省一些时间,这样您就可以在适当的位置插入较大的值。

注意:我建议使用此算法来获得示例中给出的3个排列的确切顺序。如果组内的顺序可能不同,则其他算法也是可能的,并且比我的快。我的算法运行得很好并且完全给出了所述的顺序,但它比具有不同顺序的算法慢。

答案 1 :(得分:1)

可能会优化对集合中p的搜索,但是通过使用集合来实现显示排列本身的目标的一种方法是:

import itertools
N = 5
spam = set()
for i in range(N):
    print('new permutation', list(range(i+1)))
    for p in itertools.permutations(range(i+1), r=3):
        if p not in spam:
            print(p)
            spam.add(p)

答案 2 :(得分:1)

我终于找到了一个解决方案,这似乎是最佳的:

for i in range(N):            # i is the biggest
    print 'biggest = %i' % i
    for j in range(i):        # j is the second
        for k in range(j):    # k is the smallest
                print i, j, k
                print j, k, i
                print k, i, j
                print j, i, k
                print k, j, i
                print i, k, j

这是输出

biggest = 0
biggest = 1
biggest = 2
2 1 0
1 0 2
0 2 1
1 2 0
0 1 2
2 0 1
biggest = 3
3 1 0
1 0 3
0 3 1
1 3 0
0 1 3
3 0 1
3 2 0
2 0 3
0 3 2
2 3 0
0 2 3
3 0 2
3 2 1
2 1 3
1 3 2
2 3 1
1 2 3
3 1 2
biggest = 4
4 1 0
1 0 4
0 4 1
1 4 0
0 1 4
4 0 1
4 2 0
2 0 4
0 4 2
2 4 0
0 2 4
4 0 2
4 2 1
2 1 4
1 4 2
2 4 1
1 2 4
4 1 2
4 3 0
3 0 4
0 4 3
3 4 0
0 3 4
4 0 3
4 3 1
3 1 4
1 4 3
3 4 1
1 3 4
4 1 3
4 3 2
3 2 4
2 4 3
3 4 2
2 3 4
4 2 3

答案 3 :(得分:1)

您的答案看起来是最好的方法,但您可以使用permutations使其更紧凑(并改善排序)。

from itertools import permutations

num = 5
for i in range(2, num):
    for j in range(i):
        for k in range(j):
            for t in permutations((k, j, i)):
                print(t)

<强>输出

(0, 1, 2)
(0, 2, 1)
(1, 0, 2)
(1, 2, 0)
(2, 0, 1)
(2, 1, 0)
(0, 1, 3)
(0, 3, 1)
(1, 0, 3)
(1, 3, 0)
(3, 0, 1)
(3, 1, 0)
(0, 2, 3)
(0, 3, 2)
(2, 0, 3)
(2, 3, 0)
(3, 0, 2)
(3, 2, 0)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
(0, 1, 4)
(0, 4, 1)
(1, 0, 4)
(1, 4, 0)
(4, 0, 1)
(4, 1, 0)
(0, 2, 4)
(0, 4, 2)
(2, 0, 4)
(2, 4, 0)
(4, 0, 2)
(4, 2, 0)
(1, 2, 4)
(1, 4, 2)
(2, 1, 4)
(2, 4, 1)
(4, 1, 2)
(4, 2, 1)
(0, 3, 4)
(0, 4, 3)
(3, 0, 4)
(3, 4, 0)
(4, 0, 3)
(4, 3, 0)
(1, 3, 4)
(1, 4, 3)
(3, 1, 4)
(3, 4, 1)
(4, 1, 3)
(4, 3, 1)
(2, 3, 4)
(2, 4, 3)
(3, 2, 4)
(3, 4, 2)
(4, 2, 3)
(4, 3, 2)

这是我之前提出的一些代码。它更紧凑,但当N很大时它会占用大量的RAM。

from itertools import permutations

num = 5
a = [(i, 1<<i) for i in range(num)]
perms = sorted(permutations(a, 3), key=lambda t: sum(u[1] for u in t))
for t in perms:
    print(tuple(u[0] for u in t))

这产生与上述代码相同的输出(以相同的顺序)。

<小时/> FWIW,这是Rory Daulton' algorithm的实现。请注意,输出顺序略有不同。

from itertools import permutations, combinations

num = 5
for i in range(2, num):
    for u, v in combinations(range(i), 2):
        for t in permutations((u, v, i)):
            print(t)

<强>输出

(0, 1, 2)
(0, 2, 1)
(1, 0, 2)
(1, 2, 0)
(2, 0, 1)
(2, 1, 0)
(0, 1, 3)
(0, 3, 1)
(1, 0, 3)
(1, 3, 0)
(3, 0, 1)
(3, 1, 0)
(0, 2, 3)
(0, 3, 2)
(2, 0, 3)
(2, 3, 0)
(3, 0, 2)
(3, 2, 0)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
(0, 1, 4)
(0, 4, 1)
(1, 0, 4)
(1, 4, 0)
(4, 0, 1)
(4, 1, 0)
(0, 2, 4)
(0, 4, 2)
(2, 0, 4)
(2, 4, 0)
(4, 0, 2)
(4, 2, 0)
(0, 3, 4)
(0, 4, 3)
(3, 0, 4)
(3, 4, 0)
(4, 0, 3)
(4, 3, 0)
(1, 2, 4)
(1, 4, 2)
(2, 1, 4)
(2, 4, 1)
(4, 1, 2)
(4, 2, 1)
(1, 3, 4)
(1, 4, 3)
(3, 1, 4)
(3, 4, 1)
(4, 1, 3)
(4, 3, 1)
(2, 3, 4)
(2, 4, 3)
(3, 2, 4)
(3, 4, 2)
(4, 2, 3)
(4, 3, 2)

答案 4 :(得分:1)

@ Uvar帖子的抽象生成函数变体:

<强>代码

import itertools as it


def unique_permute(iterable, r=3, verbose=False):
    seen = set()
    for i, _ in enumerate(iterable):
        part = iterable[:i+1]
        if verbose: print("# Display 3-permutations of {} that haven't been displayed before".format(part))
        for p in it.permutations(part, r=r):
            if p not in seen:
                yield p
            seen.add(p)

<强>演示

lst = [0, 1, 2, 3, 4]
for p in unique_permute(lst, verbose=True):
    print("", p)

输出

# Display 3-permutations of [0] that haven't been displayed before
# Display 3-permutations of [0, 1] that haven't been displayed before
# Display 3-permutations of [0, 1, 2] that haven't been displayed before
 (0, 1, 2)
 (0, 2, 1)
 (1, 0, 2)
 (1, 2, 0)
 (2, 0, 1)
 (2, 1, 0)
# Display 3-permutations of [0, 1, 2, 3] that haven't been displayed before
 (0, 1, 3)
 (0, 2, 3)
 (0, 3, 1)
 (0, 3, 2)
 ...

答案 5 :(得分:0)

@Rory Daulton的解决方案有一个班轮:

session

输出:

from itertools import *
a=[0,1,2,3,4]
print '\n'.join(['\n'.join([str(list(permutations(t)))  for t in list(combinations(a[:i+1],3)) if t not in list(combinations(a[:i],3))]) for i in range(2,len(a))])