我希望通过以下条件重新排列(或置换)数组元素:
每个元素的位置不应超过
k
位置
然后我想有效地重复所有这样的重排。
生成重排的最快方式是什么?
我知道复杂性会低于O((2k+1)^n)
,我正在寻找这种算法的最有效实现。
我正在寻找一种可以击败天真算法的解决方案,该算法考虑每个排列并检查条件。
答案 0 :(得分:1)
这是一个时间O(n P)算法,其中P是排列的数量。假设你要在每个排列上运行一些线性时间或更差的东西,这是渐近最优的(尽管Knuth可能会对此有所说明)。
用于生成所有排列的惯用递归算法就是这样。
def swap(array, i, j):
array[i], array[j] = array[j], array[i]
def perms(array, start=0):
if start >= len(array):
print(array)
else:
for i in range(start, len(array)):
swap(array, start, i)
perms(array, start + 1)
swap(array, start, i)
我要做的修改背后的想法是有效地修剪不产生输出的子树。假设每次递归调用的工作是O(1),剩余的叶子为其他调用付费。
首先我们进行修改以跟踪逆置换。
def swap(array, indices, inverse, i, j):
array[i], array[j] = array[j], array[i]
indices[i], indices[j] = indices[j], indices[i]
inverse[indices[i]] = i
inverse[indices[j]] = j
def perms(array, indices, inverse, start=0):
if start >= len(array):
print(array)
else:
for i in range(start, len(array)):
swap(array, indices, inverse, start, i)
perms(array, indices, inverse, start + 1)
swap(array, indices, inverse, start, i)
现在我们使用逆修剪。关键不变量是符合条件start
的元素位于start
和以下k个位置。符合条件后,元素在选定之前或start
变得过大之前仍然符合条件。反过来帮助我们避免后一种情况。
def perms(array, indices, inverse, k, start=0):
if start >= len(array):
print(array)
elif start - k >= 0 and inverse[start - k] >= start:
i = inverse[start - k]
swap(array, indices, inverse, start, i)
perms(array, indices, inverse, k, start + 1)
swap(array, indices, inverse, start, i)
else:
for i in range(start, min(start + k + 1, len(array))):
swap(array, indices, inverse, start, i)
perms(array, indices, inverse, k, start + 1)
swap(array, indices, inverse, start, i)
你应该能够做到
n = 8 # for example
k = 3 # for example
array = list(range(n))
indices = list(range(n))
inverse = list(range(n))
perms(array, indices, inverse, k)
并查看结果排列。