编程珠玑——随机抽取

在这本书的第12章有个随机抽样的问题,大概是这样的:

从n个数中,随机取出m个数,要求不重复,每个数被选中的概率相等。注意这里的n是固定的。如果是不固定的,其实就是蓄水池抽样问题。

算法一:

如果每次从r个剩余的数中选s个,那么我们就以s/r的概率选择下一个数。比如m=3, n=10. 以3/10的概率选择1,如果1被选,则

m=2, n=9 , 那么以2/9的概率选择2,否则m=3, n=9, 就以3/9=1/3的概率选择2。依次类推。

void randomSelect(int m,int n,std::vector<int>& v){
	if(n<=m){
		for(int i=1;i<=n;i++){
			v.push_back(i);
		}
		return;
	}
	srand(time(0));
	int select=m;
	int remain=n;
	for(int i=1;i<=n;i++){
		if(rand()%remain<select){
			v.push_back(i);
			select--;
		}
		remain--;
	}
	return;
}

时间复杂度是O(n),空间复杂度是O(1)

算法二:

 用一个set装被选中的元素。每次随机生成0~n-1范围的数字,插入集合。如果集合当前元素个数小于m,继续插入,当等于m时,即选中了m个元素。返回

void randomSelect(int m,int n,set<int>& s){
	srand(time(0));
	while(s.size()<m){
		s.insert(rand()%n);
	}
}

时间复杂度:当n很大,而m比较小时,可以近似认为每次采样几乎不重复,这样运行时间就正比与m.故时间复杂度就是O(m)。比上一个算法快些。但是如果m很大时,会出现大量的重复采样,运行时间就大大延长。有个好的办法是当m和n接近时,我们用set的元素表示不选中的元素,则其余就是选中的元素。


算法三:

用大小n的数组保存0~n-1的数字,然后用shuffle函数随机打乱,然后取前m个元素作为选中元素。

void shuffle(int a[],int n){
	srand(time(0));
	for(int i=0;i<n;i++){
		int r=i+rand()%(n-i);
		if(i!=r){
			int t=a[r];
			a[r]=a[i];
			a[i]=t;
		}
	}
}

时间复杂度O(n),空间复杂度O(n)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值