我正在为我正在开发的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 bias。 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 );
}
答案 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_index
和push_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].
*/
}