一路迅雷笔试题引发的思考?—— 不重复随机算法

一道迅雷笔试题引发的思考?—— 不重复随机算法

csdn上看到的一帖子 http://topic.csdn.net/u/20120825/20/6171393d-15ea-4a50-ba30-78d1d24974e3.html,是关于一种不重复随机算法,可以计算0 ~ n中不重复的m个数。

#include <iostream>
#include <stdlib.h>
using namespace std;

void knuth(int n, int m)
{
    srand( (unsigned)time( NULL ) );
    for( int i = 0; i < n; i++)
    {
        if( rand()%(n-i) < m)
        {
            cout << i << "\t";
            m--;
        }// end if
    }//end for
    return;
}

int main()
{
    knuth(10, 5);
    return 0;
}

常见的算法有2种:

1、用一个大小为n的数组,随机取出一个,把该位置标为已取,防止下次重复取出;重复m次。缺点是当m接近n时,最后重复取出的概率很大。

2、包0 ~ n放在数组中,通过一些方法打乱顺序(比如交换法),然后取出前m个即可。

而上面的方法看起来很神奇,特别是 if ( rand() %(n-i) < m)很让人不解。帖子的楼主啰嗦了一大堆,于是我干脆自己分析一下。

一、如何不重复

这里取出发生的代码是

 cout << i << "\t";
 i 在for循环中,每次加1。因此可以保证每次cout都没有重复。

二、如何保证m次

rand() % (n-i) 的结果是从 [0, n-i]中随机取出一个,当n-i < m时,则if判断肯定能成功!m每次判断成功后都减一,所以这个判断肯定可以成功m次。而i最小值是0,所以当n > m时,肯定会出现某时刻 n - i < m的情况,所以上面的假设一定成立。


其实上面的算法可以再改一改 

void knuth(int n, int m)
{
    srand( (unsigned)time( NULL ) );
    for( int i = 0; i < n && m; i++)
    {
        if( rand()%(n-i) < m)
        {
            cout << i << "\t";
            m--;
        }// end if
    }//end for
    return;
}
因为当m等于0时,条件二是永远不能成功的。


总结:从复杂度看,此算法 <= O(n),性能很好。不过有人怀疑它的可随机性,这需要更强的数学证明,目前我还做不到一路迅雷笔试题引发的思考?—— 不重复随机算法