tags:

views:

204

answers:

1

Hi all -

Here's a C implementation of Fisher-Yates that I want to use in a deck-shuffling routine. Am I doing this correctly (n = length of array)?

Note: The do-while loop attempts to correct for the modulo bias (see here). It adds a bit of overhead to the procedure and could be eliminated if you don't care about the low-bit bias.

void shuffle(int *array, int n) {

  int i, j, tmp, upper_bound;

  srand(time(NULL));

  for (i = n - 1; i > 0; i--) {

    upper_bound = RAND_MAX - ((RAND_MAX % (i + 1)) + 1);

    do {
      j = rand() % (i + 1);
    } while (j > upper_bound);

    tmp = array[j];
    array[j] = array[i];
    array[i] = tmp;   
  }
}

Thanks,

Mike

+4  A: 

First, you should extract the code for generating a random number that's equally distributed between 0 (inclusive) and n (exclusive) to a separate function. That's a nice task of work that you will need elsewhere, too.

Second, I would not call srand inside the shuffle function but depend on the caller on initializing the random number generator. That way you can shuffle a deck more than once in a second.

Third, you should do the test for j > upper_bound before dividing by i + 1. It's unlikely that i will ever be near RAND_MAX.

static int rand_int(int n) {
  int limit = RAND_MAX - RAND_MAX % n;
  int rnd;

  do {
    rnd = rand();
  } while (rnd >= limit);
  return rnd % n;
}

void shuffle(int *array, int n) {
  int i, j, tmp;

  for (i = n - 1; i > 0; i--) {
    j = rand_int(i + 1);
    tmp = array[j];
    array[j] = array[i];
    array[i] = tmp;
  }
}

To check whether this implementation may be correct, you need to ensure that you asked the random number generator for log2(n!) bits of randomness. In other words, the product of all the ns given to the rand_int function must be n!.

Roland Illig
+1 for the comment on seeding. It's also a "security" issue if external users of your code can predict/influence your shuffle by controlling timing.
Darron
What does it mean to have the caller initialize the random number? Is that like depending on the caller's mouse movements or something?
MikeRand
No. What it means is: Any experienced programmer would not expect a routine called `shuffle` to reset the random number generator to a specific state. That's just not included in the word `shuffle`. The only point where you should call `srand()` is in the `main` function. As a guide, just ask yourself: "What does this function do?" The answer for the `shuffle` function would be: "It shuffles the given array *and resets the random number generator*." This alone should sound weird enough.
Roland Illig
Got it, thank you.
MikeRand