rand()和Knuth shuffle算法

产生整数rand的原理是:   
      y=(ax+b)(mod  n)。其中n一般是一个很大的素数(几万)。a也是大素数,而且a,b,n都是常数。所以rand的产生决定于x,他被称seed。每一个seed都是上一次产生的y的函数。这样,如果直接取seed=y的话,虽然产生的rand之间相关性甚小,但只要知道某个y,就能推知以后的rand。   为避免这种情况,一般取seed为y和当时计算机的时间的函数,如seed=y+t系统里的随机数是利用初等数论中的同余定理来实现的.  比如C中对于rand()函数是如下实现的.   

http://www.cnblogs.com/xkfz007/archive/2012/03/27/2420154.html

Knuth-Durstenfeld Shuffle  
           Knuth 和 Durstenfeld  在Fisher 等人的基础上对算法进行了改进,在原始数组上对数字进行交互,省去了额外O(n)的空间。该算法的基本思想和 Fisher 类似,每次从未处理的数据中随机取出一个数字,然后把该数字放在数组的尾部,即数组尾部存放的是已经处理过的数字。
               算法步骤为:
                 1. 建立一个数组大小为 n 的数组 arr,分别存放 1 到 n 的数值;
                 2. 生成一个从 0 到 n - 1 的随机数 x;
                 3. 输出 arr 下标为 x 的数值,即为第一个随机数;
                 4. 将 arr 的尾元素和下标为 x 的元素互换;
                 5. 同2,生成一个从 0 到 n - 2 的随机数 x;
                 6. 输出 arr 下标为 x 的数值,为第二个随机数;
                 7. 将 arr 的倒数第二个元素和下标为 x 的元素互换;
                    ……
                 如上,直到输出 m 个数为止
该算法是经典洗牌算法。它的proof如下:
对于arr[i],洗牌后在第n-1个位置的概率是1/n(第一次交换的随机数为i)
在n-2个位置概率是[(n-1)/n] * [1/(n-1)] = 1/n,(第一次交换的随机数不为i,第二次为arr[i]所在的位置(注意,若i=n-1,第一交换arr[n-1]会被换到一个随机的位置))
在第n-k个位置的概率是[(n-1)/n] * [(n-2)/(n-1)] *...* [(n-k+1)/(n-k+2)] *[1/(n-k+1)] = 1/n
(第一个随机数不要为i,第二次不为arr[i]所在的位置(随着交换有可能会变)……第n-k次为arr[i]所在的位置).

void Knuth_Durstenfeld_Shuffle(vector&arr)
{
    for (int i=arr.size()-1;i>=0;--i)
    {
        srand((unsigned)time(NULL));
        swap(arr[rand()%(i+1)],arr[i]);
    }
} 


    时间复杂度为O(n),空间复杂度为O(1),缺点必须知道数组长度n.

原始数组被修改了,这是一个原地打乱顺序的算法,算法时间复杂度也从Fisher算法的 O(n2)提升到了O(n)。由于是从后往前扫描,无法处理不知道长度或动态增长的数组。
--------------------- 
作者:lyz_cs 
原文:https://blog.csdn.net/qq_26399665/article/details/79831490 

你可能感兴趣的:(c)