随机生成器

  1. 在不知道文件总行数的情况下,如何从文件中随机的抽取一行或是k行。
  2. 已知random_m()随机数生成器的范围是[1, m] 求random_n()生成[1, n]范围的函数,m < n && n <= m *m。
  3. 已知一随机发生器,产生0的概率是p,产生1的概率是1-p。现在要你构造一个发生器, 使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;构造一个发生器,使得它构造1、2、3、…n的概率均为1/n,要求复杂度最低。

1、思路:

  蓄水池问题:当n≤k时,序号为n的这个数放入容量为k的容器中,表示该数肯定会被选中;当n>k时,要使得前n个数每个数被选中的概率都是k⁄n。

  证明:假设n≥k,前n个数每个数被选中的概率都是k⁄n,则需要归纳证明前n+1个数每个数被选中的概率是k⁄(n+1)。

  分两个角度去思考这个问题:一是第n+1个被选中的概率,很显然是k⁄(n+1)。二是第1、2、...、n个数被选中的概率也要是k⁄(n+1)。

  后者分析:以第m个数为例(k<m<n+1),它被选中的概率为”m被选中的概率*[m后面元素没有被选中+m后面的元素被选中*但是m没被替换的概率]“。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <ctime>
 4 
 5 using namespace std;
 6 
 7 void SamplePool(int *data, int n, int m)
 8 {
 9     int i, s;
10     srand(time(0));
11     for (i = m; i < n; i++)
12     {
13         s = rand() % i;
14         if (s < m)
15             swap(data[i], data[s]);
16     }
17     for (i = 0; i < m; i++)
18         cout << data[i] << ' ';
19     cout << endl;
20 }
21 
22 int main()
23 {
24     int data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
25     SamplePool(data, 10, 5);
26     return 0;
27 }

 

2、思路:

  假设要求[1,5]随机一个[1,7]。则先用[1,5]随机生成[1,25],再通过拒绝采样定理选取[1,21],其中选21而不选7的原因是提高随机生成的效率。

  首先,将(1,5)之间的随机发生器使用两次,按照五进制进行使用,拼成一个(1,25)的随即发生器既:([gen][gen]),每一[]为一个5进制上的位,换算为十进制为:x=gen*5+gen。在十进制上的范围为:6-30,进行一个简单的左移动,可换算成1-25范围上的值;然后将(1,25)平均分配到7中情况上面,考虑21是7的倍数,因此可以每三个做一个映射(当然,也可以不管,直接截断7后面的数字,但是范围太小,效率不高),ƒ(1~3) = 1,ƒ(4~6) = 2,此时就是等概率的,如果产生了22-25之间的数字,则拒绝采样。

 1 int Rand_M2N(int m, int n)
 2 {
 3     srand(time(0));
 4     int i, s, t, k;  
 5     //k是n的最大倍数,但是小于m*m;t是倍数的量。
 6     for (i = 1; i <= MAX; i++)
 7     {
 8         if (i * n < m * m)
 9         {
10             k = i * n;
11             t = i;
12         }
13         else
14             break;
15     }
16     cout << "k: " << k << endl;
17     //s是被随机选中的数。
18     do
19     {
20         s = m * (rand() % m) + (rand() % m + 1);
21     }while(s > k);  
22     if (s % t == 0)
23         return s / t;
24     else
25         return s / t + 1;
26 }

 

3、思路:

  思考角度1:生成1,2,…n的概率分别是1/n,也就是均等的。那么我们可以想怎么生成一个序列,里面有n个独立事件,概率是相等。而且我们能够猜测到这些概率的形式为px(1-p)y,如果要相等,那么x必须等于y。这样就说明了序列中0和1的个数是相等的。而且这样的情况必须有多于n个情况才行。

  思考角度2:然后是1/n的情况了,我们以5为例,此时我们取x=2,因为C(2x,x)=C(4,2)=6是比5大的最小的x,此时我们就是一次性生成4位二进制,把1出现个数不是2的都丢弃,这时候剩下六个:0011,0101,0110,1001,1010,1100,取最小的5个,即丢弃1100,那么我们对于前5个分别编号1到5,这时候他们的概率都是p*p*(1-p)*(1-p)相等。关键是找那个最小的x,使得C(2x,x)>=n这样能提升查找效率。我之所以取C(2x,x)是为了让同样的序列长度下可用的资源尽量多,因为C(n,i)最大是在i接近n/2的地方取得,此时我有更大比率的序列用于生成,换句话说被抛掉的更少了,这样做是为了避免大量生成了丢弃序列而使得生成速率减慢,其它的没什么特别的意思。

转载于:https://www.cnblogs.com/wangpengjie/p/3168955.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值