等概率随机采样问题

1. 输入包含两个整数m和n,其中m<n。输出[0,n-1]内的m个随机数,要求:每个数选择出现的概率相等(也就是m/n),且按序输出。

  依次考虑整数0,1,2,...,n-1,并通过一个适当的随机测试对每个整数进行选择。通过按序访问整数,可以保证输出结果是有序的。 if m=2 and n=5,那么选择的每一个数字的概率都应该是2/5。

  分析过程:在0,1,2,3,4这五个数字中

  第一次遇到0时,它的选择概率应该是2/5,如果选中了,我们开始测试第二个数1,这个时候因为1选中了,所以1这个数字的选中概率就变小了,变成1/4了,有人说这似乎不对吧,因为题目说让每一个数字选中的概率是一样大的,而现在?一个2/5,一个1/4,这怎么行呢?其实不是这样的,认真思考一下就知道了,数字1选中的概率等于什么?

  数字1选中的概率p(1) = 数字0选中的概率 *(1/4)+数组0没选中的概率*(2/4)这样推算下 (2/5 * 1/4) + (3/5 * 2/4) = 8/20 = 2/5

  select = m, remaining = n

  for i in [0,n):

    if (rand() % remaining) < select:

      print i

      select --

    remianing--

  代码遵守的规则应该是要从r个剩余的整数中选出s个,我们以概率s/r选择下一个数。这个概率的选择方式和我们上面证明的是一样的。所以在程序结束的时候一定会打印出m个数字,且每一个数字的被选择概率相同,为m/n。

  这个题目还有其他的解法,将问题定义为蓄水池抽样。先把前k个数放入蓄水池中,对第k+1,我们以k/(k+1)的概率决定是否要把它换入蓄水池,换入时我们可以随机挑选一个作为替换位置,这样一直到样本空间N遍历完,最后蓄水池中留下的就是采样结果。这样的方法得到的结果每一个数字被选择的概率也是k/n。

2. 问题扩展:如何从n个对象(可以以此看到这n个对象,但事先不知道n的值)中随机选择一个?比如在不知道一个文本中有多少行,在这样的情况下要求你随机选择文件中一行,且要求文件的每一行被选择的概率相同。 在知道n这个总对象个数的情况下,谁都知道概率是1/n. 但是我们现在不知道,怎么办呢?

  考虑这样是不是可以,总是以1/i的概率去选择每一次遍历的对象,比如从1,2,3,....,N, 每一次遍历到x时,总是以1/x的概率去选择它。

  总选择第一个行,并以概率1/2选择第二行,以1/3选择第三行,也就是说设结果为result,遍历第一个时result = 1,第二个以1/2的概率替让result = 2,这样一直遍历概率性的替换下去,最终的result就是你的结果。被选择的概率就是1/n。

  第x个数被选择的概率 = x被选择的概率 * (x+1没被选择的概率) * (x+2没有被选择的概率) *......*(N没有被选择的概率)

  被选择的概率 = 1/2  * 2/3 * 3/4 * 4/5 .....* (n-1/n) 我想你知道答案了吧? 对! 是1/n.这样就可以在不知道N的大小的情况下等概率的去选择任意一个对象了!

参考伪代码如下:

i = 0

while more input lines:

  with prob 1.0/++i

  choice = this.line

print choice

3. 已知rand(),以p的概率产生0,以1-p的概率产生1,现在要求设计一个新随机函数newRand(), 使其以1/n的等概率产生1~n之间的任意一个数。
  可以通过已知随机函数rand()产生等概率产生0和1的新随机函数Rand(),然后调用k(k为整数n的二进制表示的位数)次Rand()函数,得到一个长度为k的01序列,以此序列所形成的整数即为1~n之间的数字。
  1):计算整数n的二进制表示所拥有的位数k,k = 1 +log2n(log以2为底n)

  2):调用K次Rand()产生随机数。注意:从产生序列得到的整数有可能大于n,如果大于n的话,则重新产生直至得到的整数不大于n

4. 给定一个函数rand5(),该函数可以随机生成1-5的整数,且概率一样。现要求使用该函数构造函数rand7(),使得可以随机等概率的生成1-7的整数。
  很多人的第一反应是利用rand5() + rand()%3来实现rand7()函数,这个方法确实可以产生1-7之间的随机数,但是数字生成的概率是不相等的。rand()%3 产生0的概率是1/5,而产生1和2的概率都是2/5,所以这个方法产生6和7的概率大于产生5的概率。
  正确的方法是利用rand5()函数生成1-25之间的数字,然后将其中的1-21映射成1-7,丢弃22-25。例如生成(1,1),(1,2),(1,3),则看成rand7()中的1,如果出现剩下的4种,则丢弃重新生成。

  基于,rand()产生[0,N-1],把rand()视为N进制的一位数产生器,那么可以使用rand()*N+rand()来产生2位的N进制数,以此类推,可以产生3位,4位,5位...的N进制数。这种按构造N进制数的方式生成的随机数,必定能保证随机,而相反,借助其他方式来使用rand()产生随机数(如 rand5() + rand()%3 )都是不能保证概率平均的。

题目3:给定一个函数rand()能产生0到n-1之间的等概率随机数,问如何产生0到m-1之间等概率的随机数?

int random(int m , int n)
{
  int k = rand();
  int max = n-1;
  while(k < m)
  {
    k = k*n + rand(); 生成n进制数
    max = max*n + n-1; 生成n进制所能表示的刚好大于等于m的数
  }
  return k/(max/n); 此时k是小于m
}

5. 如何产生如下概率的随机数?0出1次,1出现2次,2出现3次,n-1出现n次?

int random(int size)
{
  while(true)
  {
    int m = rand(size);
    int n = rand(size);
    if(m + n < size)
      return m+n;
  }
}

 采用这种方法保证:

  1 只有 1

  2 可以有 1 1 / 0 2 / 2 0

  3 可以有 1 2 / 2 1 / 0 3 / 3 0

时间: 2024-08-01 22:32:01

等概率随机采样问题的相关文章

随机采样和随机模拟:吉布斯采样Gibbs Sampling的具体实现

http://blog.csdn.net/pipisorry/article/details/51525308 吉布斯采样的实现问题 本文主要说明如何通过吉布斯采样进行文档分类(聚类),当然更复杂的实现可以看看吉布斯采样是如何采样LDA主题分布的[主题模型TopicModel:隐含狄利克雷分布LDA]. 关于吉布斯采样的介绍文章都停止在吉布斯采样的详细描述上,如随机采样和随机模拟:吉布斯采样Gibbs Sampling(why)但并没有说明吉布斯采样到底如何实现的(how)? 也就是具体怎么实现

python内置的一个好玩的函数-zip,并且巧妙的实现按概率随机生成有限个数的字符串。

python有一个比较有意思的内置函数-----zip,可以把传入的两组list进行一个组合变形,再输出子元素为tuple的list,不过变形的方式比较抽象. 举个例子: A=[1,2,3,4,5,6] B=['a','b','c','d'] v1=zip(A,B) v1的结果为: [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] 比较直观的理解就是,A和B是两道拉链上的扣子,生成的list的子tuple,每一组tuple就是一对扣好了的扣子,并且从每组list的

根据数字的出现的概率随机生成

今天的做项目用到了 根据数字出现的概率随机生成,看了一些园子里面大哥们的文章,总结一下, 示例:随机生成一个整数,该整数以50%的几率生成1,以40%的几率生成2,以10%的几率生成3.实现的代码如下: int n5 = r.nextInt(100); int m; //结果数字 if(n5 < 50){ //50个数字的区间,50%的几率 m = 1; }else if(n5 < 90){//[50,90),40个数字的区间,40%的几率 m = 2; }else{ m = 3; } 另外的

利用shuf对数据记录进行随机采样

最近在用SVM为分类器做实验,但是发现数据量太大(2000k条记录)但是训练时间过长...让我足足等了1天的啊!有人指导说可以先进行一下随机采样,再训练,这样对训练结果不会有太大影响(这个待考证).所以就对数据进行了一下降采样,具体方法如下: shuf data | head -n 100000 其中,我的数据是在txt文件中存储的,基本格式是: record 1 xxxxx record 2 xxxxx record 3 xxxxx record 4 xxxxx ........... rec

随机采样和随机模拟:吉布斯采样Gibbs Sampling

http://blog.csdn.net/pipisorry/article/details/51373090 马氏链收敛定理 马氏链定理: 如果一个非周期马氏链具有转移概率矩阵P,且它的任何两个状态是连通的,那么limn→∞Pnij 存在且与i无关,记limn→∞Pnij=π(j), 我们有 limn→∞Pn=???????π(1)π(1)?π(1)?π(2)π(2)?π(2)??????π(j)π(j)?π(j)????????????? π(j)=∑i=0∞π(i)Pij π 是方程 πP

概率随机3(对极限定理的认识)

对于确定的量,比如一个班上的人数,桌子上面有5个苹果之类的.都是比较确定的事情,是已经存在的.概率所研究的是不确定性量和不可准测量,不确定量代表的是没有发生的事情,及随机事件(严格来讲,概率研究的是不确定性中的一种,即在一定结果范围内的不确定性).而不可准测量代表的是虽然量有准确值,但由于技术或者操作因素,无法测得其真实值,比如一支笔的长度,理论上你是不可能测得其真实值的.总结起来有以下三种情况: 1)抛硬币所代表的随机试验.2)样本与总体之间的问题3)近似值与真实值当然,在现实中,以上3类问题

java根据概率随机中奖 测试类

最近要做一个宝箱抽奖的功能 自己先联系了一下,分别用 list和set 进行存储和抽取,做了一个时间消耗的对比,感觉list应该比set读取的要快些,但是这也不一定,希望大家能够共同讨论. 废话少说上代码: package com.test.testRandom; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.u

概率随机奖品

for (int j = 0; j < 100; j++) { List<Prize> prizes = prizeManager.findAll(); double randomNumber; randomNumber = Math.random(); for (int i = 0; i < prizes.size(); i++) { float start = 0; float end = 0; if(i==0){ start = 0; end = (end + prizes.

设计一种结构,在该结构中有如下三个功能:insert(key):将某个key加入到该结构,做到不重复加入。delete(key):将原本在结构中的某个key移除。 getRandom():等概率随机返回结构中的任何一个key。【要求】 Insert、delete和getRandom方法的时间复杂度都是 O(1)

解题思路:创建hashmap,一个存K,数字,另一个存数字,K,每当插入元素时,将元素非别对应插入两个hashmap中目的是能够方变随机查找. public class RandomPool { public static class Pool<K>{ private HashMap<K,Integer> keyIndexMap; private HashMap<Integer,K> indexKeyMap; private int size; public Pool(