ZT等概率随机函数问题

Posted bloomingflower

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZT等概率随机函数问题相关的知识,希望对你有一定的参考价值。

1. 基础问题 和 解决办法。


已知: 有一个随机函数 rand_0_and_1_with_p(), 这个随机数生成器,它能够以概率 p 产生0,以概率 (1 - p) 产生1。

要求:使用这个随机函数,设计一个新的随机函数要求以等概率生成0和1。

 

解决办法:组合问题类型:可以两次调用 该 随机函数。 运行函数 rand_0_and_1_with_p() 一次,可以得到 P(0) = p, P(1) = 1 - p。那么运行该函数两次,会生成两个数字,P (0 and 1) = p(1 - p),P(1 and 0) = (1 - p)p。这样就出现了等概率。所以实现如下:

 

 1 int rand_0_and_1_with_equal_prob() {
 2 int tmp1 = rand_0_and_1_with_p();
 3 int tmp2 = rand_0_and_1_with_p();
 4 if (tmp1 == 0 && tmp2 == 1) {
 5 return 0;
 6 } else if (tmp1 == 1 && tmp2 == 0) {
 7 return 1;
 8 } else {
 9 return rand_0_and_1_with_equal_prob();
10 }
11 
12 }

 

 

2. 问题 升级版!
已知: 有一个随机函数 rand_0_and_1_with_p(), 这个随机数生成器,它能够以概率 p 产生0,以概率 (1

求解: 使用这个随机函数 rand_0_and_1_with_p(),设计一个新的随机函数,要求以等概率产生 1 到 n 之间的随机数。

分析: 我们现在已经有了 (0, 1)的等概率随机数生成器。给定一个n,我们不妨将 (0, 1)等概率随机数生成器生成的0, 1 看作是二进制。 那么我们多云行几次,就可以生成一个二进制数字,如果这个二进制数字有 Log2(n) 个比特位。就可以了。

代码如下:

 

int rand_0_to_n_minus_1_with_equal_prob(int n) {
int k = 0;
while(n) {
k++;
n = n >> 1;
}
do {
int res = 0;
for(int i = 0; i < k; i++){
res |= rand_0_and_1_with_equal_prob() << i;
}
} while(res >= n)

return res;
}

 

 

这里要注意,可能生成的数字会超过n,此时这里要舍弃这次随机数生成,重新再来一次。
3. 问题升级版 2!
问题: 给定函数 rand5() ,它能生成 1~5 之间的随机数字。
求解: 要求据此实现 rand7(),能产生1~7 之间的随机数字。
分析:
解法1:我们可以参照问题2解法,首先由 rand5() 生成 (0,1)等概率随机数生成器。然后由这个 (0, 1)等概率生成器生成 1~7 之间的等概率生成器。也就是 rand5()。

解法2:这个题目个人感觉非常棒,可以从题2中获得一定的灵感,题2中是将n表示成2进制,那是因为已知的随机函数是产生0和1的,对于该题,一直的随机函数是随机产生1~5的,我们可以很容易的将该函数转化成随机产生0~4,然后再将7表示成5进制的数,则1=015, 2=025, 3=035, 4=045, 5=105, 6=115, 7=125。不过这里我们同样是生成所有两位的五进制数,那么最高是445,即24,然后去掉21,22,23,24剩下的21个数0~20模7正好可以等概率生成0~6,然后加1即可。代码如下:

代码如下:

 

1 int rand7() {
2 do {    
3 int x = rand5() - 1;
4 int y = rand5() - 1;
5 int res = x * 5 + y;
6 } while(res > 20)
7 return (res % 7 ) + 1
8 }

 

 

4. 问题升级版 v3!


已知: rand_n() 可以产生 0 ~ n-1 之间的随机数

求解: 实现 rand_m() 产生 0~m-1 之间的随机数。

 

实现:如果 m <= n,那么直接生成即可。 如果m > n,将m用 <n进制> 数字表示,然后进行求解。

(起始就是问题3的思路扩展)

代码如下:

 

int rand_m() {
int res = 0;
if(m <= n) {
do {
res = rand_n();
} while(res >= m)
return res;
}
int count = 1;
int tmp = n - 1;
while(tmp < m) {
tmp = tmp * n + n - 1;
count ++;
}
int times = (tmp / m) * m;
do {
res = rand_n();
for(int i = 0; i < count ;i++)
res = res * n + rand_n();
} while(res >= times)
return res % m;
}

 

 

5. 问题升级版 V4!
问题: 如何得到可以产生如下概率的随机数? 0出现1次,1出现2次,2出现3次,n-1出现n次。

分析: 我们注意到有如下规律:n - 1 = (n - 1) + 0 = (n - 2) + 1 = (n - 3) + 2 = ... = 2 + (n - 3) = 1 + (n - 2) = 0 + (n - 1)
可以发现,满足a + b = n - i的(a, b)数对的个数为n - i + 1个。所以我们得到如下代码:

 

int Rand(int n) {
while(1) {
int tmp1 = rand() % n;
int tmp2 = rand() % n;
if(tmp1 + tmp2 < n) {
return tmp1 + tmp2;
}
}
}

 

 


二. 随机数 范围扩展。

已知有个rand7()的函数,返回1到7随机自然数,让利用这个rand7()构造rand10() 随机1~10。
分析:要保证rand10()在整数1-10的均匀分布,可以构造一个1-10*n的均匀分布的随机整数区间(n为任何正整数)。假设x是这个1-10*n区间上的一个随机整数,那么x%10+1就是均匀分布在1-10区间上的整数。由于(rand7()-1)*7+rand7()可以构造出均匀分布在1-49的随机数(原因见下面的说明),可以将41~49这样的随机数剔除掉,得到的数1-40仍然是均匀分布在1-40的,这是因为每个数都可以看成一个独立事件。
下面说明为什么(rand7()-1)*7+rand7()可以构造出均匀分布在1-49的随机数:
首先rand7()-1得到一个离散整数集合{0,1,2,3,4,5,6},其中每个整数的出现概率都是1/7。那么(rand7()-1)*7得到一个离散整数集合A={0,7,14,21,28,35,42},其中每个整数的出现概率也都是1/7。而rand7()得到的集合B={1,2,3,4,5,6,7}中每个整数出现的概率也是1/7。显然集合A和B中任何两个元素组合可以与1-49之间的一个整数一一对应,也就是说1-49之间的任何一个数,可以唯一确定A和B中两个元素的一种组合方式,反过来也成立。由于A和B中元素可以看成是独立事件,根据独立事件的概率公式P(AB)=P(A)P(B),得到每个组合的概率是1/7*1/7=1/49。因此(rand7()-1)*7+rand7()生成的整数均匀分布在1-49之间,每个数的概率都是1/49。
程序:

 

1 int rand_10() 
2 { 
3 int x = 0; 
4 do 
5 { 
6 x = 7 * (rand7() - 1) + rand7(); 
7 }while(x > 40); 
8 return x % 10 + 1; 
9 }

 

 

注:由朋友问为什么用while(x>40)而不用while(x>10)呢?原因是如果用while(x>10)则有40/49的概率需要循环while,很有可能死循环了。
问题描述
已知random3()这个随机数产生器生成[1, 3]范围的随机数,请用random3()构造random5()函数,生成[1, 5]的随机数?
问题分析
如何从[1-3]范围的数构造更大范围的数呢?同时满足这个更大范围的数出现概率是相同的,可以想到的运算包括两种:加法和乘法
考虑下面的表达式:
3 * (random3() – 1) + random3();
可以计算得到上述表达式的范围是[1, 9] 而且数的出现概率是相同的,即1/9
下面考虑如何从[1, 9]范围的数生成[1, 5]的数呢?
可以想到的方法就是 rejection sampling 方法,即生成[1, 9]的随机数,如果数的范围不在[1, 5]内,则重新取样
解决方法

 

1 int random5() 
2 { 
3 int val = 0; 
4 do 
5 { 
6 val = 3 * (random3() - 1) + random3(); 
7 }while(val > 5); 
8 return val; 
9 }

 



归纳总结
将这个问题进一步抽象,已知random_m()随机数生成器的范围是[1, m] 求random_n()生成[1, n]范围的函数,m < n && n <= m *m
一般解法:

 

int random_n() 
{ 
int val = 0; 
int t; //t为n的最大倍数,且满足t<m*m 
do 
{ 
val = m * (random_m() - 1) + random_m(); 
}while(val > t); 
return val; 
}

 






























以上是关于ZT等概率随机函数问题的主要内容,如果未能解决你的问题,请参考以下文章

Java算法 -- 选择排序冒泡排序插入排序前缀和数组Java中的Math.random()函数01不等概率随机到01等概率随机从[1,5]随机到[1,7]随机对数器的使用

Java算法 -- 选择排序冒泡排序插入排序前缀和数组Java中的Math.random()函数01不等概率随机到01等概率随机从[1,5]随机到[1,7]随机对数器的使用

Java算法 -- 选择排序冒泡排序插入排序前缀和数组Java中的Math.random()函数01不等概率随机到01等概率随机从[1,5]随机到[1,7]随机对数器的使用

10,随机等概率的输出m个不重复的数

多臂强盗:当随机概率低于分配给强盗的成功概率时,为什么我们将奖励增加1

R语言基础-统计函数