## 基本分析

给定一个随机生成 $1$ ~ $7$ 的函数，要求实现等概率返回 $1$ ~ $10$ 的函数。

首先需要知道，在输出域上进行定量整体偏移，仍然满足等概率，即要实现 $0$ ~ $6$ 随机器，只需要在 `rand7` 的返回值上进行 $-1$ 操作即可。

但输出域的 拼接/叠加 并不满足等概率。例如 `rand7() + rand7()` 会产生 $[2, 14]$ 范围内的数，但每个数并非等概率：

* 产生 $2$ 的概率为：$\frac{1}{7} * \frac{1}{7} = \frac{1}{49}$
* 产生 $4$ 的概率为：$\frac{1}{7} * \frac{1}{7} + \frac{1}{7} * \frac{1}{7} + \frac{1}{7} * \frac{1}{7} = \frac{3}{49}$

在 $[2, 14]$ 这 $13$ 个数里面，等概率的数值不足 $10$ 个。

**因此，你应该知道「执行两次 `rand7()` 相加，将 $[1, 10]$ 范围内的数进行返回，否则一直重试」的做法是错误的。**

---

## $k$ 进制诸位生成 + 拒绝采样

上述做法出现概率分布不均的情况，是因为两次随机值的不同组合「相加」的会出现相同的结果（$(1, 3)$、$(2, 2)$、$(3, 1)$ 最终结果均为 $4$）。

结合每次执行 `rand7` 都可以看作一次独立事件。我们可以将两次 `rand7` 的结果看作生成 $7$ 进制的两位。**从而实现每个数值都唯一对应了一种随机值的组合（等概率），反之亦然。**

举个🌰，设随机执行两次 `rand7` 得到的结果分别是 $4$（第一次）、$7$（第二次），由于我们是要 $7$ 进制的数，因此可以先对 `rand7` 的执行结果进行 $-1$ 操作，将输出域偏移到 $[0, 6]$（仍为等概率），即得到 $3$（第一次）和 $6$（第二次），最终得到的是数值 $(63)_7$，数值 $(63)_7$ 唯一对应了我们的随机值组合方案，反过来随机值组合方案也唯一对应一个 $7$ 进制的数值。

**那么根据「进制转换」的相关知识，如果我们存在一个 `randK` 的函数，对其执行 $n$ 次，我们能够等概率产生 $[0, K^n - 1]$ 范围内的数值。**

回到本题，执行一次 `rand7` 只能产生 $[0, 6]$ 范围内的数值，不足 $10$ 个；而执行 $2$ 次 `rand7` 的话则能产生 $[0, 48]$ 范围内的数值，足够 $10$ 个，且等概率。

我们只需要判定生成的值是否为题意的 $[1, 10]$ 即可，如果是的话直接返回，否则一直重试。

代码：

* []

```Java
class Solution extends SolBase {
    public int rand10() {
        while (true) {
            int ans = (rand7() - 1) * 7 + (rand7() - 1); // 进制转换
            if (1 <= ans && ans <= 10) return ans;
        }
    }
}
```

* 时间复杂度：期望复杂度为 $O(1)$，最坏情况下为 $O(\infty)$
* 空间复杂度：$O(1)$

---

## 进阶

1. 降低对 `rand7` 的调用次数

我们发现，在上述解法中，范围 $[0, 48]$ 中，只有 $[1, 10]$ 范围内的数据会被接受返回，其余情况均被拒绝重试。

为了尽可能少的调用 `rand7` 方法，我们可以从 $[0, 48]$ 中取与 $[1, 10]$ 成倍数关系的数，来进行转换。

我们可以取 $[0, 48]$ 中的 $[1, 40]$ 范围内的数来代指 $[1, 10]$。

首先在 $[0, 48]$ 中取 $[1, 40]$ 仍为等概率，其次形如 $x1$ 的数值有 $4$ 个（$1$、$11$、$21$、$31$），形如 $x2$ 的数值有 $4$ 个（$2$、$12$、$22$、$32$）... 因此最终结果仍为等概率。

代码：

* []

```Java
class Solution extends SolBase {
    public int rand10() {
        while (true) {
            int ans = (rand7() - 1) * 7 + (rand7() - 1); // 进制转换
            if (1 <= ans && ans <= 40) return ans % 10 + 1;
        }
    }
}
```

* 时间复杂度：期望复杂度为 $O(1)$，最坏情况下为 $O(\infty)$
* 空间复杂度：$O(1)$

2. 计算 `rand7` 的期望调用次数

在 $[0, 48]$ 中我们采纳了 $[1, 40]$ 范围内的数值，即以调用两次为基本单位的话，有 $\frac{40}{49}$ 的概率被接受返回（成功）。

成功的概率为 $\frac{40}{49}$，那么触发成功所需次数（期望次数）为其倒数 $\frac{49}{40} = 1.225$，每次会调用两次 `rand7`，因而总的期望调用次数为 $1.225 * 2 = 2.45$ 。

