From 834df726223cc86c57533c46a7bc716b0c939ad4 Mon Sep 17 00:00:00 2001 From: AC_Oier Date: Sun, 5 Sep 2021 10:53:27 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feat:=20Add=20470?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Index/\346\225\260\345\255\246.md" | 1 + ...10\344\270\255\347\255\211\357\274\211.md" | 136 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 "LeetCode/461-470/470. \347\224\250 Rand7() \345\256\236\347\216\260 Rand10()\357\274\210\344\270\255\347\255\211\357\274\211.md" diff --git "a/Index/\346\225\260\345\255\246.md" "b/Index/\346\225\260\345\255\246.md" index dcb1e3fc..308f98ff 100644 --- "a/Index/\346\225\260\345\255\246.md" +++ "b/Index/\346\225\260\345\255\246.md" @@ -15,6 +15,7 @@ | [313. 超级丑数](https://leetcode-cn.com/problems/super-ugly-number/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/super-ugly-number/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-jyow/) | 中等 | 🤩🤩🤩 | | [342. 4的幂](https://leetcode-cn.com/problems/power-of-four/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/power-of-four/solution/gong-shui-san-xie-zhuan-hua-wei-2-de-mi-y21lq/) | 简单 | 🤩🤩🤩 | | [446. 等差数列划分 II - 子序列](https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence/solution/gong-shui-san-xie-xiang-jie-ru-he-fen-xi-ykvk/) | 困难 | 🤩🤩🤩🤩🤩 | +| [470. 用 Rand7() 实现 Rand10()](https://leetcode-cn.com/problems/implement-rand10-using-rand7/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/implement-rand10-using-rand7/solution/gong-shui-san-xie-k-jin-zhi-zhu-wei-shen-zmd4/) | 中等 | 🤩🤩🤩🤩 | | [477. 汉明距离总和](https://leetcode-cn.com/problems/total-hamming-distance/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/total-hamming-distance/solution/gong-shui-san-xie-ying-yong-cheng-fa-yua-g21t/) | 简单 | 🤩🤩🤩 | | [483. 最小好进制](https://leetcode-cn.com/problems/smallest-good-base/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/smallest-good-base/solution/gong-shui-san-xie-xiang-jie-ru-he-fen-xi-r94g/) | 困难 | 🤩🤩🤩🤩 | | [523. 连续的子数组和](https://leetcode-cn.com/problems/continuous-subarray-sum/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/continuous-subarray-sum/solution/gong-shui-san-xie-tuo-zhan-wei-qiu-fang-1juse/) | 中等 | 🤩🤩🤩🤩 | diff --git "a/LeetCode/461-470/470. \347\224\250 Rand7() \345\256\236\347\216\260 Rand10()\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/461-470/470. \347\224\250 Rand7() \345\256\236\347\216\260 Rand10()\357\274\210\344\270\255\347\255\211\357\274\211.md" new file mode 100644 index 00000000..6685531c --- /dev/null +++ "b/LeetCode/461-470/470. \347\224\250 Rand7() \345\256\236\347\216\260 Rand10()\357\274\210\344\270\255\347\255\211\357\274\211.md" @@ -0,0 +1,136 @@ +### 题目描述 + +这是 LeetCode 上的 **[470. 用 Rand7() 实现 Rand10()](https://leetcode-cn.com/problems/implement-rand10-using-rand7/solution/gong-shui-san-xie-k-jin-zhi-zhu-wei-shen-zmd4/)** ,难度为 **中等**。 + +Tag : 「位运算」、「数学」 + + + +已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。 + +不要使用系统的 Math.random() 方法。 + +示例 1: +``` +输入: 1 + +输出: [7] +``` +示例 2: +``` +输入: 2 + +输出: [8,4] +``` +示例 3: +``` +输入: 3 + +输出: [8,1,10] +``` + +提示: +1. rand7 已定义。 +2. 传入参数: n 表示 rand10 的调用次数。 + + +进阶: +* rand7()调用次数的 期望值 是多少 ? +* 你能否尽量少调用 rand7() ? + + +--- + +### 基本分析 + +给定一个随机生成 $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$ 。 + +--- + +### 最后 + +这是我们「刷穿 LeetCode」系列文章的第 `No.470` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 + +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 + +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 + +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 +