随机排列数组的产生

      在做hashmap测试数据生成的时候,留下了一个问题——随机数的生成。关于真随机数的生成,都需要硬件设备运行中采样的数据。是否真的做到随机,就要看采样的结果是否符合均匀分布。可以参考一下这篇文章 http://www.cnblogs.com/hehehu/archive/2005/08/23/221125.html

      在这里讨论的是随机排列数组的问题,问题引入:

      给定正数组A[n],求生成 一个随机序列。如给定A[n] = {1,2,3,4,5},生成的一个随机序列是:32451;要求每次产生的序列的概率是一样的。

      把问题建立成函数方式 void sequence(int * A, int count); 以下有两种方法实现:

      方法1:为数组的每一个元素A[i]赋一个随机的优先级P[i],然后根据优先级对数组A中的元素进行排序,这个过程在《算法导论》第二版翻译版本59页

PERMUTE-BY-SORTING(A)
1  n ← length[A]
2  for i ← 1 to n
3      do P[i ] = RANDOM(1, n^3)
4  sort A, using P as sort keys
5  return A

      这里注意到,产生这个随机序列需要生产一个RANDOM(1,n^3),也就是需要产生一个1-n^3之间的随机数,这个时候又涉及随机数的产生问题。我们在这里先回避这个问题,把随机排列数组的产生建立在已经完成随机数的生产的基础上,后续再单独讨论随机数生成的理论。在RANDOM(1,n^3)生成的数是1-n^3之间真随机数基础上,证明的过程可以参考算法导论。这个方法其中的第3行选取的一个在1到n^3之间的随机数,使用范围1到n^3,是为了让P中所有的优先级尽可能唯一的;第4行需要排序,算法的复杂度为排序算法的复杂度,我们知道比较法排序的时间代价是O(nlgn),如果数据比较多的话,是瓶颈。

      方法2:产生随机排列的一个更好的方法是原地排列给定的数列。程序RANDOMIZE-IN-PLACE在O(n)时间内完成。在第i次迭代时,元素A[i]是从元素A[i]到A[n]中随机选取的。第i次迭代之后,A[i]保持不变。

RANDOMIZE-IN-PLACE(A)
1  n ← length[A]
2  for i ← 1 to n 
3  do swap A[i ] ↔ A[RANDOM(i, n)]
      这里同样需要依赖RANDOM(i,n)来产生一个i-n之间的随机数,算法导论中通过数学归纳法证明了产生的这个序列是随机排列的。也就是每个序列出现的概率是1/n!。


      可以看到,上面两个方法都需要依赖随机数的产生。我们来看一下一般在程序库中用到的伪随机数生成器rand()以及其种子设置srand()的源码(以下以vc9中源码为例子)

void __cdecl srand (
        unsigned int seed
        )
{
        _getptd()->_holdrand = (unsigned long)seed;
}
int __cdecl rand (
        void
        )
{
        _ptiddata ptd = _getptd();

        return( ((ptd->_holdrand = ptd->_holdrand * 214013L
            + 2531011L) >> 16) & 0x7fff );
}
      ptd是运行时库多线程环境下的一个结构,我们不用关注,只要知道srand设置的_holdrand会用在rand的_holdrand中,并且这个数随着每次产生一个数的时候变化,变化的方式是通过k = k*214013L+2531011L,直觉觉得这两个数字是素数,不数的迭代产生下一个数。所以给定的一个种子数,生产的序列都是固定的。

      忽略这两个素数运算得到的序列中数据的覆盖范围问题(这也是一个问题!但一般来说不太关注,影响没这么大),问题转化为srand(unsigned int seed)这个函数需要输入的seed数值上了。通常使用的seed值可以从时间截,中断,鼠标位置等随时变化的参数中获取,但此问题已经超越了单纯计算机运算的问题。

      在此只是说明我们经常使用的产生随机序列的推理过程是正确的——上面两种产生随机序列的方法;但数据前提条件不正确——无法证明产生随机数为真随机数。在工程条件下,非真随机数也能满足条件,应用到生产中。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值