【编程珠玑】读书笔记 第十二章 取样问题

2013-07-16 21:51:49

本章介绍生成小于[0,n-1]区间内的m个顺序随机数的三种方法。

总结参考:http://www.cnblogs.com/dandingyy/archive/2012/09/09/2677511.html

1.问题

  抽象后的问题如下:输入两个整数m和n,(m < n).输出0~n-1范围内的m个随机整数的有序列表,不允许重复。

  也就是说,要对0~n-1范围内的数字进行选择,每个数字被选中的概率相等.

  有两点要注意:不允许重复,结果有序;
2.解决方案

  2.1已有知识

  利用库函数<stdlib.h>中的rand()函数可以产生0到RAND_MAX范围内的随机整数。

   RAND_MAX是在前面头文件中定义的宏,具体大小与实现有关,至少为32767(2^15-1).

  两个相关函数:

  产生很大随机整数bigrand():RAND_MAX *rand() + rand();实际上就是先产生前15位,再产生后15位,也即rand()<<15 | rand();

  产生指定范围随机整数 randint(l,u): rand()%(u-l+1)+l; 产生的数据在[l,u]之间.

  (后面对时间复杂度的讨论,都默认rand()需要单位时间)

3.时间效率

RandomGenerateKnuth时间效率:该程序的时间复杂度是与n有关的:O(n),空间上只需要几十个字节。

RandomGenerateUseSet时间效率:set每次插入时间为O(logm),while循环总共进行了m次插入,遍历集合需要O(m).故总时间为O(mlogm).额外的空间需求为set集的大小:O(m).

RandomGenerateUseQsort时间效率:时间上为O(n+mlogm), 一次初始化和排序。空间上也需要O(n).

 

完整实现代码:

  1 #include <iostream>
  2 #include <cassert>
  3 #include <set>
  4 #include <ctime>
  5 using namespace std;
  6 
  7 const int MaxLength = 10000000;
  8 
  9 //输入合法性检查
 10 void CheckInvalid(int array[],int len)
 11 {
 12     assert(NULL != array && len > 0); 
 13 }
 14 
 15 //产生大的随机数
 16 int BigRandomGenarate()
 17 {
 18     return ( RAND_MAX * rand() + rand() ); //对C语言,rand()函数需包含头文件#include<stdlib.h>,但在C++中包含iostream即可
 19 }
 20 
 21 //产生在[lowBound,upperBound - 1]区间的随机数
 22 int RandomIntGenerate(int lowBound, int upperBound)
 23 {    
 24     return (lowBound + (RAND_MAX * rand() + rand()) % (upperBound - lowBound + 1) );
 25 }
 26 
 27 //qsort对int型数据排序所需的笔记函数
 28 int IntCompare(const void *_p,const void *_q)
 29 {    
 30     int *p = (int *) _p;
 31     int *q= (int *) _q;
 32     return (*p - *q);
 33 }
 34 
 35 //用Knuth的方法产生随机数,用概率判断数据是否被选择
 36 void RandomGenerateKnuth(int randomArray[],const int lengthOfRandom,const int maxRandomNumber)
 37 {
 38     int i;
 39     int selectedNum = lengthOfRandom;
 40 
 41     srand( time(NULL) );    //产生rand()函数的种子
 42 
 43     for (i = 0;i < maxRandomNumber;++i)
 44     {
 45         if (BigRandomGenarate() % (maxRandomNumber - i) < selectedNum)
 46         {
 47             randomArray[lengthOfRandom - selectedNum] = i;
 48             --selectedNum;
 49         }
 50     }
 51 }
 52 
 53 //产生[0,n-1]区间内的随机数,将不重复的问题利用set容器的唯一性解决
 54 void RandomGenerateUseSet(int randomArray[],const int lengthOfRandom,const int maxRandomNumber)
 55 {
 56     set <int> randomSet;
 57     int i;
 58 
 59     srand( time(NULL) );    //产生rand()函数的种子
 60 
 61     while (randomSet.size() < lengthOfRandom)
 62     {
 63         randomSet.insert(BigRandomGenarate() % maxRandomNumber);
 64     }
 65 
 66     set <int> :: iterator iterSet;
 67     i = 0;
 68     for (iterSet = randomSet.begin();iterSet != randomSet.end();++iterSet)
 69     {
 70         randomArray[i++] = *iterSet;
 71     }
 72 }
 73 
 74 //通过交换保证不重复,但进行排序来保证是顺序的
 75 void RandomGenerateUseQsort(int randomArray[],const int lengthOfRandom,const int maxRandomNumber)
 76 {
 77     int i;
 78     int *sequenceArray = new int[maxRandomNumber];
 79     int randomTmp;
 80     int tmp;
 81 
 82     srand( time(NULL) );    //产生rand()函数的种子
 83 
 84     for (i = 0;i < maxRandomNumber;++i)
 85     {
 86         sequenceArray[i] = i;
 87     }
 88 
 89     for (i = 0;i < lengthOfRandom;++i)
 90     {
 91         randomTmp = RandomIntGenerate(i,maxRandomNumber);
 92         tmp = sequenceArray[i];
 93         sequenceArray[i] =  sequenceArray[randomTmp];
 94         sequenceArray[randomTmp] = tmp;
 95     }
 96 
 97     for (i = 0;i < lengthOfRandom;++i)
 98     {
 99         randomArray[i] = sequenceArray[i];
100     }
101 
102     qsort(randomArray,lengthOfRandom,sizeof(int),IntCompare);
103     delete [] sequenceArray;  
104 }
105 
106 //显示数组
107 void DisplayArray(int array[],int len)
108 {
109     CheckInvalid(array,len);
110 
111     for (int i = 0;i < len;++i)
112     {
113         cout<<array[i]<<"\t";
114     }
115     cout<<endl;
116 }
117 
118 //测试“脚手架”
119 void TestDriver()
120 {    
121     int *randomArray = new int[MaxLength];
122     int lengthOfRandom;
123     int maxRandomNumber;
124 
125     int programToTest;
126     int i;
127     int timeStart = 0;
128     double timeCostAverage = 0;
129     
130     //测试程序编号
131     cout<<"the identifier of the program is :"<<endl;
132     cout<<"RandomGenerateKnuth : 1"<<endl;
133     cout<<"RandomGenerateUseSet : 2"<<endl;
134     cout<<"RandomGenerateUseQsort : 3"<<endl;
135     cout<<endl;
136 
137     //输入测试参数
138     cout<<"please enter the lengthOfRandom and the maxRandomNumber ,end with ctrl+z :"<<endl;
139     cin>>lengthOfRandom>>maxRandomNumber;
140 
141     //输入要测试程序的编号
142     cout<<"please enter the identifier of the program to test (end with ctrl+z): "<<endl;
143     while (cin>>programToTest)
144     {
145         timeStart = clock();
146         switch (programToTest)
147         {
148             case 1: 
149                 cout<<"Test RandomGenerateKnuth..."<<endl;
150                 RandomGenerateKnuth(randomArray,lengthOfRandom,maxRandomNumber);
151                 break;
152 
153             case 2: 
154                 cout<<"Test RandomGenerateUseSet..."<<endl;
155                 RandomGenerateUseSet(randomArray,lengthOfRandom,maxRandomNumber);
156                 break;
157                 
158             case 3: 
159                 cout<<"Test RandomGenerateUseQsort..."<<endl;
160                 RandomGenerateUseQsort(randomArray,lengthOfRandom,maxRandomNumber);
161                 break;
162 
163             default:
164                 break;
165         }
166 
167         timeCostAverage = 1e6 * ( clock() - timeStart ) / ( CLOCKS_PER_SEC);
168         cout<<"the time cost to genarate "<<lengthOfRandom<<" random numbers is : "<<timeCostAverage<<" ms"<<endl;
169 
170         //检查是否是有序的
171         for (i = 0;i < lengthOfRandom - 1;++i)
172         {
173             if (randomArray[i] > randomArray[i + 1])
174             {
175                 cout<<"sort bug i = "<<i<<endl;
176             }
177         }
178 
179         /*cout<<"the sorted random array is :"<<endl;
180         DisplayArray(randomArray,lengthOfRandom);*/
181 
182         cout<<endl;
183         cout<<"please enter the identifier of the program to test (end with ctrl+z): "<<endl;
184     }
185 
186     delete [] randomArray;  //释放空间
187 }
188 
189 //主函数
190 int main(void)
191 {
192     TestDriver();
193     return 0;
194 }

测试结果:

the identifier of the program is :
RandomGenerateKnuth : 1
RandomGenerateUseSet : 2
RandomGenerateUseQsort : 3

please enter the lengthOfRandom and the maxRandomNumber ,end with ctrl+z :
1000000 10000000
please enter the identifier of the program to test (end with ctrl+z):
1
Test RandomGenerateKnuth...
the time cost to genarate 1000000 random numbers is : 2.764e+006 ms

please enter the identifier of the program to test (end with ctrl+z):
2
Test RandomGenerateUseSet...
the time cost to genarate 1000000 random numbers is : 2.5582e+007 ms

please enter the identifier of the program to test (end with ctrl+z):
3
Test RandomGenerateUseQsort...
the time cost to genarate 1000000 random numbers is : 3.255e+006 ms

please enter the identifier of the program to test (end with ctrl+z):
^Z
请按任意键继续. . .

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值