概率取样问题
2010-06-07 18:53
302 查看
问题:程序输入两个整数m和n,其中m<n。输出是0~n-1范围内的m个随机整数的有序列表,不允许重复,要求每个选择出现的概率相等。
(1)扫描算法:扫描0~n-1,通过一个概率测试来对每个整数进行选择,可保证输出结果是有序的。考虑m=2,m=5的情况。选择的0的概率为2/5,通过语句if(bigrand()%5)<2来实现。若选择了0,则在剩下的4个数中以1/4的概率选择一个数。若未选择0,则以2/4的概率选择下一个数,这样就能保证恰好能选出m个整数。这里bigrand()返回一个很大的随机整数,远大于m和n。一般来说,如果要从r个剩余整数中选择s个,我们以概率s/r选择下一个整数。
显然,算法的运行时间为O(n)。
(2)基于集合的算法:在一个初始为空的集合里面插入随机整数,直到个数足够,核心问题是如何实现集合S。我们可以考虑有序链表、二叉树等数据结构,但最直接的办法是利用C++标准模板库中的set容器。
C++ STL规范要求每次插入操作都在O(logm)时间内完成,而遍历集合则需要O(m)时间。因此整个程序需要O(mlogm)时间(当m相对于n较小时)。但是该数据结构的空间开销比较大。
当n比较大而m接近于n时,基于集合的算法需要生成大量的随机数,而其中很多随机数都要丢掉(因为之前集合中已经存在这个数了),这导致很多不必要的运行时间开销。Bob Floyd给出了一个算法,使得即使在最坏情况下也只使用m个随机数。如下(C++实现):
(3)排序算法:生成随机整数的有序子集的另一种方法是把包含整数0~n-1的数组顺序打乱,然后把前m个元素排序输出。
算法需要n个元素的内存空间和O(n+mlogm)的时间。
关键算法设计思想:扫描策略、概率测试(随机数生成)、集合数据结构、搜索策略、排序策略。
(1)扫描算法:扫描0~n-1,通过一个概率测试来对每个整数进行选择,可保证输出结果是有序的。考虑m=2,m=5的情况。选择的0的概率为2/5,通过语句if(bigrand()%5)<2来实现。若选择了0,则在剩下的4个数中以1/4的概率选择一个数。若未选择0,则以2/4的概率选择下一个数,这样就能保证恰好能选出m个整数。这里bigrand()返回一个很大的随机整数,远大于m和n。一般来说,如果要从r个剩余整数中选择s个,我们以概率s/r选择下一个整数。
#include <stdio.h> #include <stdlib.h> /* 返回一个很大的随机整数 */ int bigrand(){ return RAND_MAX*rand()+rand(); } /* 返回[t,u]范围内的一个随机整数 */ int randint(int t,int u){ return t+bigrand()%(u-t+1); } void genrand(int m,int n){ int i=0; for(i=0;i<n;++i) if((bigrand()%(n-i))<m){ printf("%d/n",i); m--; } } int main(void){ genrand(5,15); return 0; }
显然,算法的运行时间为O(n)。
(2)基于集合的算法:在一个初始为空的集合里面插入随机整数,直到个数足够,核心问题是如何实现集合S。我们可以考虑有序链表、二叉树等数据结构,但最直接的办法是利用C++标准模板库中的set容器。
#include <iostream> #include <cstdlib> #include <set> using namespace std; int bigrand(){ return RAND_MAX*rand()+rand(); } void gensets(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<<"/n"; } int main(void){ gensets(5,15); return 0; }
C++ STL规范要求每次插入操作都在O(logm)时间内完成,而遍历集合则需要O(m)时间。因此整个程序需要O(mlogm)时间(当m相对于n较小时)。但是该数据结构的空间开销比较大。
当n比较大而m接近于n时,基于集合的算法需要生成大量的随机数,而其中很多随机数都要丢掉(因为之前集合中已经存在这个数了),这导致很多不必要的运行时间开销。Bob Floyd给出了一个算法,使得即使在最坏情况下也只使用m个随机数。如下(C++实现):
void genfloyd(int m,int n){ set<int> S; set<int>::iterator iter; for(int j=n-m;j<n;j++){ int t=bigrand()%(j+1); if(S.find(t)==S.end()) //search t S.insert(t); //t not in S else S.insert(j); //t in S } for(iter=S.begin();iter!=S.end();++iter) cout<<*iter<<"/n"; }
(3)排序算法:生成随机整数的有序子集的另一种方法是把包含整数0~n-1的数组顺序打乱,然后把前m个元素排序输出。
#include <iostream> #include <cstdlib> #include <set> #include <algorithm> /* 返回一个很大的随机整数 */ int bigrand(){ return RAND_MAX*rand()+rand(); } /* 返回[t,u]范围内的一个随机整数 */ int randint(int t,int u){ return t+bigrand()%(u-t+1); } void genshuf(int m,int n){ int i,j,t; int *x=new int ; for(i=0;i<n;++i) x[i]=i; for(i=0;i<m;++i){ /* 把数组的元素顺序打乱,只需要前m个元素即可 */ j=randint(i,n-1); t=x[i]; x[i]=x[j]; x[j]=t; } std::sort(x,x+m); /* 对前m个元素排序 */ for(i=0;i<m;++i) std::cout<<x[i]<<std::endl; } int main(void){ genshuf(5,15); return 0; }
算法需要n个元素的内存空间和O(n+mlogm)的时间。
关键算法设计思想:扫描策略、概率测试(随机数生成)、集合数据结构、搜索策略、排序策略。
相关文章推荐
- 取样问题 总数n事先不知道,等概率取样 (编程珠玑chapter12 课后题10)
- 概率取样问题
- 概率取样问题
- 概率随机问题【2】 取样与概率
- 取样问题 总数n事先不知道,等概率取样 (编程珠玑chapter12 课后题10)
- 算法面试经常出现的问题——扑克牌中顺子和同花哪一个概率大?
- POJ 3756 Chess Game(概率问题)
- 一次遍历,等概率随机排列数组与带权随机选取问题
- 概率相关问题
- 从概率的角度解部分排列问题
- 概率随机问题
- HDOJ(HDU) 2201 熊猫阿波的故事(概率问题)
- 用matlab模拟一道概率投资问题
- [珠玑之椟]随机数函数取样与概率
- 概率问题
- sleepSort和硬币概率问题
- 又是一道随机问题,问题是说:写一个函数返回0,1,2,3这几个数字中的一个数,其中0概率是10%,1是20%,2是30%,3是40%
- (转)一道经典概率问题
- 《编程珠玑》--读书笔记12章:取样问题
- Alias Method解决随机类型概率问题