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

         问题描述:程序的输入包含两个整数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=m
remaining=n
for i=[0,n)
	if(bigrand()%remaining)<select
			print i
			select--
		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;
}

你可能感兴趣的:(编程,c,iterator,ini)