等概率数字筛选问题
来源:互联网 发布:汽车销量官方数据 编辑:程序博客网 时间:2024/05/22 12:48
如何等概率的从N个元素中选取出K个元素?
从1....n中随机输出m个不重复的数。(迅雷2011.10.21笔试题)
算法1
knuth(int n, int m){ for (int i=0; i<n; i++) { if ( rand_n()<m ) // rand_n()为生成[0,n)这个区间内的随机数 // rand_n()<m的概率就是m/n { cout<<i<<endl; } }}
算法2
knuth(int n, int m){ srand((unsigned int)time(0)); for (int i=0; i<n; i++) {//假设还要从后面n-i个数中选m个元素//那么每个元素被选中的概率为rand()%(n-i)<m//因此每个元素的选中概率是等价的 if ( rand()%(n-i)<m ) { cout<<i<<endl; m--; } }}
如何等概率的从数据流S中选取出K个元素?
由于数据量大小未知,因此无法使用上面的算法。我们能做的仅仅是保证所有已经输入的数据(假设已经有N个了)已k/N的等概率被选出,从而满足需求。算法如下:
S为元素数据流初始化: 初始化一个大小为K的元素空间buffor i =1 to k buf[i] = S[i]for i= k+1 to N{ //第i个元素以 k/i 的概率留下 M=random(1, i);//生成一个1到i的随机数 if( M <= k) buf[M]= S[i]}
原理:
假设当前是i+1, 那么i+1这个元素被选中的概率是k/i+1。考虑前i个元素,如果前i个元素被选中的概率都是k/i+1,则算法满足要求。证明:
a. N<=k 时 元素全保留,留下来的概率 1.
归纳证明:k < i <=N
- 当i=k+1时
第k+1个元素被选中的概率为k/(k+1)。
从K个位置中随机选出一个位置放入新数据。有1/k的概率被选出。
需要该操作的概率是k/(k+1),因此有1/k * k/(k+1) = 1/(k+1)的概率被选出。
故k/(k+1)的概率保留在缓存中。即就是前面i个元素和第i+1个元素等概率的存在于缓存中,出现的概率为k/i。 - 假设当 j=i 的时候结论成立,此时以 k/i 的概率来选择第i个元素,前i-1个元素出现在蓄水池的概率都为k/i。
- 当j=i+1时:
元素j留下的概率为k/j = k/i+1;
从K个缓存中为元素j找一个位置放入K个元素有1/k的概率被替换
因此 本次操作 K个元素被替换的概率为(k/i+1) *(1/k) = 1/(i+1),
本次操作K个元素能保留下来的概率为 1 - 1/(i+1) = i/(i+1
)此前,我们已经从i个元素中等概率k/i的选了k个元素,
经过这次操作这K个元素有i/(i+1)保留
因此有k/i * i/(i+1) = k/(i+1)
综上所述,证明成立。
扰乱一个递增序列。 还未想明白
for i =[0,N) swap(x[i],x[rand(i,n-1)];有人证明,只要扰乱前m个就可以。
void sample_shuf(const int N,const int m){ int i, j; int *x = new int[N]; for(i = 0 ; i <N ; i++) x[i]=i+1;
for(i = 0 ; i < m ; i ++) { j = rand(i,N-1); swap(x[i],x[j]); } sort(x,x+m); Print(x,m); delete []x;
x= NULL;}
关于采样的几个问题:
1、生成[1,5]整数的发生器,如何生成区间在[1,7]的发生器?解答:利用拒绝采样定理
首先,将(1,5)之间的随机发生器使用两次,按照五进制进行使用,拼成一个[0,24]的随即发生器既:([gen -1][gen -1])5,每一[]为一个5进制上的位,换算为十
进制为:x=(gen-1)*5+(gen-1) +1,在十进制上的范围为:1-25; 然后将(1,25)平均分配到7中情况上面,考虑21是7的倍数,因此可以每三个做一个映射(当然,也可以不管,直接截断7后面的数字,但是范围太小,效率不高),1-3--》1,4-6--》2,19-21--》7,此时就是等概率的,如果产生了22-25之间的数字,可以有两种方法决定结果:
(1)拒绝采样,重新再运算
(2)如果得到了22-25之间的数字,则此次的随即发生器结果,直接使用上一次得到的结果。这个方法有人证明过,是等概率的,算法Metropolis Algorithm。
2、Generate a random permutation for a deck of cards
解答:
从后往前,第k步的时候,随机产生一个1 到 k,之间的数字j,然后交换j和k处的数字,可以很容易的最后这个排列就是一个等概率得到的排列。
for k=N:1 j = rand(1,k) swap(j,k)end
同样的,也可以从前往后进行这个过程,不过产生的范围就是变成k-N之间了。
for k = 1:N j = rand(k,N) swap(j,k)end
参考资料
- http://blog.sina.com.cn/s/blog_6344728a0100h6pz.html
- http://blog.csdn.net/v_july_v/article/details/6880698
- http://blog.csdn.net/hackbuteer1/article/details/7971328
- 等概率数字筛选问题
- 等概率数字筛选问题
- 等概率色子问题
- 等概率随机数问题
- 面试中的概率问题 - 等概率方案
- 等概率采样数据流中的数字
- 【算法】等概率问题汇总
- 海量数据等概率随机选取问题
- 等概率生成随机数问题解析
- 等概率选取问题 Random Pick
- 解决等概率随机抽样问题
- 单遍历取等概率随机数问题
- 海量数据等概率选取问题
- 海量数据等概率选取问题
- 蓄水池问题(等概率抽取)详解
- 面试小记-3-等概率输出10000内数字等4道算法题
- 微软等面试100题筛选答案-25-求最长连续数字子串
- 字符串中筛选数字
- SQL实现多行变一行
- hive bucket产生的小文件问题
- WEB7-request
- 年轻人,你何必着急
- 杭电OJ 1083——courses(二分图的匹配问题!匈牙利算法解答)
- 等概率数字筛选问题
- PLSQL查看修改oracle连接数
- 快速排序
- 数据库间数据操作方法汇总
- WEB8-Cookie
- 填报表页面端录入换行问题
- 项目管理过程组图
- 条款8:了解不各种不同意义的new和delete 练习
- sql - 分组取前几条数据 and 几条数据形成一字符串