习题一:随机数可以参考《数据结构与算法分析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
证明如下: