number to unique permutation mapping of a sequence containing duplicates

后端 未结 5 1126
野趣味
野趣味 2021-02-02 00:26

I am looking for an algorithm that can map a number to a unique permutation of a sequence. I have found out about Lehmer codes and the factorial number system thanks to a simila

5条回答
  •  时光说笑
    2021-02-02 01:10

    From Permutation to Number:

    Let K be the number of character classes (example: AAABBC has three character classes)

    Let N[K] be the number of elements in each character class. (example: for AAABBC, we have N[K]=[3,2,1], and let N= sum(N[K])

    Every legal permutation of the sequence then uniquely corresponds to a path in an incomplete K-way tree.

    The unique number of the permutation then corresponds to the index of the tree-node in a post-order traversal of the K-ary tree terminal nodes.

    Luckily, we don't actually have to perform the tree traversal -- we just need to know how many terminal nodes in the tree are lexicographically less than our node. This is very easy to compute, as at any node in the tree, the number terminal nodes below the current node is equal to the number of permutations using the unused elements in the sequence, which has a closed form solution that is a simple multiplication of factorials.

    So given our 6 original letters, and the first element of our permutation is a 'B', we determine that there will be 5!/3!1!1! = 20 elements that started with 'A', so our permutation number has to be greater than 20. Had our first letter been a 'C', we could have calculated it as 5!/2!2!1! (not A) + 5!/3!1!1! (not B) = 30+ 20, or alternatively as 60 (total) - 5!/3!2!0! (C) = 50

    Using this, we can take a permutation (e.g. 'BAABCA') and perform the following computations: Permuation #= (5!/2!2!1!) ('B') + 0('A') + 0('A')+ 3!/1!1!1! ('B') + 2!/1!

    = 30 + 3 +2 = 35

    Checking that this works: CBBAAA corresponds to

    (5!/2!2!1! (not A) + 5!/3!1!1! (not B)) 'C'+ 4!/2!2!0! (not A) 'B' + 3!/2!1!0! (not A) 'B' = (30 + 20) +6 + 3 = 59

    Likewise, AAABBC = 0 ('A') + 0 'A' + '0' A' + 0 'B' + 0 'B' + 0 'C = 0

    Sample implementation:

    import math
    import copy
    from operator import mul
    
    def computePermutationNumber(inPerm, inCharClasses):
        permutation=copy.copy(inPerm)
        charClasses=copy.copy(inCharClasses)
    
        n=len(permutation)
        permNumber=0
        for i,x in enumerate(permutation):
            for j in xrange(x):
                if( charClasses[j]>0):
                    charClasses[j]-=1
                    permNumber+=multiFactorial(n-i-1, charClasses)
                    charClasses[j]+=1
            if charClasses[x]>0:
                charClasses[x]-=1
        return permNumber
    
    def multiFactorial(n, charClasses):
        val= math.factorial(n)/ reduce(mul, (map(lambda x: math.factorial(x), charClasses)))
        return val
    

    From Number to Permutation: This process can be done in reverse, though I'm not sure how efficiently: Given a permutation number, and the alphabet that it was generated from, recursively subtract the largest number of nodes less than or equal to the remaining permutation number.

    E.g. Given a permutation number of 59, we first can subtract 30 + 20 = 50 ('C') leaving 9. Then we can subtract 'B' (6) and a second 'B'(3), re-generating our original permutation.

提交回复
热议问题