1

I'm currently working on a problem that asks me to find the millionth lexicographic permutation of 0,1,2,3,4,5,6,7,8,9. I thought of a very crude solution at first glance that had a complexity of around O(n^3)

    public static String permute(char[] a){
        ArrayList<String> array = new ArrayList<String>(); 
        int counter = 1; 
        for (int i = 0; i < a.length; i++){
            array[counter] += a[i];
            for (int j = 0; j < i; j++){
            array[counter] += a[j];
                for(int k = a.length; k > i; k--){
                array[counter] += a[k];}counter++;}
        }
    }

The code may not be perfect but the idea is that a single digit is selected and then moves to the end of an array. The second array creates the numbers behind the selected digit and the third array creates numbers after it. This seems like a terrible algorithm and i remembered a past algorithm that's like this.

    public static HashSet<String> Permute(String toPermute) {
        HashSet<String> set = new HashSet<String>();
        if (toPermute.length() <= 1 )
            set.add(toPermute);
        else {
            for (int i = 0; i < toPermute.length(); i++ )
                for (String s: Permute(toPermute.substring(0,i)+ toPermute.substring(i+1)))
                {
                    set.add(toPermute.substring(i,i+1)+s);}
        }
        return set;
    }

}

The problem is that this algorithm uses unordered sets and I have no idea about how it can become ordered enough for me to find the millionth permutation. I also do not know the complexity other than the fact it could be O(n^2) because it calls itself n times and then unstacks.

cleong
  • 220
  • 1
  • 9
  • http://stackoverflow.com/questions/7918806/finding-n-th-permutation-without-computing-others – Xipan Xiao Mar 06 '13 at 20:19
  • Thanks it's useful information but i'm trying to learn about Hash Sets and recursion complexity through application. – cleong Mar 06 '13 at 20:24

3 Answers3

0

It seems to me (1) which permutation is the millionth depends absolutely on the order you use, and (2) permutations of this sort are ripe problems for recursion. I would write this as a recursive program and increment the count for each iteration. [was that your question? I didn't really see a question...]

arcy
  • 12,845
  • 12
  • 58
  • 103
  • I was wondering how a hashset algorithm can be applied to solving this permutation (0123456789). An insight to the complexity of that algorithm and recursion on a larger scale would be nice too. A full answer would be a treat and it would show me the differences of the two algorithms in the context of the problem. – cleong Mar 06 '13 at 20:27
0

A couple of things in general about your code above:

  1. You should implement to interfaces and not concrete classes I.e. List<String> array = .... Similarly with your Set.
  2. Array's start at index 0, you are starting your counter at index 1.

Finally to answer your question there is a brute force way and a more elegant way that uses some principles in math. Have a look at this site which explains the approaches.

ramsinb
  • 1,985
  • 12
  • 17
  • Thanks I was actually working on that same question but it's more about the journey then the goal right? Thanks for the input i only started it at 1 because it's the first permutation not the zeroth. – cleong Mar 06 '13 at 20:28
  • What's your question though? If its how to order then don't use a string and instead use a integer in your set. Change your implementation from HashSet to TreeSet which is ordered. It's going to be slow though. – ramsinb Mar 06 '13 at 20:32
  • Alright thanks alot! I'll look into the TreeSet and compare it to the quick algorithm provided by project euler. – cleong Mar 06 '13 at 20:35
0

Here is a solution that is more efficient:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class P24 {

    static final int digits[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    static List<Integer> remainedDigits = new ArrayList(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
    static final int factorials[] = new int[digits.length + 1];
    static final int N = 1000_000;

    static int low = -1;
    static int lowIndex = -1;
    static int highIndex = -1;

    public static void main(String args[]) {
        populateFactorials(digits.length);
        validateN(N);
        identifyMargins();
        int n = N;  // it will be changed

        int fixedDigits = digits.length - highIndex;

        String result = "";
        for (int i = 0; i < fixedDigits; i++) {
            result += remainedDigits.get(0);
            remainedDigits.remove(0);
        }

        for (int i = fixedDigits; i < digits.length; i++) {
            int pos = 0;
            int firstDigit = remainedDigits.get(pos);
            low = factorials[lowIndex];
            while (n - low > 0) {
                pos++;
                n -= low;
            }
            lowIndex--;
            result += remainedDigits.get(pos);
            remainedDigits.remove(pos);
        }

        System.out.println(result);
    }

    private static void validateN(int n) {
        if (n < 0 || n > factorials[factorials.length - 1]) {
            System.out.println("The input number is not valid");
            System.exit(0);
        }
    }

    private static void identifyMargins() {
        for (int i = 0; i < factorials.length - 1; i++) {
            if (factorials[i] <= N && N < factorials[i + 1]) {
                lowIndex = i;
                highIndex = i + 1;
            }
        }
    }

    private static void populateFactorials(int max) {
        for (int i = 0; i <= max; i++) {
            factorials[i] = fact(i);
        }
    }

    private static int fact(int x) {
        if (x == 0 || x == 1) {
            return 1;
        }
        int p = 1;
        for (int i = 2; i <= x; i++) {
            p *= i;
        }
        return p;
    }

}

Time: 305 microseconds.

Explanation:

  1. Because the total number of permutations for {a1, ..., an} is n!, I decided that I need a factorials array. I stored in it: {0!, ..., 10!}.
  2. I identified where is the number placed in this sequence, and for our case (N = 1000000) it is between 9! and 10!. If it was lower than 9! I add a padding of fixedDigits digits taken from the remainedDigits array.
  3. Because the number is bigger than 9!, I count how many times I can extract 9! from the number and the result helps me to obtain the first digit. Then, I have a similar approach for 8!, 7!, etc.

The above explanation is based on the following simple observation. If we have a set {a1,...,ai,...,an} and we fix a1, ..., ai, we can obtain (n-i)! different strings.


Notice that if you use:

static List<Integer> remainedDigits = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

you cannot remove elements from the list. `

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199