创建排列的算法,包括遗漏

Algorithm to create permutations, including omissions

我正在尝试找到一种算法,允许我创建所有 permutations/combinations 列表元素,包括在每个元素被省略时出现的元素。

例如,一个列表包含

[a, b, c]

应该产生标准排列

[a, b, c], [a, c, b], [b, a, c], [b, c, a], [c, b, a], [c, a, b]

还有那些在省略列表条目时产生的结果

[a, b], [a, c], [b, a], [b, c], [c, a], [c, b]

[a], [b], [c]

有人知道这里的最佳方法吗?不幸的是,Heap 的算法只生成标准排列,我还没有找到任何其他算法可以提供我需要的整个排列范围。

我通过递归调用Heap的算法解决了这个问题,每次递归包含一个循环,每次迭代分别删除一个元素。这会缩小列表并每次递归迭代地删除每个条目一次,然后排列剩余部分。

这里是 Python 中 Heap 算法的简单实现,编写为生成器。这可能不是您习惯的演示文稿,因为它使用了 mid-tested 循环,这是 Robert Sedgewick 最初演示的方式,但它具有完全相同的步骤顺序。 (不幸的是,Python 没有 mid-tested 循环结构,所以这个版本在开始时也有一个无用的测试。)

def heap_perm(it):
    def helper(n):
        if n == 0:
            yield A[:]
        else:
            for i in range(n):
                yield from helper(n-1)
                if i < n - 1:
                    if n % 2:
                        A[0], A[n-1] = A[n-1], A[0]
                    else:
                        A[i], A[n-1] = A[n-1], A[i]

    A = list(it)
    yield from helper(len(A))

此算法生成的排列顺序的一个重要方面是,无论后缀的长度如何,具有特定后缀的所有排列都会连续输出。所以如果我们也想要所有的后缀,我们只需要在我们第一次遇到它时产生后缀,就在递归之前。如果将这两个功能并排放置,您会发现它们有多么相似。

def heap_subset_perm(it):
    def helper(n):
        for i in range(n):
            yield A[n-1:]
            yield from helper(n-1)
            if i < n - 1:
                if n % 2:
                    A[0], A[n-1] = A[n-1], A[0]
                else:
                    A[i], A[n-1] = A[n-1], A[i]

    A = list(it)
    yield from helper(len(A))

在示例 运行 中,我 right-justified 输出以便后缀 属性 更明显:

>>> for p in heap_subset_perm("abcd"):
...     print(f"{str(p):>20}")
... 
               ['d']
          ['c', 'd']
     ['b', 'c', 'd']
['a', 'b', 'c', 'd']
     ['a', 'c', 'd']
['b', 'a', 'c', 'd']
          ['b', 'd']
     ['a', 'b', 'd']
['c', 'a', 'b', 'd']
     ['c', 'b', 'd']
['a', 'c', 'b', 'd']
          ['a', 'd']
     ['c', 'a', 'd']
['b', 'c', 'a', 'd']
     ['b', 'a', 'd']
['c', 'b', 'a', 'd']
               ['c']
          ['a', 'c']
     ['b', 'a', 'c']
['d', 'b', 'a', 'c']
     ['d', 'a', 'c']
['b', 'd', 'a', 'c']
          ['b', 'c']
     ['d', 'b', 'c']
['a', 'd', 'b', 'c']
     ['a', 'b', 'c']
['d', 'a', 'b', 'c']
          ['d', 'c']
     ['a', 'd', 'c']
['b', 'a', 'd', 'c']
     ['b', 'd', 'c']
['a', 'b', 'd', 'c']
               ['b']
          ['d', 'b']
     ['c', 'd', 'b']
['a', 'c', 'd', 'b']
     ['a', 'd', 'b']
['c', 'a', 'd', 'b']
          ['c', 'b']
     ['a', 'c', 'b']
['d', 'a', 'c', 'b']
     ['d', 'c', 'b']
['a', 'd', 'c', 'b']
          ['a', 'b']
     ['d', 'a', 'b']
['c', 'd', 'a', 'b']
     ['c', 'a', 'b']
['d', 'c', 'a', 'b']
               ['a']
          ['b', 'a']
     ['c', 'b', 'a']
['d', 'c', 'b', 'a']
     ['d', 'b', 'a']
['c', 'd', 'b', 'a']
          ['c', 'a']
     ['d', 'c', 'a']
['b', 'd', 'c', 'a']
     ['b', 'c', 'a']
['d', 'b', 'c', 'a']
          ['d', 'a']
     ['b', 'd', 'a']
['c', 'b', 'd', 'a']
     ['c', 'd', 'a']
['b', 'c', 'd', 'a']

您可以使用按前缀顺序生成排列的排列生成器执行相同的操作,例如在 itertools 中实现的按字典顺序排列的生成器。