创建排列的算法,包括遗漏
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 中实现的按字典顺序排列的生成器。
我正在尝试找到一种算法,允许我创建所有 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 中实现的按字典顺序排列的生成器。