Domanda

I have been researching for days trying to figure out a solution to this problem. I would be happy to pay someone for consulting time to solve this if need be.

I am currently using Python itertools to generate 6 character permutations of a 32 character alphabet. Via the following command:

gen = itertools.permutations('ABCDEFGHJKLMNPQRSTUVWXYZ23456789',6) 

From the documentation, this function produces "r-length tuples, all possible orderings, no repeated elements".

You can use the library to grab a slice of the resulting permutations via the following command (this example grabs the first 10 permutations, 0-10:

gen2 = itertools.islice(gen,0,10)

When iterating over the result gen2, I get exactly what I want:

('A', 'B', 'C', 'D', 'E', 'F')
('A', 'B', 'C', 'D', 'E', 'G')
('A', 'B', 'C', 'D', 'E', 'H')
('A', 'B', 'C', 'D', 'E', 'J')
('A', 'B', 'C', 'D', 'E', 'K')
('A', 'B', 'C', 'D', 'E', 'L')
('A', 'B', 'C', 'D', 'E', 'M')
('A', 'B', 'C', 'D', 'E', 'N')
('A', 'B', 'C', 'D', 'E', 'P')
('A', 'B', 'C', 'D', 'E', 'Q')

This is great, but my real desire is to be able to pick any arbitrary permutation and grab it from the permutation list (without having to store all possible permutation values). If my calculations are correct, when generating 6 character sequences of the alphabet listed above there are 652,458,240 possible combinations. So I would like to be able to do something like grab the 10,353,345th permutation. The problem is that if you use the islice function above to grab this permutation it has to iterate over the entire set of permutations up to 10,353,345th element before returning it to you. As you can imagine, this is very inefficient and takes a long time to return.

My question is, what is the algorithm to achieve the computation desired? I've done quite a bit of research on factorial decomposition and base n conversions, but have been unable to find anything explaining how to achieve something close to what I want or an algorithm that I can modify to achieve this result.

Any help would be greatly appreciated!

È stato utile?

Soluzione

What you are looking is called unrank in combinatorial algorithm. Consider the list of the element of a set S in a fixed order, unrank_S(i) returns the i-th element of the list without computing the list. So your S here is Perm(n, k) : the list of all k-permutation of a set of size n. As you know the size of this set is n!/k!. One way to do that is to use Factoradic numbers

Here is an unrank algorithm in python:

def factorial(n):
    if n == 0: return 1
    return n*factorial(n-1)

def unrank(S, k, i):
    S = list(S)   # make a copy to avoid destroying the list
    n = len(S)
    nb = factorial(n) // factorial(n-k)
    if i >= nb:
        raise IndexError
    res = []
    while k > 0:
        nb = nb // n
        pos = i // nb   # the factoradic digits
        i = i % nb      # the remaining digits
        res.append(S[pos])
        del S[pos]
        k = k-1
        n = n-1
    return res

Then

[unrank(range(5), 2, i) for i in range(20)]
 [[0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 2], [1, 3], [1, 4], [2, 0], [2, 1], [2, 3], [2, 4], [3, 0], [3, 1], [3, 2], [3, 4], [4, 0], [4, 1], [4, 2], [4, 3]]

and

unrank(list('ABCDEFGHJKLMNPQRSTUVWXYZ23456789'),6, 128347238)\
['G', 'L', 'E', 'H', 'T', 'R']

Of course you might want to compute factorial using a better method or even to cache it in a pre-computed array to avoid recomputing it.

Altri suggerimenti

I don't have much time to give you complete solution but following idea can provide some line to think on.

You need to find Nth permutation taking 6 characters at a time.
Lets fix first place character. Then there remains 25 other characters.
Total number of permutations from remaining characters is P = 25C5 * 5!.

So with A as first character, you can have P permutations. If P is less than N, then A can't be at first place.

Now keep B at first place and total numbers of permutations till B at first place are 2*P.

Say you keep Kth character at first place so that total number of permutations till Kth character are K*P, with K*P less than N, and after keeping K+1th character, (K+1)*P, exceeds N. So your required string need to have K+1th character at first place.

So you have to find N-K*P remaining permutations. with remaining 25 characters and 5 places. So same problem is reduced to 1 character less, 1 place less and lesser number of more permutations to find.
So solve in similar way for all places.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top