# 470. Implement Rand10() Using Rand7()

**<font color=red>难度: Medium</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/implement-rand10-using-rand7/

> 内容描述

```
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()?
```

## 解题方案

> 思路 1
******- 时间复杂度: O(1) or O(infinity)******- 空间复杂度: O(1)******

![](https://github.com/apachecn/awesome-algorithm/blob/master/images/470/image.png)

Average `2*49/40 = 2.45` Call rand7 Per rand10

beats 66.53%

```python
class Solution:
    def rand10(self):
        """
        :rtype: int
        """
        res = (rand7() - 1) * 7 + (rand7() - 1)
        while res >= 40:
            res = (rand7() - 1) * 7 + (rand7() - 1)
        return res % 10 + 1
```


> 思路 2
******- 时间复杂度: O(1)******- 空间复杂度: O(1)******

然后就是更优的一个思路，见[lee215](https://leetcode.com/problems/implement-rand10-using-rand7/discuss/151567/C%2B%2BJavaPython-Average-1.199-Call-rand7-Per-rand10)

Average 1.199 Call rand7 Per rand10

```
alexcheen says:

I think, rand7() gives log2(7) bits information, 
and rand10() need log2(10) bits information. so, in theory, it need call rand7() at least log7(10) times.
```

```python
class Solution:
    def rand10(self):
        """
        :rtype: int
        """ 
        self.cache = []
        while not self.cache: 
            self.generate()
        return self.cache.pop()

    def generate(self):
        n = 19  # 1.199
        cur = sum((rand7() - 1) * (7**i) for i in range(n))
        rang = 7 ** n
        while cur < rang // 10 * 10:
            self.cache.append(cur % 10 + 1)
            cur //= 10
            rang //= 10
```


## Follow up 1
Suppose we are given randM() which generates [1, 2, ..., M], we want to generate randN() which generates [1, 2, ..., N].
What would you do?


参考[herokillerever](https://leetcode.com/problems/implement-rand10-using-rand7/discuss/151045/Generalize-implement-RandM()-using-RandN())

- if N < M, then we can select 1, 2, ..., N, N+1, N+2, ..., M from randM(), as we know that [1, 2, ..., N] each with prob 1/M, since the algorithm terminates when the generated number less than N+1. Hence we have the uniform distribution.
- M < N < M^2, then we can construct randM^2() = M * (randM()-1) + randM(), still we can select 1, 2, M, ..., N, N+1, N+2, ..., M^2, as we know that [1, 2, ..., N] each with prob 1/M^2, since the algorithm terminates when the generated number less than N+1. Hence we have the uniform distribution.
- ......
- The idea is that we can construct randM^n = M * (randM^(n-1)-1) + randM^(n-1) recursively. And then select accordingly.

Average 1.199 Call rand7 Per rand10

```python
class Solution(object):
    def rand10(self):
        """
        :rtype: int
        """
        res = 11
        while res > 10:
            res = (rand7() - 1) * 7 + rand7()
        return res
```



## Follow up 2
What if we have a rand(N) function which will generate [0, N] uniformly, and we want to return `0` 1s, `1` 2s, ... , `N-1` Ns?


```python
def get():
    a = rand(N)
    b = rand(N)
    if a + b < N:
        return a + b
```





































