您的位置:首页 > 编程语言

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

2013-07-16 22:22 399 查看
2013-07-16 21:51:49

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

总结参考:/article/5973089.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).

完整实现代码:

#include <iostream>
#include <cassert>
#include <set>
#include <ctime>
using namespace std;

const int MaxLength = 10000000;

//输入合法性检查
void CheckInvalid(int array[],int len)
{
assert(NULL != array && len > 0);
}

//产生大的随机数
int BigRandomGenarate()
{
return ( RAND_MAX * rand() + rand() ); //对C语言,rand()函数需包含头文件#include<stdlib.h>,但在C++中包含iostream即可
}

//产生在[lowBound,upperBound - 1]区间的随机数
int RandomIntGenerate(int lowBound, int upperBound)
{
return (lowBound + (RAND_MAX * rand() + rand()) % (upperBound - lowBound + 1) );
}

//qsort对int型数据排序所需的笔记函数
int IntCompare(const void *_p,const void *_q)
{
int *p = (int *) _p;
int *q= (int *) _q;
return (*p - *q);
}

//用Knuth的方法产生随机数,用概率判断数据是否被选择
void RandomGenerateKnuth(int randomArray[],const int lengthOfRandom,const int maxRandomNumber)
{
int i;
int selectedNum = lengthOfRandom;

srand( time(NULL) );    //产生rand()函数的种子

for (i = 0;i < maxRandomNumber;++i)
{
if (BigRandomGenarate() % (maxRandomNumber - i) < selectedNum)
{
randomArray[lengthOfRandom - selectedNum] = i;
--selectedNum;
}
}
}

//产生[0,n-1]区间内的随机数,将不重复的问题利用set容器的唯一性解决
void RandomGenerateUseSet(int randomArray[],const int lengthOfRandom,const int maxRandomNumber)
{
set <int> randomSet;
int i;

srand( time(NULL) );    //产生rand()函数的种子

while (randomSet.size() < lengthOfRandom)
{
randomSet.insert(BigRandomGenarate() % maxRandomNumber);
}

set <int> :: iterator iterSet;
i = 0;
for (iterSet = randomSet.begin();iterSet != randomSet.end();++iterSet)
{
randomArray[i++] = *iterSet;
}
}

//通过交换保证不重复,但进行排序来保证是顺序的
void RandomGenerateUseQsort(int randomArray[],const int lengthOfRandom,const int maxRandomNumber)
{
int i;
int *sequenceArray = new int[maxRandomNumber];
int randomTmp;
int tmp;

srand( time(NULL) );    //产生rand()函数的种子

for (i = 0;i < maxRandomNumber;++i)
{
sequenceArray[i] = i;
}

for (i = 0;i < lengthOfRandom;++i)
{
randomTmp = RandomIntGenerate(i,maxRandomNumber);
tmp = sequenceArray[i];
sequenceArray[i] =  sequenceArray[randomTmp];
sequenceArray[randomTmp] = tmp;
}

for (i = 0;i < lengthOfRandom;++i)
{
randomArray[i] = sequenceArray[i];
}

qsort(randomArray,lengthOfRandom,sizeof(int),IntCompare);
delete [] sequenceArray;
}

//显示数组
void DisplayArray(int array[],int len)
{
CheckInvalid(array,len);

for (int i = 0;i < len;++i)
{
cout<<array[i]<<"\t";
}
cout<<endl;
}

//测试“脚手架”
void TestDriver()
{
int *randomArray = new int[MaxLength];
int lengthOfRandom;
int maxRandomNumber;

int programToTest;
int i;
int timeStart = 0;
double timeCostAverage = 0;

//测试程序编号
cout<<"the identifier of the program is :"<<endl;
cout<<"RandomGenerateKnuth : 1"<<endl;
cout<<"RandomGenerateUseSet : 2"<<endl;
cout<<"RandomGenerateUseQsort : 3"<<endl;
cout<<endl;

//输入测试参数
cout<<"please enter the lengthOfRandom and the maxRandomNumber ,end with ctrl+z :"<<endl;
cin>>lengthOfRandom>>maxRandomNumber;

//输入要测试程序的编号
cout<<"please enter the identifier of the program to test (end with ctrl+z): "<<endl;
while (cin>>programToTest)
{
timeStart = clock();
switch (programToTest)
{
case 1:
cout<<"Test RandomGenerateKnuth..."<<endl;
RandomGenerateKnuth(randomArray,lengthOfRandom,maxRandomNumber);
break;

case 2:
cout<<"Test RandomGenerateUseSet..."<<endl;
RandomGenerateUseSet(randomArray,lengthOfRandom,maxRandomNumber);
break;

case 3:
cout<<"Test RandomGenerateUseQsort..."<<endl;
RandomGenerateUseQsort(randomArray,lengthOfRandom,maxRandomNumber);
break;

default:
break;
}

timeCostAverage = 1e6 * ( clock() - timeStart ) / ( CLOCKS_PER_SEC);
cout<<"the time cost to genarate "<<lengthOfRandom<<" random numbers is : "<<timeCostAverage<<" ms"<<endl;

//检查是否是有序的
for (i = 0;i < lengthOfRandom - 1;++i)
{
if (randomArray[i] > randomArray[i + 1])
{
cout<<"sort bug i = "<<i<<endl;
}
}

/*cout<<"the sorted random array is :"<<endl;
DisplayArray(randomArray,lengthOfRandom);*/

cout<<endl;
cout<<"please enter the identifier of the program to test (end with ctrl+z): "<<endl;
}

delete [] randomArray;  //释放空间
}

//主函数
int main(void)
{
TestDriver();
return 0;
}


测试结果:

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
请按任意键继续. . .
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: