21

The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, We get the following sequence (ie, for n = 3 ) :

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321" Given n and k, return the kth permutation sequence.

For example, given n = 3, k = 4, ans = "231".

There are multiple solutions out there. But all of them uses either factorial or there complexity is larger than O(n) such as O(n!). If you use factorial and find the number at the position by k/(n-1)!, the problem comes when n is large(n = 100). Here as n is large, (n-1)! overflows and becomes 0. In result, I am getting a divide by zero error...any solution or algorithm for that?

Here is my code:

public class KthPermutation {
    public String getPermutation(int n, int k) {
        // initialize all numbers
        ArrayList<Integer> numberList = new ArrayList<Integer>();

        for (int i = 1; i <= n; i++) {
            numberList.add(i);
        }
        int fact = 1;   // set factorial of n-1

        for (int i = 1; i <= n-1; i++) {
            fact = fact * i;
        }   

        if ((long) k > (long) fact * n) {
            k = (int) ((long) k - (long) (fact * n));
        }
        k--; // set k to base 0

        StringBuilder result = new StringBuilder();
        result = getP(result, numberList, n, k, fact);
        return result.toString();
    }
    public static StringBuilder getP(StringBuilder result,
                ArrayList<Integer> numberList, int n, int k, int fact) {    
        if (numberList.size() == 1 || n == 1) {
            result.append(numberList.get(0));
            return result;  // return condition
        }
        int number = (k / fact) + 1 ;
        result.append(numberList.get(number - 1));
        numberList.remove(number - 1);
        k = k % fact;  // update k
        fact = fact / (n - 1);
        n--;
        return getP(result, numberList, n, k, fact);
    }
}
explorer
  • 737
  • 1
  • 8
  • 23
  • To get around the issue with large numbers, you probably want `BigInteger` or something like that; I'm not sure your code is correct though. Could you explain why you use `fact*n`? And what is base 0? – G. Bach Jul 04 '15 at 02:18
  • @G.Bach to change the k to be an index? code is right though...you can check it for the given example in your ide. .... – explorer Jul 04 '15 at 02:22
  • @G.Bach fact*n represents n!....in this case if k = 7 which is greater than n! = 6 then your k should be 1 (7-6)....think of what happens when k > n! – explorer Jul 04 '15 at 02:24
  • 1
    Oh yeah, you're basically going `k := k mod n!`, I see. Have you tried using BigInteger? On a closer look, your code looks correct to me apart from not checking whether n or k are non-positive, k being bigger than 2*n!, and integer overflow. – G. Bach Jul 04 '15 at 02:27
  • @G.Bach I know about BigInteger though...but there is no need to use BigInteger...cause 1. with that it would be hard to work with other variables and 2. there is possibly a solution/better algorithm exist! that's what i am trying to come up with – explorer Jul 04 '15 at 02:31
  • This question didn't help? (First Q in the "related" list on the right) http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms – rici Jul 04 '15 at 02:38
  • @rici nop, it didn't.. – explorer Jul 04 '15 at 04:49
  • Maybe there's a way to map k to a permutation without actually going through n! entries. It's like how you can add 0-n without going through all n elements. – David Ehrmann Jul 04 '15 at 05:18
  • How do you plan to provide an input to the program? The numbers of permutations span the range of 1 to N!, you will not be able to choose a permutation if you don't allow using numbers big enough to keep the factorial value! – CiaPan Jul 04 '15 at 22:35

4 Answers4

36

So if I'm reading the question correctly, you want to find the kth permutation, preferrably without using BigIntegers, provided k is not large enough to require a BigInteger.

If we look at the sequence

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

We can rewrite it so that the number in each position is an index into a list of the numbers that haven't appeared so far on the line:

0 0 0
0 1 0
1 0 0
1 1 0
2 0 0
2 1 0

So for example "2, 0, 0" means start with the list "1, 2, 3", then take the third (because we are indexing from zero), which is a 3, then take the first of the remaining digits "1, 2" which is a 1, then the first of the remaining digit, which is "2". So it produces "3, 1, 2".

To generate these indices, go from right to left and divide k by 1! for the rightmost two places, then 2! then 3! then 4! etc, and then modulo the result with the number of possible indices in that position, which is 1 for the rightmost, 2 for the second-rightmost etc. You don't have to calculate the factorial each time because you can keep a running product.

You can break out of the loop as soon as k divided by the factorial is zero, so you only have to compute factorials up until roughly the size of k multiplied by the last place in which k divided by the factorial is non-zero. If k is too large, you need to switch to BigIntegers.

Once you have the indices it's pretty straightforward to use them to generate the permutation.

Code (k starts from 0, so to find the first pass 0, not 1):

static public void findPermutation(int n, int k)
{
    int[] numbers = new int[n];
    int[] indices = new int[n];

    // initialise the numbers 1, 2, 3...
    for (int i = 0; i < n; i++)
        numbers[i] = i + 1;

    int divisor = 1;
    for (int place = 1; place <= n; place++)
    {
        if((k / divisor) == 0)
            break;  // all the remaining indices will be zero

        // compute the index at that place:
        indices[n-place] = (k / divisor) % place;
        divisor *= place;
    }

    // print out the indices:
    // System.out.println(Arrays.toString(indices));

    // permute the numbers array according to the indices:
    for (int i = 0; i < n; i++)
    {
        int index = indices[i] + i;

        // take the element at index and place it at i, moving the rest up
        if(index != i)
        {
            int temp = numbers[index];
            for(int j = index; j > i; j--)
               numbers[j] = numbers[j-1];
            numbers[i] = temp;
        }
    }

    // print out the permutation:
    System.out.println(Arrays.toString(numbers));
}

Demo

output:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

10000000th permutation for n = 100:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 92, 98, 96, 90, 91, 100, 94, 97, 95, 99, 93]

samgak
  • 23,944
  • 4
  • 60
  • 82
  • Thanks @samgak....efficient algorithm with best explanation and simple code...Thanks a lot! – explorer Jul 04 '15 at 17:09
  • I am not quite able to wrap my head around one thing. How does indices[n-place] = (k / divisor) % place work correctly?? – Shubham Kadlag May 16 '19 at 03:33
  • @ShubhamKadlag the `divisor`variable contains the factorial (it is initially 1, then 1, then 2 then 6 etc), which is why it is repeatedly multiplied by `place`. Dividing k by the divisor, then taking the modulo gives the index for each position. `place` stores the number of of possible index values in each position, which is why it is used for the modulo. The index is stored in `location[n-place]` (and not `location[n]`) because the algorithm works from right to left. – samgak May 16 '19 at 04:45
  • Right, But how does same K work for all position? I understand why it would work for first digit but not for other digits as k is number of permutation, so shouldn't it change with position as remainder after calculation of first digit?. – Shubham Kadlag May 17 '19 at 11:45
  • Think about a simple case where you are finding the digits of a decimal number e.g. 4836. You can compute rightmost place by dividing 4836 by 1 then modulo 10 to get 6. Then 4836 by 10 modulo 10 to get 3, then 4836 by 100 modulo 10 to get 8 etc. We don't need to change the number 4836 each time. Permutation case is similar but we use factorials instead of powers of 10 and modulus value changes. – samgak May 18 '19 at 00:04
  • 1
    The indices generated are simply the [factoradic](https://en.wikipedia.org/wiki/Factorial_number_system) representation of `k`. – Quirk Jun 20 '20 at 07:35
3

The indices for k'th permutation (used in the answer to this question) are the factoradic representation of k and can be calculated without using factorial or running product.

public static List<Integer> toFactoradic(int x) {
    List<Integer> result = new ArrayList<>();

    for(int i = 1; x > 0; x /= i++) {
        result.add(x % i);
    }

    Collections.reverse(result);
    return result;
}

Of course, the indices array should be padded by 0's from the left so that length of the indices array is equal to number of elements to get the actual indices. Alternatively, the permutation could be applied from the right end.

1

Of course there is a need for bigints with such an interface

when you have n = 100 then you have n! permutations which means k is in the range k=<1,n!>

100!=93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

which does not fit into the standard unsigned int

2^32=          4294967296
2^64=18446744073709551616

see Fast exact bigint factorial

if you change the interface a bit you suddenly do not need any bigints anymore

just change API so it sequentially returns 1st,2nd,3th,...permutation without specifying k so you need something like:

of course this is usable only if your usage of permutation is also sequential. You can also make function previous() to handle algorithms which are almost sequential. For random or non-sequential access you need to use bigints

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
1

First we can generate the factoradic reprsentation of k and then use it generate the necessary permutation. Please see https://en.wikipedia.org/wiki/Factorial_number_system for more details.

public String getPermutation(int n, int k) {
    LinkedList<Integer> factoradic = new LinkedList<>();
    k=k-1; // because factoradic representation and its mapping to permutation starts from 0
    for(int i=1;i<=n; i++){ // get radix digits for n digits
        factoradic.addFirst(k%i);
        k=k/i;
    }
    
    //System.out.println(factoradic.size());
    List<Integer> numbers = new LinkedList<>();
    for(int i=1;i<=n;i++){
        numbers.add(i);
    }
    StringBuilder str = new StringBuilder();
    for(int x: factoradic){
        // System.out.println(x);
        str.append(String.valueOf(numbers.get(x)));
        numbers.remove(x);
    }
    return str.toString();
}