我希望有一个函数get_permutation
,在给定列表l
和索引i
的情况下,返回l
的排列,使得排列是唯一的所有i
大于0
且低于n!
(n = len(l)
)。
即。所有get_permutation(l,i) != get_permutation(l,j)
,i!=j
s.t. i
j
0 <= i and j < len(l)!
O(n)
)。
此外,此功能必须在def get_permutation(l, i):
return list(itertools.permutations(l))[i]
中运行。
例如,如果指数顺序不符合要求,此函数将符合要求:
{{1}}
有没有人能解决上述问题?
编辑:我希望索引中的排列 而不是排列中的索引
答案 0 :(得分:6)
如果你不关心哪些排列得到哪些索引,如果我们认为任意整数的算术运算是O(1),那么O(n)解就成为可能。
例如,请参阅论文&#34; Ranking and unranking permutations in linear time&#34;作者:Wendy Myrvold和Frank Ruskey。
简而言之,有两个想法。
(1)考虑Fisher-Yates shuffle方法生成随机排列(下面的伪代码):
p = [0, 1, ..., n-1]
for i := 0 upto n-1:
j := random_integer (0, i)
exchange p[i] and p[j]
这种变换是单射的:如果我们给它一个不同的随机整数序列,它可以保证产生不同的排列。因此,我们用非随机整数替换随机整数:第一个是0,第二个是0或1,...,最后一个可以是从0到n-1的任何整数。
(2)有n!秩序的排列n。我们现在要做的是在factorial number system中写一个从0到n!-1的整数:最后一个数字总是0,前一个数字是0或1,......,并且有n个可能性来自第一个数字为0到n-1。因此,我们将获得一个独特的序列来为上面的伪代码提供。
现在,如果我们考虑将数字从1到n的整数除以O(1)运算,将数字转换为阶乘系统就是O(n)这样的除法。严格来说,这不是真的:对于大n,数字n!包含O(n log n)二进制数字的顺序,并且该除法的成本与数字位数成比例。
实际上,对于小n,O(n ^ 2)或O(n log n)方法来排列或取消排列,以及需要存储O(2 ^ n)或O(n!)内存的方法一些预先计算的值可能比涉及整数除法的O(n)方法更快,这在现代处理器上是相对慢的操作。 对于n足够大,使n!不适合机器字,&#34; O(n)如果订单-n!整数运算是O(1)&#34;论证停止工作。所以,如果你不坚持它理论上是O(n),那么对于小n和大n你可能会更好。
答案 1 :(得分:1)
更新:Finding n-th permutation without computing others的可能欺骗,请查看算法。
如果len(l)
很小,您可以预先计算perm_index = permutations(range(len(l)))
并将其用作实际数据中的索引列表列表。
此外,如果您有range(len(l))
的排列列表,并且range(len(l) - 1)
需要一个排列,则可以执行以下操作:
[x - 1 for x in perm_index[i][1:]]
利用了排列在生成时按排序顺序排列的事实。
答案 2 :(得分:1)
此解决方案适用于O(1)
(运行时复杂性;字典查找的摊销成本):
#!/usr/bin/env python
import itertools
def get_permutation():
memoize = {}
def _memoizer(l, i):
if str(l) in memoize and i not in memoize[str(l)]:
memoize[str(l)][i] = memoize[str(l)]['permutations'].next()
else:
p = itertools.permutations(l)
memoize[str(l)] = {'permutations': p}
memoize[str(l)][i] = memoize[str(l)]['permutations'].next()
return memoize[str(l)][i]
return _memoizer
if __name__ == '__main__':
get_permutation = get_permutation()
l1 = list(range(10))
l2 = list(range(5))
print(get_permutation(l1, 1))
print(get_permutation(l1, 20))
print(get_permutation(l2, 3))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
(0, 1, 2, 3, 4, 5, 6, 7, 9, 8)
(0, 1, 2, 3, 4)
代码将所有过去的调用存储在字典中。它还存储排列对象。因此,如果请求新的排列,则使用下一个排列。
答案 3 :(得分:0)
基于http://www.2ality.com/2013/03/permutations.html,这是一个可能的解决方案。正如@Gassa指出的那样,elements.pop按顺序不是常数,因此解决方案在列表长度上不是线性的。因此,我不会将此标记为已接受的答案。但是,它完成了这项工作。
def integerToCode(idx, permSize):
if (permSize <= 1):
return [0]
multiplier = math.factorial(permSize-1)
digit =idx / multiplier
return [digit] + integerToCode(idx % multiplier, permSize-1)
def codeToPermutation(elements, code):
return map(lambda i: elements.pop(i), code)
def get_permutation(l, i):
c = integerToCode(i, len(l))
return codeToPermutation(list(l), c)
答案 4 :(得分:0)
有点太晚了...... C#代码可以为您提供您期望的结果:
INSTALLED_APPS