对具有重复项的排列进行排名和排名

时间:2011-07-30 17:13:03

标签: c++ algorithm permutation

我正在阅读关于排列的内容,我对排名/取消排名方法感兴趣。

摘自论文摘要:

  

n个符号的排列的排名函数分配唯一   [0,n!范围内的整数 - 1]每个n!排列。相应的   unranking函数是反向的:给定0到n之间的整数! - 1,   函数的值是具有该等级的排列。

我使用next_permutation在C ++中进行了排名和无排名功能。但是这对于n> 8是不实际的。我正在寻找一种更快的方法,factoradics似乎很受欢迎。 但我不确定这是否适用于重复。那么用重复排序/取消排列排序的好方法是什么呢?

4 个答案:

答案 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;
  }
}