《编程珠玑》第12章习题

习题一:随机数可以参考《数据结构与算法分析C++描述》[美]Mark Allen Weiss著,张怀永等译。这本书的P329讲解了随机数的产生,即用xi+1=(Axi+C)2^B-1,来产生的随机数,因此随机数也是伪随机的,甚至会有一定的周期性。

#include<iostream>
#include<time.h>
using namespace std;

int bigRand()
{
	return (rand()<<15)+rand();
}

int randInt(int lo,int hi)
{
	return lo+bigRand()%(hi-lo+1);
}

int main()
{
	srand(time(0));
	cout<<bigRand()<<endl;
	cout<<randInt(10,100)<<endl;

	system("pause");
	return 0;
}

习题七:这个是一个比较常用的,在递归函数中输出或写入的一个技巧,放到前面是正序,放到后面是反序。

#include<iostream>
#include<time.h>
using namespace std;

int bigRand()
{
	return (rand()<<15)+rand();
}

int randInt(int lo,int hi)
{
	return lo+bigRand()%(hi-lo+1);
}

void randSelect(int m,int n)
{
	if(m>0)
	{
		if(bigRand()%(n-1)<m)
		{
			randSelect(m-1,n-1);
			cout<<n-1<<endl;
		}
		else
			randSelect(m,n-1);
	}
}

int main()
{
	srand(time(0));
	randSelect(10,100);
	
	system("pause");
	return 0;
}
习题八:

#include<iostream>
#include<set>
#include<vector>
#include<time.h>
#include<unordered_set>
using namespace std;

vector<int> randSelectAndRandOutput(int n,int m)
{
	unordered_set<int> unSet;//这里也可以用一个数组来保存当前的数是否存储过了
	vector<int> result;
	while(result.size()<m)
	{
		int temp=rand()%n;
		int size=unSet.size();
		unSet.insert(temp);
		if(unSet.size()!=size)
			result.push_back(temp);
	}
	return result;
}

void randMult(int n,int m)
{
	multiset<int> muSet;
	for(int i=0;i<m;i++)
		muSet.insert(rand()%n);
	for(multiset<int>::iterator iter=muSet.begin();iter!=muSet.end();++iter)
		cout<<*iter<<endl;
}

void randMultUnorder(int n,int m)
{
	vector<int> result;
	for(int i=0;i<m;i++)
	{
		result.push_back(rand()%n);
		cout<<result.back()<<endl;
	}
}

int main()
{
	srand(time(0));
	vector<int> randSelect=randSelectAndRandOutput(100,10);
	cout<<"输出顺序随机的m个随机数"<<endl;
	for(int i=0;i<10;i++)
		cout<<randSelect[i]<<endl;
	cout<<"输出顺序的有重复数字的m个随机数"<<endl;
	randMult(30,10);
	cout<<"输出顺序随机的有重复数字m个随机数"<<endl;
	randMultUnorder(30,10);

	system("pause");
	return 0;
}
习题九:证明选中每个元素的概率都是m/n。下面以10个数中选3个为例:对于前8个数来说,第一次被选中概率为1/8,第二次被选中,概率为7/8*1/9,第三次被选中的概率为 7/8*8/9*1/10,三者相加为3/10。对于第9个数来说,第一次选中概率为0,第二次选中概率为2/9,第三次选中概率为7/9*1/10,相加为3/10。对于最后一个元素,前两次选中概率为0,最后一次选中的概率为3/10,因此得证每个元素被选中的概率相等,都等于3/10。

#include<iostream>
#include<set>
#include<time.h>
using namespace std;

void genFloyd(int n,int m)
{
	set<int> S;
	for(int k=n-m;k<n;k++)
	{
		int temp=rand()%(k+1);//产生0-k的数
		if(S.find(temp)==S.end())
			S.insert(temp);
		else
			S.insert(k);
	}
	set<int>::iterator iter;
	for(iter=S.begin();iter!=S.end();++iter)
		cout<<*iter<<endl;
}

int main()
{
	genFloyd(100,10);

	system("pause");
	return 0;
}
第十题:参考:http://www.cnblogs.com/luxiaoxun/archive/2012/09/09/2677267.html 点击打开链接

1、问题定义可以简化如下:在不知道文件总行数的情况下,如何从文件中随机的抽取一行,并且每行被抽中的概率相等?

首先想到的是我们做过类似的题目吗?当然,在知道文件行数的情况下,我们可以很容易的用C运行库的rand()函数随机的获得一个行数,从而随机的取出一行,但是,当前的情况是不知道行数,这样如何求呢?我们需要一个概念来帮助我们做出猜想,来使得对每一行取出的概率相等,也即随机。这个概念即蓄水池抽样(Reservoir Sampling)。
有了这个概念,我们便有了这样一个解决方案:定义取出的行号为choice,第一次直接以第一行作为取出choice ,而后第二次以二分之一概率决定是否用第二行替换 choice ,第三次以三分之一的概率决定是否以第三行替换 choice ……,以此类推,可用伪代码描述如下:

i = 0
while more input lines
    with probability 1.0/++i
        choice = this input line
print choice

这种方法的巧妙之处在于成功的构造出了一种方式使得最后可以证明对每一行的取出概率都为1/n(其中n为当前扫描到的文件行数),换句话说对每一行取出的概率均相等,也即完成了随机的选取。

具体操作可参考如下伪代码:

复制代码
Element RandomPick(file): 
Int count = 0; 
while(count <= file.size) 
    If(random(0,count) == 0) 
        picked = file[count]; 
    ++ count; 
Return picked 
复制代码

证明如下:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值