验证Knuth shuffle算法尽可能无偏

时间:2009-11-06 04:07:38

标签: c++ algorithm shuffle random knuth

我正在为我正在开发的C ++项目实现Knuth shuffle。我试图从我的shuffle获得最无偏见的结果(我不是(伪)随机数生成的专家)。我只是想确保这是最无偏见的随机实现。

draw_t是字节类型(typedef'd到unsigned char)。 items是列表中的项目数。我在下面列出了random::get( draw_t max )的代码。

for( draw_t pull_index = (items - 1); pull_index > 1; pull_index-- )
{
    draw_t push_index = random::get( pull_index );

    draw_t push_item = this->_list[push_index];
    draw_t pull_item = this->_list[pull_index];

    this->_list[push_index] = pull_item;
    this->_list[pull_index] = push_item;
}

我正在使用的随机函数已被修改以消除modulo biasRAND_MAX被分配到random::_internal_max

draw_t random::get( draw_t max )
{
    if( random::_is_seeded == false )
    {
        random::seed( );
    }

    int rand_value = random::_internal_max;
    int max_rand_value = random::_internal_max - ( max - ( random::_internal_max % max ) );

    do
    {
        rand_value = ::rand( );
    } while( rand_value >= max_rand_value );

    return static_cast< draw_t >( rand_value % max );
}

5 个答案:

答案 0 :(得分:8)

嗯,你可以做的一件事就是进行黑盒测试需要一些相对较小的数组大小,对它执行大量的随机播放,计算你观察每个排列的次数,然后执行Pearson's Chi-square测试以确定结果是否均匀分布在置换空间上。

另一方面,只要索引来自的随机数发生器是无偏的,Knuth shuffle,AKA the Fisher-Yates shuffle,被证明是无偏见的。

答案 1 :(得分:8)

如果我看到了,random::get (max)不包括max

这一行:

draw_t push_index = random::get( pull_index );

然后会产生“经典”的逐个错误,因为pull_indexpush_index错误地永远不会相同。这产生了一种微妙的偏见,你可以永远不会有一个项目在洗牌之前。在一个极端的例子中,这种“洗牌”下的两项清单总是会逆转。

答案 2 :(得分:3)

看看杰夫阿特伍德的这篇文章:

<强>洗牌
http://www.codinghorror.com/blog/archives/001008.html

另见:

Naïveté的危险
http://www.codinghorror.com/blog/archives/001015.html

答案 3 :(得分:2)

Knuth shuffle本身可以证明是无偏见的:只有一系列操作可以产生每个可能的混乱。然而,你的PRNG不太可能有足够的状态来表达每一个可能的洗牌,所以真正的问题是你的PRNG对于它实际产生的洗牌是否“足够随机”,以及你的播种策略是否足够安全

只有你可以决定这个,因为它取决于一个不随机的随机播放的后果。例如,如果您正在处理真钱,我建议您切换到加密安全的PRNG并改进播种策略。虽然大多数内置的PRNG都能产生良好的随机性,但它们也很容易进行逆向工程,并且调用没有参数的seed()可能会根据当前时间播种,这很容易预测。

答案 4 :(得分:0)

#include <cstdlib> // srand() && rand()

/** Shufle the first 'dim' values in array 'V[]'.
    - Implements the Fisher–Yates_shuffle.
    - Uses the standard function 'rand()' for randomness.
    - Initialices the random sequence using 'seed'.
    - Uses 'dim' swaps.
    \see http://stackoverflow.com/questions/1685339/
    \see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
*/
template <class T>
void Fisher_Yates_shuffle( T* V, unsigned dim , unsigned seed ) {
    srand(seed);
    T temp;
    unsigned i,iPP;

    i   = dim-1;
    iPP = dim;
    while ( i>0 ) {
        unsigned j = rand() % iPP;
        if ( i!=j ) { // swap
            temp = V[i]; V[i] = V[j]; V[j] = temp;
        }
        iPP = i;
        --i;
    }
/*
    This implementation depends on the randomness of the random number
    generator used ['rand()' in this case].
*/
}