【编程珠玑】读书笔记 第十二章 取样问题
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 请按任意键继续. . .