从5随机到7随机及其扩展
一、题目
给定一个等概率随机产生 1 - 5
的随机函数 rand1To5
。如下:
1 | int rand1To5() { |
除此之外,不能使用任何额外的随机机制,请用 rand1To5 实现等概率随机产生 1-7
的随机函数 rand1To7
二、解析
先解决原问题,具体步骤如下:
rand1To5()
等概率随机产生1,2,3,4,5
rand1To5()-1
等概率随机产生0,1,2,3,4
(rand1To5() - 1) * 5
等概率随机产生0,5,10,15,20
(rand1To5()-1)*5 + (rand1To5()-1)
等概率随机产生0,1,2,3, ... ,23,24
- 如果步骤 4 产生的结果 20,则重复进行步骤 4,知道产生的结果在
0 - 20
之间。同时可以轻易知道出现21 - 24
的概率,会平均分配到0 - 20
上 - 步骤 5 会等概率随机产生
0 - 20
,所以步骤 5 的结果再进行%7
操作,就会等概率的随机产生0 - 6
- 步骤 6 的结果再加 1,就会等概率随机产生
1 - 7
三、代码
1 | public int rand1To5() { |
四、补充问题
给定一个以 p 概率产生 0,以 1-p
概率产生 1 的随机函数 rand01p
如下:
1 | public int rand01p() { |
请用 rand01p
实现等概率随机产生 1-6
的随机函数 rand1To6
分析如下:
rand01p
产生 01 和 10 的概率都是p(1-p)
。所以可以利用这点来实现等概率随机产生 0 和 1 的函数。
1 | public int rand01() { |
rand01()
方法可以等概率随机产生 0 和 1,rand01()*2
等概率随机产生 0 和 2。rand01()*2 + rand01()
等概率随机产生0,1,2,3
1 | public int rand0To3() { |
rand0To3()*4 + rand0To3()
等概率随机产生0,1,2 ... 14,15
。这个就是插空的过程。- 如果步骤 4 产生的结果大于 11,则重复进行步骤 4,直到产生的结果在 0 - 11 之间。那么可以知道出现
12 - 15
的概率会平均分配到0 - 11
上。这就是筛的过程。 - 因为步骤 5 的结果是等概率随机产生
0 - 11
,所以用第 5 步的结果再进行%6
操作,就会等概率随机产生0 - 5
- 第 6 步的结果再加 1,就会等概率随机产生
1 - 6
1 | public int rand1To6() { |
五、进阶问题
进阶问题:给定一个等概率随机产生 1 - M
的随机数 rand1ToM
如下:
1 | public int rand1ToM(int m) { |
除此之外,不能使用任何额外的随机机制。有两个输入参数,分别是 m 和 n,请用 rand1ToM(m)
实现等概率随机产生 1 - n
的随机函数 rand1ToN
我们会发现,只要给定某一个区间上的等概率随机函数,就可以实现任意区间上的随机函数。所以:
- 如果 M 大于等于 N,直接进入如上筛的过程
- 如果 M 小于 N,先进行插空,直到产生比 N 的范围还大的随机范围后,再进入筛的过程。也就是调用 K 次
rand1ToM(m)
,生成有 K 位的 M 进制数,并且产生的范围大于或等于 N。再把范围扩到大于或等于 N 的级别之后,如果真实生成的数大于或等于 N,就忽略,也就是筛的过程。只留下小于或等于 N 的数,那么在0 - N-1
上就可以做到均匀分布。
1 | public int rand1ToM(int m) { |