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

来源:岁月联盟 编辑:exp 时间:2012-08-31

关于一种不重复随机算法,可以计算0 ~ n中不重复的m个数。

[cpp] 
#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)很让人不解。帖子的楼主啰嗦了一大堆,于是我干脆自己分析一下。

一、如何不重复

这里取出发生的代码是

[cpp] 
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的情况,所以上面的假设一定成立。


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

[cpp] 
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时,条件二是永远不能成功的。