我的问题是:给定长度为n的列表L和整数i,使得0 <= i <1。 n!,你怎么能写一个函数perm(L,n)来在O(n)时间内产生L的第i个排列?我的意思是ith排列只是在某些实现定义的排序中的第i个排列必须具有属性:
对于任何i和任何2个列表A和B,perm(A,i)和perm(B,i)必须将A和B的第j个元素映射到两个A的相同位置的元素和B.
对于任何输入(A,i),(A,j)perm(A,i)== perm(A,j)当且仅当i == j。
注意:这不是作业。事实上,我在2年前解决了这个问题,但我已经完全忘记了这一点,这让我感到害怕。此外,这是我在一个解决方案中做出的破坏尝试:
def perm(s, i):
n = len(s)
perm = [0]*n
itCount = 0
for elem in s:
perm[i%n + itCount] = elem
i = i / n
n -= 1
itCount+=1
return perm
另请注意:O(n)要求非常重要。否则你可以生成n!大小的所有排列列表,只返回其第i个元素。
答案 0 :(得分:5)
def perm(sequence, index):
sequence = list(sequence)
result = []
for x in xrange(len(sequence)):
idx = index % len(sequence)
index /= len(sequence)
result.append( sequence[idx] )
# constant time non-order preserving removal
sequence[idx] = sequence[-1]
del sequence[-1]
return result
基于混洗算法,但我们每次都采用数字中最不重要的部分来决定采用哪个元素而不是随机数。或者将其视为转换为某个任意基数的问题,除了基本名称每增加一个数字缩小。
答案 1 :(得分:3)
你能使用factoradics吗?您可以通过此MSDN article找到插图。
更新:我写了一个extension of the MSDN algorithm,发现我一次只能处理n个事物的排列,即使是n!= r。
答案 2 :(得分:2)
计算简约方法(用C风格的伪代码编写):
function perm(list,i){
for(a=list.length;a;a--){
list.switch(a-1,i mod a);
i=i/a;
}
return list;
}
请注意,依赖于从原始列表中删除元素的实现往往在O(n ^ 2)时间运行,最好是O(n * log(n)),给定一个特殊的树样式列表实现,用于快速插入和删除列表元素。
上面的代码而不是缩小原始列表并保持其顺序只是将元素从末尾移动到空位,仍然在索引和置换之间进行完美的1:1映射,只是稍微加扰一个,但是在纯粹的O(n)时间。
答案 3 :(得分:1)
所以,我想我终于解决了它。在我阅读任何答案之前,我会在这里发布自己的答案。
def perm(L, i):
n = len(L)
if (n == 1):
return L
else:
split = i%n
return [L[split]] + perm(L[:split] + L[split+1:], i/n)
答案 4 :(得分:0)
有n!排列。第一个字符可以通过n种方式从L中选择。每个选择都离开(n-1)!他们之间的排列。所以这个想法足以建立一个订单。一般来说,你会弄清楚你所在的部分,选择适当的元素然后递归/循环在较小的L上。
这种方法正确的论点是通过对序列长度的归纳。 (草图)长度为1时,它是微不足道的。对于n的长度,您使用上述观察将问题分成n个部分,每个部分都有一个带有长度(n-1)的L'的问题。通过感应,所有的L'都被正确构造(并且在线性时间内)。然后很明显我们可以使用IH来构建长度为n的解决方案。