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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With