我正在阅读关于排列的内容,我对排名/取消排名方法感兴趣。
摘自论文摘要:
n个符号的排列的排名函数分配唯一 [0,n!范围内的整数 - 1]每个n!排列。相应的 unranking函数是反向的:给定0到n之间的整数! - 1, 函数的值是具有该等级的排列。
我使用next_permutation在C ++中进行了排名和无排名功能。但是这对于n> 8是不实际的。我正在寻找一种更快的方法,factoradics似乎很受欢迎。 但我不确定这是否适用于重复。那么用重复排序/取消排列排序的好方法是什么呢?
答案 0 :(得分:2)
对于大型n-s,您需要任意精度库,如GMP。
这是我上一篇用python编写的无人值守函数的帖子,我认为它是可读的,几乎就像一个伪代码,评论中也有一些解释:Given a list of elements in lexicographical order (i.e. ['a', 'b', 'c', 'd']), find the nth permutation - Average time to solve?
基于此你应该能够找出排名函数,它基本上是相同的逻辑;)
答案 1 :(得分:1)
一种方法是通过特定数量的相同组来排列和取消对指数的选择,例如,
def choose(n, k):
c = 1
for f in xrange(1, k + 1):
c = (c * (n - f + 1)) // f
return c
def rank_choice(S):
k = len(S)
r = 0
j = k - 1
for n in S:
for i in xrange(j, n):
r += choose(i, j)
j -= 1
return r
def unrank_choice(k, r):
S = []
for j in xrange(k - 1, -1, -1):
n = j
while r >= choose(n, j):
r -= choose(n, j)
n += 1
S.append(n)
return S
def rank_perm(P):
P = list(P)
r = 0
for n in xrange(max(P), -1, -1):
S = []
for i, p in enumerate(P):
if p == n:
S.append(i)
S.reverse()
for i in S:
del P[i]
r *= choose(len(P) + len(S), len(S))
r += rank_choice(S)
return r
def unrank_perm(M, r):
P = []
for n, m in enumerate(M):
S = unrank_choice(m, r % choose(len(P) + m, m))
r //= choose(len(P) + m, m)
S.reverse()
for i in S:
P.insert(i, n)
return tuple(P)
if __name__ == '__main__':
for i in xrange(60):
print rank_perm(unrank_perm([2, 3, 1], i))
答案 2 :(得分:1)
我将在这个答案中涵盖你的问题的一半 - “没有人”。目标是有效地找到有序字符串[abcd ...]的按字典顺序排列的“K”排列。
我们需要了解因子数字系统(factoradics)。阶乘数系统使用阶乘值而不是数字的幂(二进制系统使用2的幂,十进制使用10的幂)来表示地点值(或基数)。
地点值(基数)是 -
5!= 120 4!= 24 3!=6 2!= 2 1!=1 0!=1 etc..
第0位的数字始终为0.第一位的数字(基数= 1!)可以是0或1.第二位的数字(基数为2!)可以是0,1或2等等。一般来说,第n位的数字可以取0到n之间的任何值。
前几个数字表示为factoradics -
0 -> 0 = 0*0!
1 -> 10 = 1*1! + 0*0!
2 -> 100 = 1*2! + 0*1! + 0*0!
3 -> 110 = 1*2! + 1*1! + 0*0!
4 -> 200 = 2*2! + 0*1! + 0*0!
5 -> 210 = 2*2! + 1*1! + 0*0!
6 -> 1000 = 1*3! + 0*2! + 0*1! + 0*0!
7 -> 1010 = 1*3! + 0*2! + 1*1! + 0*0!
8 -> 1100 = 1*3! + 1*2! + 0*1! + 0*0!
9 -> 1110
10-> 1200
字符串的第n个词典排列与其事实代表之间存在直接关系。
例如,以下是字符串“abcd”的排列。
0 abcd 6 bacd 12 cabd 18 dabc
1 abdc 7 badc 13 cadb 19 dacb
2 acbd 8 bcad 14 cbad 20 dbac
3 acdb 9 bcda 15 cbda 21 dbca
4 adbc 10 bdac 16 cdab 22 dcab
5 adcb 11 bdca 17 cdba 23 dcba
如果仔细观察,我们可以看到这里的模式。第6个(3!)排列后第一个字母发生变化。第二个字母在2(2!)排列后改变。第三个字母在每次(1!)排列后改变,第四个字母在每次(0!)排列后改变。我们可以使用这种关系直接找到第n个排列。
一旦我们在实际上表示n,我们会考虑其中的每个数字,并将给定字符串中的字符添加到输出中。如果我们需要找到'abcd'的第14个排列。 14 in factoradics - > 2100。
从第一个数字开始 - > 2,String是'abcd'。假设索引从0开始,从字符串中取出位置2处的元素并将其添加到输出。
Output String
c abd
2 012
下一个数字 - > 1.String现在是'abd'。再次,在位置1处拔出字符并将其添加到输出。
Output String
cb ad
21 01
下一个数字 - >字符串是'ad'。将位置1的字符添加到输出。
Output String
cba d
210 0
下一个数字 - >字符串是'd'。将位置0处的字符添加到输出。
输出字符串 cbad'' 2100
要将给定数字转换为阶乘数字系统,请将数字依次除以1,2,3,4,5,依此类推,直到商数为零。每个步骤的提醒形成了事实代表。
例如,要将349转换为factoradic,
Quotient Reminder Factorial Representation
349/1 349 0 0
349/2 174 1 10
174/3 58 0 010
58/4 14 2 2010
14/5 2 4 42010
2/6 0 2 242010
349的Factoradic表示是242010。
答案 3 :(得分:0)
Java,来自https://github.com/timtiemens/permute/blob/master/src/main/java/permute/PermuteUtil.java(我的公共域代码,减去错误检查):
public class PermuteUtil {
public <T> List<T> nthPermutation(List<T> original, final BigInteger permutationNumber) {
final int size = original.size();
// the return list:
List<T> ret = new ArrayList<>();
// local mutable copy of the original list:
List<T> numbers = new ArrayList<>(original);
// Our input permutationNumber is [1,N!], but array indexes are [0,N!-1], so subtract one:
BigInteger permNum = permutationNumber.subtract(BigInteger.ONE);
for (int i = 1; i <= size; i++) {
BigInteger factorialNminusI = factorial(size - i);
// casting to integer is ok here, because even though permNum _could_ be big,
// the factorialNminusI is _always_ big
int j = permNum.divide(factorialNminusI).intValue();
permNum = permNum.mod(factorialNminusI);
// remove item at index j, and put it in the return list at the end
T item = numbers.remove(j);
ret.add(item);
}
return ret;
}
}