编程珠玑--第12章 取样问题

来源:互联网 发布:mysql中删除列 编辑:程序博客网 时间:2024/05/14 13:03

         问题描述:程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。从概率的角度说,我们希望得到没有重复的有序序列,其中每个选择出现的概率相等。

讨论这些问题前有两个假设:1.有一个函数bigrand()能够返回很大的随机整数(远远大于m和n)。

             2.另一个函数randint(i,j)能够返回i..j范围内均匀选择的随机整数。

解法一:假设m=2,n=5,选择第一个数0的概率为2/5,可以通过以下语句来实现:if(bigrand()%5)<2,<2也就是只能取0或者1,表示了概率为2/5。但是当我们选择1的时候,要根据前面是否选择了0。如果选择了0,则按照1/4的概率选择1,而未选择0的情况下按照2/4的概率选择1,以此类推。当m=n的时候,一定能够被选中(因为bigrand()%m<n(m)一定是成立的)。

select=mremaining=nfor i=[0,n)if(bigrand()%remaining)<selectprint iselect--remaining--
分析:这里我们是遍历[0,n),然后用概率选择,如果选中了select和remaining都要减1,如果没有选中remaining减1。当select减少到0的时候程序虽然在运行,但是已经不可能再选中了,所以可以修改一下程序当select减少为0的时候跳出。Knuth给出了证明,每个子集被选中的可能性是相等的。

c++实现:

void getknuth(int m,int n){for(int i=0;i<n;i++)if((bigrand()%(n-i))<m){cout<<i<<"\n";m--;}}

解法二:在一个初始为空的集合里面插入随机整数,直到个数足够。我们这里使用c++ stl中的set来实现。

set是一个容器,它其中包含的元素值是唯一的。比如我们以下面这段程序说明:

#include<iostream>#include<set>using namespace std;int main(){set<int> s;s.insert(3);s.insert(3);cout<<s.size();return 0;}
输出的值是1,若相同的话set选择不插入。

void getsets(int m,int n){set<int> S;while(S.size()<m)S.insert(bigrand()%n);set<int>::iterator i;for(i=S.begin();i!=S.end();++i)cout<<*i<<endl;}
解法三:把包含0~n-1的数组顺序打乱,然后把前m个元素排序输出。

void genshuf(int m,int n){int i,j;int *x=new int[n];for(i=0;i<n;i++)x[i]=i;for(i=0;i<m;i++){j=randint(i,n-1);int t=x[i];x[i]=x[j];x[j]=t;}sort(x,x+m);for(i=0;i<m;i++)cout<<x[i]<<endl;}

原创粉丝点击