2

Anyone know of a fast/the fastest way to generate a random permutation of a list of integers in Java. For example if I want a random permutation of length five an answer would be 1 5 4 2 3, where each of the 5! possibilities is equally likely.

My thoughts on how to tackle this are to run a method which generates random real numbers in an array of desired length and then sorts them returning the index i.e. 0.712 0.314 0.42 0.69 0.1 would return a permutation of 5 2 3 4 1. I think this is possible to run in O(n^2) and at the moment my code is running in approximately O(n^3) and is a large proportion of the running time of my program at the moment. Theoretically this seems OK but I'm not sure about it in practice.

user528676
  • 197
  • 1
  • 3
  • 12

4 Answers4

9

Have you tried the following?

Collections.shuffle(list)

This iterates through each element, swapping that element with a random remaining element. This has a O(n) time complexity.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Is each result equally likely? I wasn't sure whether it had inherent bias in it? – user528676 Aug 04 '11 at 18:44
  • Why do you suspect this if it behaves as I suggested and you suggested? What is your doubt? – Peter Lawrey Aug 04 '11 at 19:01
  • 3
    From [Java API](http://download.oracle.com/javase/6/docs/api/) _"Randomly permutes the specified list using a default source of randomness. All permutations occur with approximately equal likelihood. The hedge "approximately" is used in the foregoing description because default source of randomness is only approximately an unbiased source of independently chosen bits. If it were a perfect source of randomly chosen bits, then the algorithm would choose permutations with perfect uniformity."_ If you use `Random`, your own implementation is **in best case** as good as `Collections.shuffle(List)` – pmnt Aug 04 '11 at 19:30
  • I wasn't sure as I'm not that familiar with it. Thanks for the tip anyway. – user528676 Aug 04 '11 at 19:40
  • 1
    Your statements regarding the time complexity of the [Collections shuffle](http://download.oracle.com/javase/6/docs/api/java/util/Collections.html#shuffle%28java.util.List%29) method are inaccurate. It is linear time. – Atreys Aug 04 '11 at 19:49
  • @Atreys, Thank you for correcting me. I knew that as well. doh. :( – Peter Lawrey Aug 04 '11 at 20:09
7

If the purpose is just to generate a random permutation, I don't really understand the need for sorting. The following code runs in linear time as far as I can tell

public static int[] getRandomPermutation (int length){

    // initialize array and fill it with {0,1,2...}
    int[] array = new int[length];
    for(int i = 0; i < array.length; i++)
        array[i] = i;

    for(int i = 0; i < length; i++){

        // randomly chosen position in array whose element
        // will be swapped with the element in position i
        // note that when i = 0, any position can chosen (0 thru length-1)
        // when i = 1, only positions 1 through length -1
                    // NOTE: r is an instance of java.util.Random
        int ran = i + r.nextInt (length-i);

        // perform swap
        int temp = array[i];
        array[i] = array[ran];
        array[ran] = temp;
    }                       
    return array;
}

And here is some code to test it:

public static void testGetRandomPermutation () {

    int length =4;  // length of arrays to construct

    // This code tests the DISTRIBUTIONAL PROPERTIES
    ArrayList<Integer> counts = new ArrayList <Integer> ();  // filled with Integer
    ArrayList<int[]> arrays = new ArrayList <int[]> ();  // filled with int[]

    int T = 1000000; // number of trials
    for (int t = 0; t < T; t++) {           
        int[] perm = getRandomPermutation(length);
        // System.out.println (getString (perm));
        boolean matchFound = false;
        for(int j = 0; j < arrays.size(); j++) {
            if(equals(perm,arrays.get(j))) {
                //System.out.println ("match found!");
                matchFound = true;
                // increment value of count in corresponding position of count list
                counts.set(j, Integer.valueOf(counts.get(j).intValue()+1));
                break;
    }                       
        }
        if (!matchFound) {
            arrays.add(perm);
            counts.add(Integer.valueOf(1));
        }   
    }

    for(int i = 0; i < arrays.size(); i++){
        System.out.println (getString (arrays.get (i)));
        System.out.println ("frequency: " + counts.get (i).intValue ());
    }

    // Now let's test the speed
    T = 500000;  // trials per array length n       
    // n will the the length of the arrays
    double[] times = new double[97];
    for(int n = 3; n < 100; n++){
        long beginTime = System.currentTimeMillis();
        for(int t = 0; t < T; t++){
            int[] perm = getRandomPermutation(n);
        }
        long endTime = System.currentTimeMillis();
        times[n-3] = (double)(endTime-beginTime);
        System.out.println("time to make "+T+" random permutations of length "+n+" : "+ (endTime-beginTime));
    }
    // Plotter.plot(new double[][]{times});     
}
user unknown
  • 35,537
  • 11
  • 75
  • 121
user879331
  • 86
  • 2
2

There is an O(n) Shuffle method that is easy to implement.

Atreys
  • 3,741
  • 1
  • 17
  • 27
1

Just generate random number between 0 and n! - 1 and use
the algorithm I provided elsewhere (to generate permutation by its rank).

Community
  • 1
  • 1
Tomas
  • 57,621
  • 49
  • 238
  • 373