题目
Given a function rand7 which generates a uniform random integer in the range 1 to 7, write a function rand10 which generates a uniform random integer in the range 1 to 10.
Do NOT use system’s Math.random().
Example 1:
Input: 1
Output: [7]
Example 2:
Input: 2
Output: [8,4]
Example 3:
Input: 3
Output: [8,1,10]
Note:
- rand7 is predefined.
- Each testcase has one argument: n, the number of times that rand10 is called.
Follow up:
- What is the expected value for the number of calls to rand7() function?
- Could you minimize the number of calls to rand7()?
思路
这个问题的本质是基于一个整形的随机方法实现另一个整形随机方法(问题A),这个问题乍一看跟基于一个概率分布得到另个一概率分布(问题B)这个问题很像,但其实不一样。问题B在大多数情况下可以通过线性方法来做个转换即可得到。但是在整形随机方法的基础之上通过线性转换(比如rand7() * 10 / 7),是无法得到一个均匀的整形随机方法rand10()的。原因的话可以把所有的情况列出来就知道了。
可以思考另一个更简单的问题: 已知rand2()方法,如何能够得到rand4()方法。
rand2()方法的结果和概率如下表:
result | 1 | 2 |
---|---|---|
property | 1/2 | 1/2 |
期望得到的rand4()方法的结果和概率如下表:
result | 1 | 2 | 3 | 4 |
---|---|---|---|---|
property | 1/4 | 1/4 | 1/4 | 1/4 |
从这两个表,就可以想到一个简单的得到1/4概率的方法:rand2() + rand2()。这个方法的结果和概率如下表:
result | 2 | 3 | 4 |
---|---|---|---|
property | 1/4 | 1/2 | 1/4 |
虽然没有4个1/4,但是可以通过如下逻辑判断即可得到:
int a = rand2(), b = rand2();
if ((a + b) == 2)
return 1;
else if ((a + b) == 4)
return 4;
else if (a == 1)
return 2;
else
return 3;
根据rand2()获取rand4()很容易,因为两个rand2()的联合概率可以被均匀分开。下一个问题可以考虑如何根据rand2()获取rand3()。
因为2和3互质的关系,无法通过若干个rand2()的联合概率来均分分成三份,因为2^ n mod 3 永远不为0。所以需要考虑另一种近似的解法:
1. 构造一个能均匀分成大于3份的随机方法;
2. 当这个随机方法的结果位于第1、2、3种情况时,分别返回1,2,3;
3. 当这个随机方法得结果位于第1、2、3种情况以外时,重新获取这个随机方法的结果。
很自然的,我们通过rand2()得到了rand4(),那使用rand4()来构造rand3()就可以得到如下代码:
int a = rand4();
while (true) {
if (a <= 3)
return a;
a = rand4();
}
这个方法是我第一直觉想到的,还需要证明rand3()的返回值的概率均为1/3。但是还需要证明,从直观上看,1,2,3这三个值每个值能返回的概率都是一致的。比如返回1的概率为:
1. 1次循环返回1的概率为 1/4;
2. 2次循环返回1的概率为 (1/4)^2;
3. …
4. n次循环返回1的概率为(1/4)^n;
归纳即为: P(1) = 1/4 + (1/4)^2 + (1/4)^n;
同理,对于2和3的概率是一样的;综上,我们获得了rand3();
代码上,抛弃rand4()这个临时方法,可以得到:
int sum = 0, a = rand2(), b = rand2();
while (true) {
sum = a + b;
if (sum != 3)
return sum - 1;
else if (a == 1)
return 3;
a = rand2();
b = rand2();
}
简单问题解决了,现在看根据rand7()到rand10()的话,同样的道理,用将两个rand7()的联合随机方法,该方法返回结果的最小概率为1/49,可以用如下逻辑:
a = rand7(), b = rand7();
1. a == 1 && b <= 4 ⇒ return 1;
2. a == 1 && b <= 4 ⇒ return 2;
3. …
4. a == 7 && b <= 4 ⇒ return 7;
5. b == 5 && a <= 4 ⇒ return 8;
6. …
7. b == 7 && a <= 4 ⇒ return 10;
8. 不满足如上条件,则再重新获取a和b。
代码如下:
Code in C++
// The rand7() API is already defined for you.
// int rand7();
// @return a random integer in the range 1 to 7
class Solution {
public:
int rand10() {
int a = rand7(), b = rand7();
while (true) {
if (b <= 4)
return a;
else if (a <= 4)
return b + 3;
a = rand7();
b = rand7();
}
}
};
性能
通过的数量不多,无性能展示。
优化方案
- 暂时没想到