如何完美地取样?
来源:互联网 发布:linux系统cpu使用率 编辑:程序博客网 时间:2024/04/26 11:20
对于n个样本,如何均匀随机的取出m个样本?即n个样本中每个样本都能有m/n的概率被取中。
1.简单插入取样
这是最基本,最直观的方法。在一个初始为空的集合中插入1~n的随机整数,知道个数为m个为止。但这个方法有个弱点,就是要插入一个数时,判断集合中是否存在该数,如果其存在,则要继续取样直到取到一个不在原集合中的数,重复取样需要很大的开销,而且越到后来开销越大。
2.Floyd取样
Floyd取样是大名鼎鼎的Robert W. Floyd提出来的。其基本思想:当已经在[1,n-1]个区间中已随机取出m-1个样本时,这时,生成一个1~n的随机数,如果该数落在原来的m-1个样本中,则样本集合加入n;如果不落在原来的m-1个样本中,那么就将随机数加入已取样本集合。这样的算法复杂度为O(m)
证明:对于第n个数,其被选中的概率为1/n+(m-1)/n=m/n;
而对于前面n-1个数中的任意一个数,总被选中的概率P=上一轮被选中的概率+上一轮未被选中的概率*本轮被选中的总概率:
易得P=(m-1)/(n-1)*1+(n-m)/(n-1)*1/(n+1)=m/n.
void floySampling(int n,int m){if(m>n){printf("Error\n");return;}vector<int> q;for(int j=n-m;j<n;++j){int t=rand()%(j+1);++t;vector<int>::iterator t_position=find(q.begin(),q.end(),t);if(t_position==q.end())q.push_back(t);elseq.push_back(j+1);}for(vector<int>::iterator iter=q.begin();iter!=q.end();++iter)printf("%-3d",*iter);cout<<endl;}
3.有百度一到笔试题所联想到的取样方法
百度笔试原题: 为分析用户行为,系统常需存储用户的一些query,但因query非常多,故系统不能全存,设系统每天只存m个query,现设计一个算法,对用户请求的query进行随机选择m个,请给一个方案,使得每个query被抽中的概率相等,并分析之,注意:不到最后一刻,并不知用户的总请求量。
答案策略:取一个[1,m+i]中的随机数,如果随机数落在(m,m+i]时,应该保留原来的m个数;如果随机数落在[1,m]中,则应该用最新的一条记录取代[1,m]中随机的一个数。
证明如下:
1)假设现在系统读取第n+1条记录,现在存储的m条记录都是前面m+n条记录中以m/(m+n)的概率留下来的;
2)取一个[1,m+n+1]的随机数,按照上述策略。
3)现在新记录能保留在m数组的概率为m/(m+n+1)
4)原来m数组中的数(设为A)在本轮选择中还能保留的条件概率(条件是,上一轮选择中,A被保留):
(n+1)/(m+n+1)+m/(m+n+1)*(1-1/m)=(m+n)/(m+n+1)。
然后要乘以其原来保留下的概率。得到的A仍在m数组中的概率为m/(m+n+1)。
如此循环,总是可以保障每个数被选择的概率相等。该算法的复杂度为O(n)
void baiduSampling(int n,int m){if(m>n){printf("Error\n");return;}vector<int> v;for(int i=1;i<=m;++i){v.push_back(i);}for(int i=m+1;i<=n;++i){int t=rand()%(i-1);++t;if(t<=m)v[t-1]=i;}for(vector<int>::iterator iter=v.begin();iter!=v.end();++iter)printf("%-3d",*iter);cout<<endl;}
4.随机顺序序列
有时,我们不仅希望能在n个样本中随机选取m个样本,还希望m个样本的顺序也是随机的。显然,简单插入取样最后得到的序列顺序是随机的。而后面两种方法得到的顺序并不是随机的。Floyd每一轮插入样本中,如果随机数在原来的集合中,最大的数总是被插在容器的最后面,这是造成顺序不随机的主要原因。我们令其插入在随机数的后面。以上只是直观的上的说明。真正严谨的证明请看编程珠玑2的13章。
第三种方法的不随机主要是因为初始的前m个样本序列的不随机造成的。
稍微修改一下floyd算法代码即可得到能生成随机顺序的序列的算法:
void generateRandomSeries(int n,int m){if(m>n){printf("Error\n");return;}deque<int> q;for(int j=n-m;j<n;++j){int t=rand()%(j+1);++t;deque<int>::iterator t_position=find(q.begin(),q.end(),t);if(t_position==q.end())q.push_front(t);elseq.insert(t_position+1,1,j+1);}for(deque<int>::iterator iter=q.begin();iter!=q.end();++iter)printf("%-3d",*iter);cout<<endl;}
- 如何完美地取样?
- 取样
- 如何将Markdown文章轻松地搬运到微信公众号并完美地呈现代码内容
- 如何将Markdown文章轻松地搬运到微信公众号并完美地呈现代码内容
- Mac OS X 10.10优胜美地如何完美接管iphone上的电话和短信
- 瘦瘦告诉你,如何和脂肪完美地死磕到底?
- 落幕,并不完美地结局!
- 如何根据电流的大小来选择取样电阻
- 完美地关闭Socket的注意事项
- 怎么完美地完成编程任务
- 如何得体地说话
- 如何有效率地使用时间
- 如何清晰地思考
- 如何清晰地思考
- 如何高效地绘图
- 如何快速地学习
- 如何清晰地思考
- 如何高效地读书?
- Sublime Text 2 2217 32位和64位破解
- 最大传输长度
- C++使用ADO连接SQLSERVER
- SilverLight中ImageButton控件制作方法
- myeclipse中对齐代码快捷键
- 如何完美地取样?
- VB 捕获 WebBrowser控件的鼠标和键盘事件
- Android 四大组件之一 BroadcastReceiver
- ubuntu 11.10(32位系统)下编译android源码
- 超棒的野生狼摄影作品 - 狼之印象
- HDU-2136(素数问题)
- android的对话框
- [程序员]多些时间思考 少写些代码
- C++ Singleton (单例) 模式