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

Mik*_*and 10 c shuffle

这是Fisher-Yates的C实现,我想在一个套牌改组例程中使用它.我这样做是否正确(n =数组的长度)?

注意:do-while循环尝试校正模偏差(参见此处).它为程序增加了一些开销,如果您不关心低位偏置,可以将其消除.

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;   
  }
}
Run Code Online (Sandbox Code Playgroud)

Rol*_*lig 27

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

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

第三,你应该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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 不.它意味着:任何有经验的程序员都不会期望一个名为`shuffle`的例程将随机数发生器重置为特定状态.这只是"shuffle"这个词中没有包含的内容.你应该调用`srand()`的唯一一点是`main`函数.作为指导,请问自己:"这个功能有什么作用?" `shuffle`函数的答案是:"它会改变给定的数组*并重置随机数生成器*." 仅此一点听起来应该很奇怪. (3认同)
  • @KomodoDave这将节省一个单位的内存,但代价是调用`swap`函数所涉及的开销和上下文切换.此外,这似乎是纯C,而不是C++,所以它可能不是一个选项. (3认同)