大小为 k 的递归排列,彼此之间没有相同的元素

Posted

技术标签:

【中文标题】大小为 k 的递归排列,彼此之间没有相同的元素【英文标题】:Recursive permutations of size k with no same elements next to each other 【发布时间】:2022-01-20 08:47:12 【问题描述】:

这是我的第一个问题,请原谅我的格式。我基本上是在尝试创建重复大小为 k 的排列,并且彼此之间没有重复元素。 所以,我有一个像 1,2,3,4 这样的列表和一个 k 值。 例如,通过取 1,2,3,4 和 k=4, 我们将得到一个大小为 k 且没有两个相同连续项的所有可能排列的列表,如下所示: 1,2,3,4 1,2,3,2 1,2,3,1 ..而这样的例子不胜枚举 我可以递归地进行基本排列,但我已经为此苦苦挣扎了一段时间。我不想导入任何库,因为我认为没有它就可以完成,我想将所有组合附加到一个列表中。任何帮助将不胜感激!

【问题讨论】:

那么,您需要枚举所有这些排列,还是随机抽取其中一个?如果您要采样,则至少需要一个来自random 的随机数生成器,因此如果没有库,就无法严格执行。 【参考方案1】:

只返回一个排列:

我知道,你不想使用库,但你需要有某种随机性:

from random import sample 
k = 4
l = [i for i in range(k)]
sample(l, k)

这将是递归的:

from random import randint

def recursvie_sample(l, k):
    if k == 1:
        return l
    else:
        return [l.pop(randint(0, k - 1))] + recursvie_sample(l, k - 1)

k = 4
l = [i for i in range(k)]
recursvie_sample(l.copy(), k)

注意:复制列表l作为recursive_sample的输入值,通过函数调用保持l不变,之后您仍然可以使用它...

编辑

列出所有排列,没有任何数字出现多次

def perm(l1, l2 = []):
    if not l1:
        result.append(l2)
    else:
        for i in range(len(l1)):
            l1_copy = l1.copy()
            l2_copy = l2.copy()
            l2_copy.append(l1_copy.pop(i))
            perm(l1_copy, l2_copy)

result = []
initList = [i for i in range(4)]
perm(initList)
print(result)

【讨论】:

您能解释一下这是如何解决样本中没有两个连续元素相同的限制的吗? 我一定表达错了!我的意思是所有大小为 k 的排列,没有重复元素。所以,我们会得到多个输出,而不仅仅是一个。 @IvanLeongito 但是你的例子1,3,2,3 不是一个有效的例子。请编辑问题以澄清! 已编辑!请注意,我需要在给定列表和给定 k 上应用该函数,因此在这种情况下,in range(4) 中的 i 将不起作用。 啊,好吧,您也想拥有条目/数字重复的项目。是啊,阿兰的解决方案当然是正确的……【参考方案2】:

要生成所有排列,可以将当前的部分排列向下传递,依次添加每一项,然后再调用递归添加另一个位置(跳过重复的项):

def perm(A,k,p=[]):
    if not k:                          # size reached
        yield p 
        return          
    for n in A:                        # combine every item
        if p and n == p[-1]: continue  # except the repeated one
        yield from perm(A,k-1,p+[n])   

输出:

# place in a list:

permList = list(perm([1,2,3,4],4))

# or access sequentially without storing:

for i,p in enumerate(perm([1,2,3,4],4)): print(i,p)

0 [1, 2, 1, 2]
1 [1, 2, 1, 3]
2 [1, 2, 1, 4]
3 [1, 2, 3, 1]
4 [1, 2, 3, 2]
5 [1, 2, 3, 4]
6 [1, 2, 4, 1]
7 [1, 2, 4, 2]
8 [1, 2, 4, 3]
9 [1, 3, 1, 2]
10 [1, 3, 1, 3]
11 [1, 3, 1, 4]
12 [1, 3, 2, 1]
13 [1, 3, 2, 3]
14 [1, 3, 2, 4]
15 [1, 3, 4, 1]
16 [1, 3, 4, 2]
17 [1, 3, 4, 3]
18 [1, 4, 1, 2]
19 [1, 4, 1, 3]
20 [1, 4, 1, 4]
21 [1, 4, 2, 1]
22 [1, 4, 2, 3]
23 [1, 4, 2, 4]
24 [1, 4, 3, 1]
25 [1, 4, 3, 2]
26 [1, 4, 3, 4]
27 [2, 1, 2, 1]
28 [2, 1, 2, 3]
29 [2, 1, 2, 4]
30 [2, 1, 3, 1]
31 [2, 1, 3, 2]
32 [2, 1, 3, 4]
33 [2, 1, 4, 1]
34 [2, 1, 4, 2]
35 [2, 1, 4, 3]
36 [2, 3, 1, 2]
37 [2, 3, 1, 3]
38 [2, 3, 1, 4]
39 [2, 3, 2, 1]
40 [2, 3, 2, 3]
41 [2, 3, 2, 4]
42 [2, 3, 4, 1]
43 [2, 3, 4, 2]
44 [2, 3, 4, 3]
45 [2, 4, 1, 2]
46 [2, 4, 1, 3]
47 [2, 4, 1, 4]
48 [2, 4, 2, 1]
49 [2, 4, 2, 3]
50 [2, 4, 2, 4]
51 [2, 4, 3, 1]
52 [2, 4, 3, 2]
53 [2, 4, 3, 4]
54 [3, 1, 2, 1]
55 [3, 1, 2, 3]
56 [3, 1, 2, 4]
57 [3, 1, 3, 1]
58 [3, 1, 3, 2]
59 [3, 1, 3, 4]
60 [3, 1, 4, 1]
61 [3, 1, 4, 2]
62 [3, 1, 4, 3]
63 [3, 2, 1, 2]
64 [3, 2, 1, 3]
65 [3, 2, 1, 4]
66 [3, 2, 3, 1]
67 [3, 2, 3, 2]
68 [3, 2, 3, 4]
69 [3, 2, 4, 1]
70 [3, 2, 4, 2]
71 [3, 2, 4, 3]
72 [3, 4, 1, 2]
73 [3, 4, 1, 3]
74 [3, 4, 1, 4]
75 [3, 4, 2, 1]
76 [3, 4, 2, 3]
77 [3, 4, 2, 4]
78 [3, 4, 3, 1]
79 [3, 4, 3, 2]
80 [3, 4, 3, 4]
81 [4, 1, 2, 1]
82 [4, 1, 2, 3]
83 [4, 1, 2, 4]
84 [4, 1, 3, 1]
85 [4, 1, 3, 2]
86 [4, 1, 3, 4]
87 [4, 1, 4, 1]
88 [4, 1, 4, 2]
89 [4, 1, 4, 3]
90 [4, 2, 1, 2]
91 [4, 2, 1, 3]
92 [4, 2, 1, 4]
93 [4, 2, 3, 1]
94 [4, 2, 3, 2]
95 [4, 2, 3, 4]
96 [4, 2, 4, 1]
97 [4, 2, 4, 2]
98 [4, 2, 4, 3]
99 [4, 3, 1, 2]
100 [4, 3, 1, 3]
101 [4, 3, 1, 4]
102 [4, 3, 2, 1]
103 [4, 3, 2, 3]
104 [4, 3, 2, 4]
105 [4, 3, 4, 1]
106 [4, 3, 4, 2]
107 [4, 3, 4, 3]

该函数是一个生成器,它允许您按顺序获取排列,而不必将它们全部存储在一个列表中(这很容易变得太大并且需要很长时间才能生成)。

如果您想像处理列表一样操作排列,但不生成实际列表,您可以创建一个函数,在“虚拟”列表中为您提供第 N 个排列:

def permCount(A,k): return len(A)*(len(A)-1)**(k-1)
def permAtIndex(A,k,i):
    p = [len(A)]                 # positions of elements
    c = (len(A)-1)**(k-1)        # chunk of sub-permutations
    for _ in range(k):           # for required size
        j,i = divmod(i,c)        # j is index at position
        c //= len(A)-1           # next chunk size
        p.append(j+(j>=p[-1]))   # adjust index for non-repeat
    return [A[j] for j in p[1:]] # build permutation from indexes

这将为您提供排列“列表”的大小,并允许您在给定索引处获得排列(如果需要,您可以随机选择):

permCount([1,2,3,4],4)       # 108
permAtIndex([1,2,3,4],4,84)  # [4, 1, 3, 1]

permCount(range(15),10)                # 309915701760
permAtIndex(range(15),10,123456789101) # [5, 14, 9, 2, 5, 10, 7, 3, 0, 10]

[EDIT]直接返回一个列表:

def perm(A,k,p=[]):
    if not k:                          # size reached
        return [p]               
    result = []
    for n in A:                        # combine every item
        if p and n == p[-1]: continue  # except the repeated one
        result += perm(A,k-1,p+[n])
    return result

【讨论】:

谢谢!我实际上只需要获取列表中的所有排列:) 有没有办法使用列表代替生成器? 是的(尽管您可以使用生成器来创建列表)。我将添加一个示例。

以上是关于大小为 k 的递归排列,彼此之间没有相同的元素的主要内容,如果未能解决你的问题,请参考以下文章

c++ - 按特定顺序排列向量元素

非递归全排列 python实现

数据--第21课-递归课后练习

LeetCode刷题(130)~前 K 个高频元素最小堆|快速排列 难点!

如何在彼此下方对齐多个元素?

有重复元素的排列问题