这个Fisher-Yates的C实现是否正确?

时间:2010-07-27 12:54:43

标签: c shuffle

这是Fisher-Yates的C实现,我想在deck-shuffling例程中使用它。我这样做是否正确(n =数组的长度)?

注意:do-while循环尝试校正模偏差(参见here)。它会给程序增加一些开销,如果你不关心低位偏差,可以将其消除。

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;   
  }
}

1 个答案:

答案 0 :(得分:27)

首先,您应该提取用于生成随机数的代码,该随机数在0(包括)和n(不包括)之间平均分配给单独的函数。这也是你在其他地方需要的一项很好的工作任务。

其次,我不会在srand函数内调用shuffle,而是在初始化随机数生成器时依赖调用者。这样你就可以在一秒钟内多次洗牌。

第三,在除以j > upper_bound之前,您应该对i + 1进行测试。 i不太可能接近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;
  }
}

要检查此实现是否正确,您需要确保向随机数生成器询问log2(n!)位随机性。换句话说,n函数的所有rand_int s的乘积必须为n!