4

我正在为我正在处理的 C++ 项目实现Knuth shuffle。我试图从我的洗牌中获得最公正的结果(而且我不是(伪)随机数生成方面的专家)。我只是想确保这是最公正的洗牌实现。

draw_t是字节类型(typedef'd to 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;
}

我正在使用的随机函数已被修改以消除模偏差RAND_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 );
}
4

5 回答 5

8

好吧,作为黑盒测试,您可以做的一件事是采用一些相对较小的数组大小,对其执行大量洗牌,计算您观察每个排列的次数,然后执行皮尔逊卡方检验以确定是否结果均匀分布在置换空间上。

另一方面,只要索引来自的随机数生成器是无偏的,Knuth shuffle(又名 Fisher-Yates shuffle)就被证明是无偏的。

于 2009-11-06T04:23:36.413 回答
8

如果我没看错,你的random::get (max)不包括max.

这一行:

draw_t push_index = random::get( pull_index );

然后会产生一个“经典”的错误,因为您的pull_index错误push_index永远不会相同。这会产生一种微妙的偏差,即您永远无法拥有洗牌前的物品。在一个极端的例子中,这种“洗牌”下的两项列表总是会颠倒过来。

于 2009-12-08T13:26:03.937 回答
3

看看 Jeff Atwood 的这篇文章:

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

也可以看看:

天真的危险
http://www.codinghorror.com/blog/archives/001015.html

于 2009-11-06T04:22:22.527 回答
2

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

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

于 2009-11-06T09:37:19.353 回答
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].
*/
}
于 2015-01-02T16:59:27.707 回答