从1....n中随机输出m个不重复的数

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

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <stdlib.h>  
  3. using namespace std;  
  4.   
  5. void knuth(int n, int m)  
  6. {  
  7.     srand( (unsigned)time( NULL ) );  
  8.     forint i = 0; i < n; i++)  
  9.     {  
  10.         if( rand()%(n-i) < m)  
  11.         {  
  12.             cout << i << "\t";  
  13.             m--;  
  14.         }// end if  
  15.     }//end for  
  16.     return;  
  17. }  
  18.   
  19. int main()  
  20. {  
  21.     knuth(10, 5);  
  22.     return 0;  
  23. }  
 
 

常见的算法有2种:

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

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

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

一、如何不重复

这里取出发生的代码是

[cpp]  view plain copy
  1. 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]  view plain copy
  1. void knuth(int n, int m)  
  2. {  
  3.     srand( (unsigned)time( NULL ) );  
  4.     forint i = 0; i < n && m; i++)  
  5.     {  
  6.         if( rand()%(n-i) < m)  
  7.         {  
  8.             cout << i << "\t";  
  9.             m--;  
  10.         }// end if  
  11.     }//end for  
  12.     return;  
  13. }  
因为当m等于0时,条件二是永远不能成功的。


总结:从复杂度看,此算法 <= O(n),性能很好。不过有人怀疑它的可随机性,这需要更强的数学证明,目前我还做不到尴尬

你可能感兴趣的:(从1....n中随机输出m个不重复的数)