rand7 实现 rand10,从基础开始推导并讲讲我的理解

题目

给定方法 rand7 可生成 [1,7] 范围内的均匀随机整数,试写一个方法 rand10 生成 [1,10] 范围内的均匀随机整数。
你只能调用 rand7() 且不能调用其他方法。请不要使用系统的 Math.random() 方法。
每个测试用例将有一个内部参数 n,即你实现的函数 rand10() 在测试时将被调用的次数。请注意,这不是传递给 rand10() 的参数。

基础推导

使用 rand2 实现 rand4

在实现 rand7 之前,我们先看一个简单的例子。

rand2 可以生成 [1, 2] 之间的随机数,那么怎么利用 rand2 实现 rand4?

我们一步步尝试一下:

使用加法

首先我们很容易想到:rand2 + rand2 的方式,这种方式肯定可以得到 4。我们列举一下它的所有可能

rand2rand2rand2 + rand2
111 + 1 = 2
121 + 2 = 3
212 + 1 = 3
222 + 2 = 4

分析一下,可以看出,在保证 rand2 随机的情况下,rand2 + rand2 的生成结果分别是 [2, 3, 3, 4]。

也就是生成 2 的概率是 1 / 4 1/4 1/4,生成 3 的概率是 1 / 2 1/2 1/2,生成 4 的概率是 1 / 4 1/4 1/4

得出结论:首先没有 1,不符合 rand4 接口要求。其次,数据生成的不均匀,不够随机

使用乘法

加法不行,试试乘法,让 rand2 直接 * 2

rand2 * 2,这种方式也可以得到 4。还是一样列举一下所有的可能

rand22rand2 * 2
121 * 1 = 1
121 * 2 = 2

得出结论,没有 3 和 4,缺少一部分需要生成的数据,但是生成出来的数据是均匀的

使用加法和乘法

如果我们结合上述两种情况呢:

rand2rand2rand2 * 2 + rand2
111 * 2 + 1 = 3
121 * 2 + 2 = 4
212 * 2 * 1 = 5
222 * 2 + 2 = 6

得出结论:数据范围不符合要求,但是数据非常均匀,可以均匀地生成 [3, 6],但是生成的上限太大了(有点像 [1, 4] 向右平移了两个单位的感觉),所以我们尝试去缩小,或者说向左平移一下这个区间。

使用加法和乘法,改进版

如果我们使用 (rand2 - 1) * 2 + rand2,来可以缩小整体的值,也就是让整体缩小 2 个单位,我们再列举一下所有情况

rand2rand2(rand2 - 1) * 2 + rand2
11(1 - 1) * 2 + 1 = 1
12(1 - 1) * 2 + 2 = 2
21(2 - 1) * 2 + 1 = 3
22(2 - 1) * 2 + 2 = 4

得出结论:可以看出,非常均匀地生成了 [1, 4] 的数据。可以满足 rand4 的接口需求

这里可以试试一样的方式,利用 rand2 实现 rand6,可以自己推算一下。

根据上面可以得出一个小结论,如果 randX = [1, X],那么可以使用 (randX - 1) * N + randN 来均匀地生成 [1, X * N] 的数据(这个结论暂时看不明白也没事,继续看一下下面的例子会比较清晰)

使用 rand7 实现 rand49

我们使用上面的结论验证一下:

那么代入公式:(rand7 - 1) * 7 + rand7

rand7 = [1, 7],那么
(rand7 - 1) = [0, 6],那么
(rand7 - 1) * 7 = {0, 7, 14, 21, 28, 35, 42}
注意:这个公式的前半部分,也就是 (rand7 - 1) * 7 可以生成的随机数是 {0, 7, 14, 21, 28, 35, 42}
那么再去和后面的 rand7 相加,也就是:
{
	0  + [1, 7],	即 [1, 7]
	7  + [1, 7],	即 [8, 14]
	14 + [1, 7],	即 [15, 21]
	...
	42 + [1, 7],	即 [43, 49]
}
可以看出,上面生成的数据是非常均匀的,并且没有重叠,没有重复。

使用 rand7 和 rand9 实现 rand63

我们换个没那么特殊的例子

那么带入公式:(rand7 - 1) * 9 + rand9

rand7 = [1, 7],那么
(rand7 - 1) = [0, 6],那么
(rand7 - 1) * 9 = {0, 9, 18, 27, 36, 45, 54}
那么再和后面的 rand9 相加,也就是:
{
	0  + [1, 9],	即 [1, 9]
	9  + [1, 9],	即 [10, 18]
	18 + [1, 9],	即 [19, 27]
	...
	54 + [1, 9],	即 [55, 63]
}
可以看出,结果仍然非常均匀,没有重叠重复

回归正传,使用 rand7 实现 rand10

我们可以先使用 rand7 来实现 rand49。然后循环判断是否 <= 10,比如这样

    int rand10() {
        int rand = INT_MAX;
        while (rand > 10) {
            rand = rand49();
        }

        return rand;
    }

    int rand49() {
        return (rand7() - 1) * 7 + rand7();
    }

但是其实这样运行效率会比较低,超过 3 / 4 3/4 3/4 的情况会进入循环。我们可以让他生成 [1, 40] 区间的元素,这样就只会舍弃 [41, 49] 的元素。然后再想办法进行换算 => 比如如果随到 39,那么应该换算成 9,如果是 40,那么应该换算成 10,那么最终就可以得到这样的公式: ( r a n d − 1 ) % 10 + 1 (rand - 1)\%10+1 (rand1)%10+1

最终代码:

class Solution {
public:
    int rand10() {
        int rand = 0;
        while (rand > 10) {
            rand = rand49();
        }

        return ;
    }

    int rand49() {
        return (rand7() - 1) * 7 + rand7();
    }
};
  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

答辣喇叭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值