1

I want to generate a permutation of an array a and I don't want to use utility functions such as java.util.Collections().
The permutations should be randomized and every permutation should be possible to occur - but there is no need for an equally distributed probability.

The following code achieves this - but at poor performance :

// array holding 1,...,n
// initialized somewhere else
int[] a = new int[N];

for (int i = 0; i < a.length ; i++) {
     int r = (int) (Math.random() * (i+1));     
     swap(r,i,a);
  }

private static void swap(int j, int k, int[] array){
      int temp = array[k];
      array[k] = array[j];
      array[j] = temp;
}

Question:
Is there any chance of reducing the total number of random numbers used for the generation of a permutation?

Gnark
  • 4,080
  • 7
  • 33
  • 44

3 Answers3

4

I'll be surprised if anyone improves on the Knuth shuffle. It's O(n).

It takes O(n) random numbers, which is not sufficient for me.

This citation claims an O(n log n) algorithm.

We'd all love to see see O(log n) or O(1).

O(log n) algorithms usually depend on "divide and conquer" bisection, which brings to mind cutting the deck and dividing each half.

But I can't help but think that if a faster algorithm were accessible Knuth would have found it.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • thanks for that interessting link... this would also explain why i couldnt manage to find any faster solution! :-P – Gnark Nov 14 '09 at 15:51
2

A sequence of length n has n! permutations. If each permuation needs to be possible, there must be a possible sequence of random numbers for each one.

To randomly permute an array of length n, you can therefore generate a single random number from the range 1..n! uniformly at random. This identifies a single permutation, which you can then apply.

You might improve your question to ask how many random bits are needed. By the same argument, that will be log(n!). To give you an idea about the asymptotic behaviour of that function, consider:

Let n > 3:

n = log(2^n) < log(n!) < log(n^n) = n * log(n)

So n random bits can not be enough (for n > 3). In fact, one can prove that log(n!) is not in O(n).

meriton
  • 68,356
  • 14
  • 108
  • 175
  • But if you read the Knuth citation, you'll see that it is indeed O(n). – duffymo Nov 14 '09 at 18:44
  • 1
    No. That algorithm states that you can find a random permutation with O(n) random real numbers from the range [0,1], where the number are represented with sufficient accuracy to select among n different array indices. This can not be accomplished with a fixed-bit random numbers. It is easy to see that at least the first random number will require log n bits, leading to a bit-complexity of O(n log n). – meriton Nov 14 '09 at 20:06
1

The only possible optimization I can think of is making the random number generator faster. An easy solution is to generate random ints in the first place:

import java.util.Random;
Random rand = new Random();

for (int i = 0; i < a.length ; i++) {
    swap(rand.nextInt(i+1), i, a);
}

...

Alternatively, you can invent a faster way to generate more or less random numbers (uniformly distributed or not, suited to your needs). However, there's no way you can overcome the O(n) limitation.

Eemeli Kantola
  • 5,437
  • 6
  • 35
  • 43