[路飞]_用 Rand7() 实现 Rand10()

940 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

leetcode-470 用 Rand7() 实现 Rand10()

题目介绍

给定方法 rand7 可生成 [1,7] 范围内的均匀随机整数,试写一个方法 rand10 生成 [1,10] 范围内的均匀随机整数。

你只能调用 rand7() 且不能调用其他方法。请不要使用系统的 Math.random() 方法。

每个测试用例将有一个内部参数 n,即你实现的函数 rand10() 在测试时将被调用的次数。请注意,这不是传递给 rand10() 的参数。

示例1

输入: 1
输出: [2]

示例2

输入: 2
输出: [2,8]

示例3

输入: 3
输出: [3,8,10]

提示:

  • 1 <= n <= 10^5 进阶:

  • rand7()调用次数的 期望值 是多少 ?

  • 你能否尽量少调用 rand7() ?

解题思路一

首先思考一个问题,rand10 指的是等概率的生成 [1, 10] 中的整数,也就是说,生成每个整数的概率都是 1/10。如果用 rand10 实现 rand7 的话,应该怎么实现?

可以直接用 rand10 生成一个随机数,如果生成的数属于 [1, 7] 就可以了,上面已经说过了,生成每个数的概率都是 1/10,因此符合题目所给的 rand7 的性质。如果生成的数属于 [8, 10],可以重新生成随机数

那么为什么 rand7 不能直接生成 rand10?因为数不够,因此我们的思考方向就是想办法利用 rand7 等概率的生成超过 10 个数

rand7() + rand7() ?

rand7() + rand7() 确实可以生成 13 个数 [2, 14],但是并不是等概率的,看一下下面的表格

+1234567
12345678
23456789
345678910
4567891011
56789101112
678910111213
7891011121314

rand7() * 7 ?

rand7() * 7 会得到 {1, 14, 21, 28, 35, 42, 49},只能等概率生成这几个数中的一个,中间的空白怎么补上?可以用加法,但是 1 加上任意不为 0 的数都将大于 1,因此考虑 (rand7() - 1) * 7

(rand7() - 1) * 7 ?

rand7() - 1 会等概率生成 {0, 1, 2, 3, 4, 5, 6}
(rand7() - 1) * 7 会等概率生成集合A {0, 7, 14, 21, 28, 35, 42},怎么填补空白,加上 rand7

(rand7() - 1) * 7 + rand7() ?

rand7 等概率生成集合B {1, 2, 3, 4, 5, 6, 7}
因为 (rand7() - 1) * 7rand7() 是两个独立事件,根据概率论 P(AB) = P(A) * P(B) = 1/7 * 1/7 = 1/49,因此 (rand7() - 1) * 7 + rand7() 能够等概率生成 [1, 49]

因此可以直接使用 [1, 40]40 个数对 10 取余再加 1,就可以等概率的得到 [1, 10] 中的随机数,如果是 [41, 49] 则重新获取

解题代码一

var rand10 = function() {
    let num = (rand7() - 1) * 7 + rand7()
    while (num > 40){
        num = (rand7() - 1) * 7 + rand7()
    }
    return 1 + num % 10
};

优化

在上面我们将 [41, 49] 直接舍去了,其实 [41, 49] 也是等概率出现的,完全可以看成是 rand9,那么问题就变成了 用 Rand9() 实现 Rand10(),思路和 rand7 一样

var rand10 = function() {
    while(true) {
        let num = (rand7() - 1) * 7 + rand7()
        if (num <= 40) return 1 + num % 10
        // 如果是 [41, 49],再次生成 [1, 63]
        num = (num - 40 - 1) * 7 + rand7()
        if (num <= 60) return 1 + num % 10
    }
};

上述优化之后舍弃了 [61, 63],同理 用 Rand3() 实现 Rand10()

var rand10 = function() {
    while(true) {
        let num = (rand7()-1) * 7 + rand7()
        if (num <= 40) return 1 + num % 10
        // 如果是 [41, 49],再次生成 [1, 63]
        num = (num - 40 - 1) * 7 + rand7()
        if (num <= 60) return 1 + num % 10
        // 如果是 [61, 63],再次生成 [1, 21]
        num = (num - 60 - 1) * 7 + rand7()
        if (num <= 20) return 1 + num % 10
    }
};

解题思路二

思路一已经说明了用 Rand10() 实现 Rand7() 很简单,那么用 Rand7() 实现 Rand5()Rand7() 实现 Rand2() 也很简单

rand5 会等概率生成 {1, 2, 3, 4, 5}rand2 会等概率生成 {1, 2},如果 rand2 生成 2 时,rand5() + 5rand2 生成 1 时,rand5 不变,那么就等概率得到 [1, 10],实现了 rand10

解题代码二

var rand10 = function() {
    let p = rand7(), q = rand7()
    // p 实现 rand5
    while (p > 5) p = rand7()
    while (q === 7) q = rand7()
    // q 大于 3 的概率是 1/2,可以看成 rand2
    return q > 3 ? p : p + 5
};