#### 解题思路

##### 从 rand10() 到 rand7()

如果题目是给你 $rand10()$，让你生成 $1～7$ 之间的某个数，那非常好办，我们只要不断调用 $rand10()$ 即可，直到得到我们要的数，但是为什么可以呢？你可能会怀疑这个是不是等概率的，我们来计算一下
- 如果第一次就 $rand$ 到 $1～7$ 之间的数，那就是直接命中了，概率为 $\frac{1}{10}$

- 如果第二次命中,那么第一次必定没命中，没命中的概率为 $\frac{3}{10}$ ，再乘命中的概率 $\frac{1}{10}$ ，所以第二次命中的概率是 $\frac{1}{10}*\frac{3}{10}$

依次类推，我们求和，可以得到如下结果，可以知道，从 $rand10()$ 到 $rand7()$ 它是等概率的

![image.png](https://pic.leetcode-cn.com/13662225e7f9704ff4475d2a539c7228028ec61d3762f94fb833d29fb237c808-image.png)

##### 从 rand7() 到 rand10()

现在要从 $rand7()$ 到 $rand10()$，也要求是等概率的，那只要我们把小的数映射到一个大的数就好办了，那首先想到的办法是乘个两倍试一试，每个 $rand7()$ 它能生成数的范围是 $1～7$，$rand$ 两次，那么数的范围就变为 $2～14$，哦，你可能发现没有 $1$ 了，想要再减去个 $1$ 来弥补，$rand7()\ +\ rand7() - \ 1$，其实这样是错误的做法，因为对于数字 $5$ 这种，你有两种组合方式 $(2+3\ or\ 3+2)$，而对于 $14$，你只有一种组合方式$(7+7)$，它并不是等概率的，那么简单的加减法不能使用，因为它会使得概率不一致，我们的方法是利用乘法，一般思路如下面这样构建：

$\qquad\qquad\qquad\qquad\qquad\qquad\qquad (rand7() - 1)*7+rand7()$

- 首先 $rand7()-1$ 得到的数的集合为 $\left\{ 0,1,2,3,4,5,6 \right\}$

- 再乘 $7$ 后得到的集合 $A$ 为 $\left\{ 0，7，14，21，28，35，42\right\}$

- 后面 $rand7()$ 得到的集合B为 $\left\{ 1,2,3,4,5,6,7\right\}$

有人可能会疑惑，你咋不乘 $6$，乘 $5$ 呢？因为它不是等概率生成，只有乘 $7$ 才能使得结果是等概率生成的，啥意思？我们得到的集合 $A$ 和集合 $B$，利用这两个集合，得到的数的范围是 $1～49$，每个数它显然是等概率出现的，因为这两个事件是独立事件，如果你不懂什么是独立事件，你试着加加看也能体会一点。

$\qquad\qquad\qquad\qquad\qquad\qquad\qquad P(AB) = P(A)*P(B)=$$$\frac{1}{7}\ *\ \frac{1}{7}$

那么我们可以写出下面的代码

* []

```Java
class Solution extends SolBase {
    public int rand10() {
        // 首先得到一个数
        int num = (rand7() - 1) * 7 + rand7();
        // 只要它还大于10，那就给我不断生成，因为我只要范围在1-10的，最后直接返回就可以了
        while (num > 10){
            num = (rand7() - 1) * 7 + rand7();
        }
        return num;
    }
}
```

提交发现跑的很慢
![image.png](https://pic.leetcode-cn.com/6b845e4c71c5d187df9b3be82953a97c207e812800cffea06231708e9a499a38-image.png)

我们来想想为什么

这样的一个问题是，我们的函数会得到 $1～49$ 之间的数，而我们只想得到 $1～10$ 之间的数，这一部分占的比例太少了，简而言之，这样效率太低，太慢，可能要 $while$ 循环很多次，那么解决思路就是舍弃一部分数，舍弃 $41～49$，因为是独立事件，我们生成的 $1～40$ 之间的数它是等概率的，我们最后完全可以利用 $1～40$ 之间的数来得到 $1～10$ 之间的数。所以，我们的代码可以改成下面这样

* []

```Java
public int rand10() {
    // 首先得到一个数
    int num = (rand7() - 1) * 7 + rand7();
    // 只要它还大于40，那你就给我不断生成吧
    while (num > 40)
        num = (rand7() - 1) * 7 + rand7();
    // 返回结果，+1是为了解决 40%10为0的情况
    return 1 + num % 10;
}
```

![image.png](https://pic.leetcode-cn.com/8582eef1938e2d3e75e7e1717ab61871b180551b7b9b665cc2fe26207caec44c-image.png)

##### 再优化

更进一步，这时候我们舍弃了 $9$ 个数，舍弃的还是有点多，效率还是不高，怎么提高效率呢？那就是舍弃的数最好再少一点！因为这样能让 $while$ 循环少转几次，那么对于大于 $40$ 的随机数，别舍弃呀，利用这 $9$ 个数，再利用那个公式操作一下：

$\qquad\qquad\qquad\qquad\qquad\qquad\qquad (大于40的随机数 - 40 - 1) * 7 + rand7()$

这样我们可以得到 $1-63$ 之间的随机数，只要舍弃 $3$ 个即可，那对于这 $3$ 个舍弃的，还可以再来一轮：

$\qquad\qquad\qquad\qquad\qquad\qquad\qquad (大于60的随机数 - 60 - 1) * 7 + rand7()$

这样我们可以得到 $1-21$ 之间的随机数，只要舍弃 $1$ 个即可。

* []

```Java
/**
 * The rand7() API is already defined in the parent class SolBase.
 * public int rand7();
 * @return a random integer in the range 1 to 7
 */
class Solution extends SolBase {
    public int rand10() {
        while (true){
            int num = (rand7() - 1) * 7 + rand7();
            // 如果在40以内，那就直接返回
            if(num <= 40) return 1 + num % 10;
            // 说明刚才生成的在41-49之间，利用随机数再操作一遍
            num = (num - 40 - 1) * 7 + rand7();
            if(num <= 60) return 1 + num % 10;
            // 说明刚才生成的在61-63之间，利用随机数再操作一遍
            num = (num - 60 - 1) * 7 + rand7();
            if(num <= 20) return 1 + num % 10;

        }
    }
}
```

提交发现，本来是9ms，结果只要6ms了～

![image.png](https://pic.leetcode-cn.com/864308cff322f7d32a9b3b44dc89c2c4ec4ebc418f4f5619b1697677e612da65-image.png)

希望我的题解对您有帮助～～
