Search code examples
arraysalgorithmcombinationspermutationranking

permutation ranking with DI sequence


I want to rank and unrank through a subset of permutations given by length. The subset is definded as follows:

Example for permutation length 4:

We have the Input the Bitstring length 3 (always permutation length - 1)

010

0 means 2 consecutive elements are Increasing.

1 means 2 consecutive elements are Decreasing.

For this Bitstring exist the subset with following permutations: 1324,1423,2314,2413,3412

The bitstring defined subset of permutations i want to rank and unrank? Is there an algotrithmic way for a given bitstring to do this?


Solution

  • Let me restate the problem that I think you mean.

    You have a bit string of length n-1. If its digits are a pattern of increase/decrease, that describes a set of permutations that fit the pattern. That set can be put into ascending order.

    You want to be able to solve two problems.

    1. Given a permutation that fits the pattern, say where it is in that order (ie "rank" it)
    2. Given a number, produce the permutation that is at that place in the order (ie "unrank" it)

    And ideally you'd like to be able to solve these without having to generate all of the permutations that fit the pattern.

    The key to both is the following function:

    def count_matching (bitstring, start):
        ''' Returns how many permutations of 1..(len(bitstring) + 1)
        ''' match bitstring with starting value start
        # some implementation here.
    

    This can be calculated recursively fairly easily. However doing it the naive way generates all permutations. But if we add a caching layer to memoize it, then we store a polynomial amount of data and make a polynomial number of calls to fill it in.

    Here is the data you get once it is cached for your example:

    {
        ('010', 1): 2,
        ('010', 2): 2,
        ('010', 3): 1,
        ('010', 4): 0,
        ('10', 1): 0,
        ('10', 2): 1,
        ('10', 3): 1,
        ('0', 1): 1,
        ('0', 2): 0,
        ('', 1): 1
    }
    

    Now this seems like a lot of data for a small number of patterns. But for a permutation of length n the number of entries grows like O(n^2) and the number of calls to populate it grows like O(n^3). (Any eagle eyed readers may figure out how to populate it in time O(n^2). I'm going with the simple version.)


    With this in hand, we can take a rank and figure out which permutation it must be with the following idea.

    Suppose that we want to find the rank 4 permutation. Our starting list of numbers is (1 2 3 4). We can skip over 0 permutations which start with ('010', 1) and the answer will be the second of the 2 with ('010', 2).

    Take the second number 2 and our partial permutation is [2, and we have the numbers (1 3 4). We are looking for the 2nd for bitstring '10'. We skip over the 0 permutations which start ('10', 1), the 1 with ('10', 2) and want the first of the 1 with ('10', 3).

    Take the third number 4 and our partial permutation is [2, 4, and we have the numbers (1 3). As before we find that we want the first of the 1 with ('0', 1).

    Take the first number 1 and our partial permutation is [2, 4, 1 and we have the numbers (3). There aren't a lot of choices.

    So we finish and get [2, 4, 1, 3]. Which you can verify is the 4th.

    And so we finish with [2, 4, 3, 1].


    We can also go the other way. Taking the same permutation, we start with [2, 4, 3, 1] and want its rank.

    How many are before it that differ in the first digit? It used the 2nd possible first number. From the entry for ('010', 1) we know there are 2. And the numbers left are 1 3 4.

    How many are before it that differ in the second digit? It uses the 3rd possible second number. From the entries for ('10', 1) and ('10', 2) we know there is 1 more in front of it.

    We now have the numbers 1 3 left. None came before it in the third digit. And again, none in the last.

    With 3 before it, it must have rank 4.


    And there you have it. For memoizing one recursive function, you now make finding permutations by rank, or ranking a given permutation straightforward.