impl Solution {
// 学习一下拒绝采样:
// 每一步都列出算式得出的数字分布情况, 可以加深理解
pub fn rand10() -> i32 {
// 首先我们清晰一下目标分布rand10(): {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 然后我们看看手中的rand7()能产生的分布: {1, 2, 3, 4, 5, 6, 7}
// 1. 我们手中能产生的分布范围是小于目标范围的, 所以我们需要尝试拓展
// 能等概率的拓展分布的操作有 '数乘' 与 '加'
// 我们试试数乘:
// 1 * rand7() = {1, 2, 3, 4, 5, 6, 7}
// 2 * rand7() = {2, 4, 6, 8, 10, 12, 14}
// 3 * rand7() = {3, 6, 9, 12, 15, 18, 21}
// 4 * rand7() = {4, 8, 12, 16, 20, 24, 28}
// 5 * rand7() = {5, 10, 15, 20, 25, 30, 35}
// 6 * rand7() = {6, 12, 18, 24, 30, 36, 42}
// 7 * rand7() = {7, 14, 21, 28, 35, 42, 49} <-- 等会儿会使用这个
// 8 * rand7() = {8, 16, 24, 32, 40, 48, 56}
// ...
// 这里就有感觉啦, 为了能够产生无空的分布, 我们可以用 7 * rand7() - (rand7() - 1)
// rand7() - 1 = {0, 1, 2, 3, 4, 5, 6}
// 7 * rand7() - (rand7() - 1) = {1, 2, 3, 4, 5, ..., 48, 49}
// 这样我们就有了一个[1, 49]的随机数生成器
// 因为采用拒绝采样的思想, 首先我们进入一个无限循环, 当得到结果就退出, 如果得不到就一直循环
loop {
let range_1_to_49 = 7 * rand7() - (rand7() - 1);
if range_1_to_49 <= 40 {
// 因为我们要1..10的等概率分布, 所以我们只需要前40个数字, 拒绝剩下的9个
// 这里的range_1_to_49 = {1, 2, 3, 4, ..., 39, 40},
// range_1_to_49 - 1 = {0, 1, 2, 3, ..., 38, 39},
// (range_1_to_49 - 1) % 10 = {0, 1, 2, 3, ..., 8, 9}
// (range_1_to_49 - 1) % 10 + 1 = {1, 2, 3, ..., 9, 10} 即为rand10()
return (range_1_to_49 - 1) % 10 + 1;
}
// 到这里说明我们的range_1_to_49 = {41, 42, 43, 44, 45, 46, 47, 48, 49}
// 我们也不要浪费它们, 用它们我们可以得到一个[1, 9]的等概率分布
let range_1_to_9 = range_1_to_49 - 40;
// 我们可以用之前的思想, 使用9 * rand7()得到一个空隙为9的分布, 再把这个[1, 9]的分布插进去
// 9 * rand7() = {9, 18, 27, 36, 45, 54, 63}
// range_1_to_9 - 1 = {0, 1, 2, 3, 4, 5, 6, 7, 8}
// 9 * rand7() - (range_1_to_9 - 1) = {1, 2, 3, 4, 5, ..., 62, 63}
let range_1_to_63 = 9 * rand7() - (range_1_to_9 - 1);
if range_1_to_63 <= 60 {
// 类似之前的
// 因为我们要1..10的等概率分布, 所以我们只需要前60个数字, 拒绝剩下的3个
// 这里的range_1_to_63 = {1, 2, 3, 4, ..., 59, 60},
// range_1_to_63 - 1 = {0, 1, 2, 3, ..., 58, 59},
// (range_1_to_63 - 1) % 10 = {0, 1, 2, 3, ..., 8, 9}
// (range_1_to_63 - 1) % 10 + 1 = {1, 2, 3, ..., 9, 10} 即为rand10()
return (range_1_to_63 - 1) % 10 + 1;
}
// 走到这里说明range_1_to_63 = {61, 62, 63}
// 我们也不要浪费它们, 用它们我们可以得到一个[1, 3]的等概率分布
let range_1_to_3 = range_1_to_63 - 60;
// 我们可以用之前的思想, 使用3 * rand7()得到一个空隙为3的分布, 再把这个[1, 3]的分布插进去
// 3 * rand7() = {3, 6, 9, 12, 15, 18, 21}
// range_1_to_3 - 1 = {0, 1, 2}
// 3 * rand7() - (range_1_to_3 - 1) = {1, 2, 3, 4, 5, ..., 20, 21};
let range_1_to_21 = 3 * rand7() - (range_1_to_3 - 1);
if range_1_to_21 <= 20 {
// 类似之前的
// 因为我们要1..10的等概率分布, 所以我们只需要前20个数字, 拒绝剩下的1个
// 这里的range_1_to_21 = {1, 2, 3, 4, ..., 19, 20},
// range_1_to_21 - 1 = {0, 1, 2, 3, ..., 18 19},
// (range_1_to_21 - 1) % 10 = {0, 1, 2, 3, ..., 8, 9}
// (range_1_to_21 - 1) % 10 + 1 = {1, 2, 3, ..., 9, 10} 即为rand10()
return (range_1_to_21 - 1) % 10 + 1;
}
// 走到这里说明range_1_to_21 = {21}
// 我们没法使用它了
}
-1
}
}