news 2026/8/4 18:06:59

LeetCode 470 用 Rand7() 实现 Rand10()

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 470 用 Rand7() 实现 Rand10()


文章目录

    • 摘要
    • 描述
    • 题解答案
    • 题解代码分析
      • 第一步:为什么是 `(rand7() - 1) * 7 + rand7()`
      • 第二步:为什么只取 `[1,40]`
      • 第三步:为什么不会死循环
    • 示例测试及结果
    • 时间复杂度
    • 空间复杂度
    • 总结

摘要

LeetCode 470 这道题乍一看像是“随机数题”,但真正考的并不是 API 调用,而是**对概率、均匀分布以及拒绝采样(Rejection Sampling)**的理解。
题目限制只能用rand7(),却要生成rand10(),这类问题在工程里并不少见,比如基于受限随机源做更高维度的随机模拟、灰度实验、AB 测试分流等。

这篇文章会从直觉误区开始,一步步推到正确解法,并用 Swift 写出一个可以直接跑的 Demo,顺便聊聊它在真实系统里的意义。

描述

题目给了一个现成的方法:

rand7() -> 返回 [1, 7] 之间的均匀随机整数

我们的目标是实现:

rand10() -> 返回 [1, 10] 之间的均匀随机整数

限制条件很严格:

  • 只能调用rand7()
  • 不能使用系统随机函数
  • 要求结果是均匀分布

很多人第一反应是:

“那我rand7() % 10 + 1不就行了?”

这个想法非常自然,但也是这道题的第一个坑。
因为7 不能整除 10,直接取模一定会导致某些数字出现的概率更高。

所以问题的本质其实是:

如何把一个有限、离散、范围不整除的随机源,映射成另一个均匀随机源?

题解答案

核心思路只有一句话:

rand7()组合出一个更大的均匀空间,再从中“筛选”出[1,10]

具体做法是:

  1. 用两次rand7()生成一个[1,49]的均匀随机数
  2. 只保留[1,40]这一段
  3. 对 10 取模,映射到[1,10]

为什么是 40?
因为40小于等于 49 的最大、能被 10 整除的数

题解代码分析

下面是一个完整、可运行的 Swift Demo,包含rand7()的模拟实现,方便本地测试。

classSolution{// 模拟题目给定的 rand7()funcrand7()->Int{returnInt.random(in:1...7)}// 目标函数funcrand10()->Int{whiletrue{// 将 rand7() 扩展成 [1, 49]letnum=(rand7()-1)*7+rand7()// 只接受 [1, 40]ifnum<=40{return(num-1)%10+1}// 超过 40 的部分直接丢弃,重新来}}}

下面我们一行一行拆解这段逻辑。

第一步:为什么是(rand7() - 1) * 7 + rand7()

你可以把它想象成一个二维表:

  • 第一次rand7()决定“行”
  • 第二次rand7()决定“列”

这样一来:

  • 一共 7 × 7 = 49 种组合
  • 每一种组合出现的概率完全相等

结果自然就是一个均匀分布的[1,49]

第二步:为什么只取[1,40]

这是这道题最关键的地方。

  • 49 不能整除 10
  • 40 可以整除 10

如果我们只使用[1,40],那么:

  • 每个数字 1~10 都对应4 个等概率来源
  • 映射后仍然是均匀分布

剩下的[41,49]呢?

答案是:
不要了,直接重来。

这就是经典的拒绝采样(Rejection Sampling)

第三步:为什么不会死循环

很多人担心:

“一直 reject,会不会一直跑不出来?”

实际上不会。

  • 接受概率是40 / 49
  • 平均不到 1.25 次循环就能成功一次

在进阶问题里,这正是题目希望你分析的“期望调用次数”。

示例测试及结果

我们可以简单写个测试,看看分布是不是均匀的。

letsolution=Solution()varcounter=Array(repeating:0,count:10)lettimes=100_000for_in0..<times{letvalue=solution.rand10()counter[value-1]+=1}foriin0..<10{print("Number\(i+1):\(counter[i])")}

你会看到类似这样的输出(每次略有波动):

Number 1: 10021 Number 2: 9958 Number 3: 10044 Number 4: 10003 Number 5: 10012 Number 6: 9987 Number 7: 10031 Number 8: 9996 Number 9: 10019 Number 10: 9929

从结果上看,每个数字的出现次数都非常接近,说明分布是均匀的。

时间复杂度

单次rand10()

  • 平均调用rand7()的次数是常数级
  • 接受概率是40 / 49

所以:

  • 时间复杂度是 O(1)(期望意义下)

这也是为什么这种“看起来有循环”的写法,在随机问题中是完全可以接受的。

空间复杂度

  • 没有使用额外的数据结构
  • 只用了几个临时变量

所以:

  • 空间复杂度是 O(1)

总结

LeetCode 470 表面上是一个随机数题,实际上考的是三件事:

  1. 你是否真正理解“均匀分布”
  2. 你能不能跳出“取模”的直觉陷阱
  3. 你是否知道如何通过拒绝采样构造新概率空间

在真实业务里,这类思路经常用在:

  • 灰度发布中的流量均匀分桶
  • 基于有限随机源的模拟系统
  • 游戏、推荐系统里的概率控制

如果你能把这道题讲清楚,说明你对“概率 + 工程实现”这件事,已经不是停留在表面了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/25 17:43:31

【无宏恐惧】告别VBA禁用!用纯BAT脚本实现Excel复杂档案编号批量生成

当Excel弹出“宏已被禁用”的警告时&#xff0c;你的自动化方案是否就此夭折&#xff1f;面对单位严格的IT安全政策&#xff0c;VBA方案常常无法执行。但工作还得继续——1000份学生档案&#xff0c;每份1-5册不等&#xff0c;需要生成符合复杂规则的编号、索引号。本文提供一套…

作者头像 李华
网站建设 2026/7/26 3:15:18

深入理解es查询语法在Kibana中的实际应用与技巧

玩转Kibana&#xff1a;用好ES查询语法&#xff0c;让日志分析快准狠你有没有过这样的经历&#xff1f;线上服务突然报警&#xff0c;CPU飙升、接口超时&#xff0c;而你打开Kibana后却一脸茫然——成千上万条日志刷屏滚动&#xff0c;关键词满天飞&#xff0c;但关键线索像针一…

作者头像 李华
网站建设 2026/8/3 1:13:43

无源蜂鸣器声音生成原理:结合PWM脉冲解析

无源蜂鸣器是如何“唱歌”的&#xff1f;从PWM脉冲讲起你有没有想过&#xff0c;家里门铃那声清脆的“叮咚”&#xff0c;或是微波炉加热结束时的“嘀——”&#xff0c;背后其实藏着一个简单的物理原理&#xff1f;这些声音大多来自一种叫无源蜂鸣器的小元件。它不像喇叭那样能…

作者头像 李华
网站建设 2026/8/4 15:28:10

一文说清上位机与下位机通信中的延时匹配问题

上位机与下位机通信为何“卡顿”&#xff1f;一文讲透延时匹配的底层逻辑与实战策略你有没有遇到过这样的场景&#xff1f;明明上位机程序写得逻辑清晰、界面流畅&#xff0c;可一连上下位机&#xff0c;数据就开始跳变、指令响应迟缓&#xff0c;甚至偶尔“失联”。重启&#…

作者头像 李华
网站建设 2026/8/4 3:52:50

RS485和RS232区别总结:抗干扰能力系统学习

RS485和RS232区别总结&#xff1a;抗干扰能力系统学习在工业现场&#xff0c;你有没有遇到过这样的问题&#xff1f;设备明明接好了线&#xff0c;通信协议也没写错&#xff0c;可数据就是时通时断&#xff0c;偶尔还冒出一堆乱码。排查半天&#xff0c;最后发现是串口选型不当…

作者头像 李华