快速避免模偏置的方法

Fast way to avoid modulo bias

本文关键字:方法      更新时间:2023-10-16

我正在进行洗牌,它经常在一个小数组上完成。可以是1 - 10个元素。

我已经尝试了这个问题的公认答案:

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

不幸的是,它非常慢。

我需要一种更快的方法来做到这一点,并避免我所看到的模偏差。有什么建议吗?

编辑:

抱歉,我应该指出慢的不是shuffle,而是用来生成一个随机int范围的方法。即rand_int()。我使用的是梅森绕口令算法,在我的情况下,RAND_MAX是UINT_MAX来帮助。当n远小于RAND_MAX

时,这当然会使它变慢

我还发现了两个rand_int类型函数的实现。

static int rand_int(int n) {
  int limit = RAND_MAX - RAND_MAX % n;
  int rnd;
  do {
    rnd = rand();
  } while (rnd >= limit);
  return rnd % n;
}

下面的内容要快得多。但是,它能避免模偏置问题吗?

int rand_int(int limit) {
    int divisor = RAND_MAX/(limit);
    int retval;
    do { 
        retval = rand() / divisor;
    } while (retval > limit);
    return retval;
}

编辑

关于避免rand()模偏置的基本问题,请参见http://eternallyconfuzzled.com/arts/jsw_art_rand.aspx。

简而言之,除非跳过非域随机数1,否则无法获得真正的均匀;本文列出了一些公式,可以在不牺牲更多性能的情况下获得更小的偏差(int r = rand() / ( RAND_MAX / N + 1 ))。

1参见Java中Random.nextInt(int)的实现:http://download.oracle.com/javase/1.4.2/docs/api/java/util/Random.html nextInt (int)


使用c++

你应该能够使用std::random_shuffle(从<algorithm>头);

如果必须使用自己的shuffle实现,我建议使用std::random (TR1、c++ 0x或Boost)。它附带了许多生成器和发行版,具有不同的性能特征。

#include <random>
std::mt19937 rng(seed);
std::uniform_int_distribution<int> gen(0, N); // uniform, unbiased
int r = gen(rng);

关于boost随机生成器和分布特性的概述,请参阅boost文档:

  • http://www.boost.org/doc/libs/1_47_0/doc/html/boost_random/reference.html boost_random.reference.generators

下面是一个使用Boost Random直接执行std::random_shuffle的示例:

#include <algorithm>
#include <functional>
#include <vector>
#include <boost/random.hpp>
struct Rng
{
    Rng(boost::mt19937 &rng) : _rng(rng) {}
    unsigned operator()(unsigned i) 
    {
        boost::uniform_int<> dist(0, i - 1);
        return dist(_rng);
    }
  private:        
    boost::mt19937 &_rng;
};
boost::mt19937 state;
std::random_shuffle(v.begin(), v.end(), Rng(state));