首页
/ freeCodeCamp Basic JavaScript 全解析:用 Math.random() 与 Math.floor() 生成 0–9 的随机整数

freeCodeCamp Basic JavaScript 全解析:用 Math.random() 与 Math.floor() 生成 0–9 的随机整数

2026-09-07 15:29:18作者:翟江哲Frasier

本篇技术指南以 freeCodeCamp 开源课程 Basic JavaScript 学习块(block)中的经典挑战 Generate Random Whole Numbers with JavaScript 为切入点,系统讲解「如何把 Math.random() 产生的随机小数加工成指定区间内的随机整数」。读完本文,你将掌握三步取整法的工作原理、边界条件的推导方式、该挑战的题面与自动化验证逻辑,并能把结论推广到「任意 min–max 区间」这一更通用的随机整数公式。

本文的讲解与全部代码均对应仓库中的 cf1111c1c12feddfaeb1bdef.md,同时会引用其相邻挑战与课程结构文件作为佐证,所有示例都可以在浏览器控制台直接运行验证。

这道题在课程里学什么、处于什么位置

在 freeCodeCamp 的课程源码中,每个挑战都以 Markdown 文件存储,并通过 YAML 风格的 frontmatter 声明 idtitlechallengeTypeforumTopicIddashedName。本挑战的元数据如下:

  • id: cf1111c1c12feddfaeb1bdef
  • title: Generate Random Whole Numbers with JavaScript
  • challengeType: 1
  • forumTopicId: 18186
  • dashedName: generate-random-whole-numbers-with-javascript

它的任务非常聚焦:生成一个 0 到 9 之间的随机整数。这是初学者从「随机小数」迈向「随机整数」的关键一步——随机数是后续许多趣味编程(骰子、抽签、随机动画等)的基础构件。

curriculum/structure/blocks/basic-javascript.json 可以看到,该挑战在 Basic JavaScript 块内的 challengeOrder 顺序如下:

  1. cf1111c1c11feddfaeb9bdef — Generate Random Fractions with JavaScript
  2. cf1111c1c12feddfaeb1bdefGenerate Random Whole Numbers with JavaScript(本文主题)
  3. cf1111c1c12feddfaeb2bdef — Generate Random Whole Numbers within a Range

也就是说,它的前置知识是「随机小数」(fractions),后续章节再把同样思想推广到「任意区间的随机整数」(range)。理解本文就能顺畅衔接到下一课。

前置基础:Math.random() 究竟返回什么

在进入整数生成前,先明确随机小数的行为。该块上一题 Generate Random Fractions with JavaScript 中对此做了精确定义:

JavaScript has a Math.random() function that generates a random decimal number between 0 (inclusive) and 1 (exclusive). Thus Math.random() can return a 0 but never return a 1.

翻译成严格的语言:Math.random() 返回一个 大于等于 0、且严格小于 1 的随机小数。它可能返回 0,但永远不可能返回 1。这一「上限开区间」性质是整个随机整数推导公式的根基。

该题还附带了一条值得记住的注记:与赋值运算一样,函数调用会在 return 执行前全部求值完毕,因此可以直接把 Math.random() 的返回值写在 return 语句中,例如:

function randomFraction() {
  return Math.random();
}

了解这一点,你就能明白为什么本挑战的解法可以把一长串表达式直接放进 return 里,而不用担心「先返回了 0 再去随机」。

三步法:如何把随机小数变成随机整数

本挑战文档给出了一个非常清晰的可复现流程。假设我们要生成一个「小于 20」的随机整数:

  1. 先用 Math.random() 生成一个随机小数——取值范围为 [0, 1)
  2. 把这个随机小数乘以 20——数值被放大到 [0, 20) 区间。
  3. Math.floor() 向下取整——得到最接近的整数。

把三步合并成一行代码:

Math.floor(Math.random() * 20);

为什么结果一定落在 0 到 19,且永远拿不到 20

文档中点明了一个关键边界:由于 Math.random() 永远不可能真正返回 1(它至多无限接近 1),因此:

  • Math.random() * 20 的最大值无限接近 20,但永远不会等于 20
  • 再经 Math.floor() 向下取整后,输出范围是 0, 1, 2, ..., 19

所以「乘以 20 + 向下取整」给出的随机整数是 0 到 19(含两端)。逐层拆解这段代码的执行过程:

Math.floor(Math.random() * 20);
//        └──────┬──────┘
//   Math.random()           例如 0.37
//   * 20                    例如 7.4
// Math.floor(...)           例如 7

即:先调用 Math.random() 产生小数 → 将结果乘以 20 放大区间 → 再把乘积作为实参传给 Math.floor() 向下舍入到最近整数。三者按从内到外的顺序完成。

本课实战任务:返回 0 到 9 的随机整数

题目要求

挑战的 instructions 是:

Use this technique to generate and return a random whole number in the range from 0 to 9.

也就是把同样的技术套用到 10 这个放大倍数上。题目给出的起始代码(seed)为:

function randomWholeNum() {
  return Math.random();
}

标准答案

参考解法(solutions 段)是把 Math.random() 放大 10 倍后再向下取整:

function randomWholeNum() {
  return Math.floor(Math.random() * 10);
}

推导验证:Math.random()[0,1) 内取值 → 乘以 10 后处于 [0,10)Math.floor() 向下取整后为 09 的整数。这正是题面要求的范围。

自动化测试在验证什么:逐条解读 hints

freeCodeCamp 的挑战通过 hints 中内嵌的断言脚本对学习者代码做自动校验。读懂这些断言,能让你精确把握「规范答案」与「违规写法」之间的边界。以下四条断言都来自 cf1111c1c12feddfaeb1bdef.md# --hints-- 段。

断言一:返回值必须是整数。

assert(
  typeof randomWholeNum() === 'number' &&
    (function () {
      var r = randomWholeNum();
      return Math.floor(r) === r;
    })()
);

它先检查返回值是 number 类型,再校验「对该数向下取整后仍等于它自身」——这是「整数」的数学定义式。如果直接返回 Math.random(),返回值与 Math.floor(r) 不相等,断言即失败。

断言二:必须使用 Math.random

assert(__helpers.removeJSComments(code).match(/Math.random/g).length >= 1);

注意这里用了 __helpers.removeJSComments(code):先把提交代码中的注释剥离,再做字符串匹配。这条规则防止学习者用硬编码常量假装随机,同时屏蔽了「只在注释里写 Math.random」这类取巧行为。

断言三:必须把 Math.random 的结果乘以 10。

assert(
  __helpers.removeJSComments(code).match(/\s*?Math.random\s*?\(\s*?\)\s*?\*\s*?10[\D]\s*?/g) ||
    __helpers.removeJSComments(code).match(/\s*?10\s*?\*\s*?Math.random\s*?\(\s*?\)\s*?/g)
);

该正则同时接受两种书写方向:Math.random() * 1010 * Math.random(),因为乘法满足交换律,两种写法数学等价。[\D] 确保 10 后面不是数字,避免把 Math.random() * 100 之类误判通过。

断言四:必须使用 Math.floor

assert(__helpers.removeJSComments(code).match(/Math.floor/g).length >= 1);

理由在于:Math.ceil 会向上取整导致结果可能等于 10,Math.round 会产生不均匀分布,只有 Math.floor 配合 [0,1) 区间才能得到完全均匀的 0–9 整数。

把「错误写法」代入这几条断言可以直观感受约束强度:

function randomWholeNum() {        // 错误 1:返回 0-9 间的小数,
  return Math.random() * 10;        // Math.floor(r) !== r,断言一失败
}

function randomWholeNum() {        // 错误 2:放大 100 倍,
  return Math.floor(Math.random() * 100); // 值域超出 0-9,断言三正则不匹配
}

function randomWholeNum() {        // 错误 3:向上取整,
  return Math.ceil(Math.random() * 10);    // 可能返回 10,断言四匹配失败
}

举一反三:推广到任意 min 到 max 区间

紧接本挑战的下一课 Generate Random Whole Numbers within a Range 把思路推广到了通用场景。设下限为 min、上限为 max(均为闭区间端点),通用公式为:

Math.floor(Math.random() * (max - min + 1)) + min

公式的几何直觉

  • (max - min + 1) 是区间内整数的个数。例如 min=5, max=15 时区间长度为 11
  • Math.random() * 11 把随机小数映射到 [0, 11)
  • Math.floor(...) 得到 010 的整数(共 11 个取值);
  • 最后 + min(即 +5)把整体平移到 515

对照该题的标准解法(myMinmyMax 为函数参数):

function randomRange(myMin, myMax) {
  return Math.floor(Math.random() * (myMax - myMin + 1)) + myMin;
}

该题的自动化测试会循环调用 randomRange(5, 15) 一百次,并断言:观察到的极小值等于 5calcMin === 5)、极大值等于 15calcMax === 15),从而证明两个闭端点都「可达」;同时用 randomRange(0, 1) % 1 === 0 确保输出是整数。这套「循环采样 + 极值追踪」的测试方法本身也值得学习——它是验证随机函数边界正确的经典手段。

小技巧:当 min = 0 时,公式退化为 Math.floor(Math.random() * (max + 1));当 max = 9min = 0 时再退化一步,正是本文这道挑战的答案。三层公式是逐级递进的关系。

实战中的注意点与边界意识

结合本题推导出的两个不等式,可以沉淀几条适用于所有随机整数生成场景的经验:

  • 上限永远取不到(exclusive):凡是「Math.floor(Math.random() * N)」形态,结果都在 0 … N-1,想要「0 … max 含 max」就必须写成 N = max + 1,即 Math.floor(Math.random() * (max + 1))
  • 放大倍数是「取值个数」而非「最大值」:生成 0–19 需要乘以 20 而非 19;生成 0–9 需要乘以 10。初学者最常见的错误是把放大倍数错写成区间终点。
  • Math.floor 而非 Math.round/Math.ceilMath.round 会让端点 0N 的出现概率低于中间值,破坏均匀性;Math.ceil 会引入「不可能由 [0,1) 放大得到的上界溢出」。只有向下取整配合半开区间才是均匀的。
  • Math.random() 并非加密安全的随机源:它属于伪随机数生成器,返回值不可预测性不足以用于安全场景(如令牌、密钥、抽奖防作弊)。若需求涉及安全性,应当换用专门为加密设计的随机接口(如 Web Crypto 的 crypto.getRandomValues),这是 JavaScript 语言的通用常识,与本课程面向学习场景的定位并不冲突。

在源码仓库中继续深入

如果你想亲自验证本文的全部论述,可以依次打开以下仓库文件:

本块位于 JavaScript Algorithms and Data Structures 超级块(superblock)下(对应 curriculum/structure/superblocks/javascript-algorithms-and-data-structures.json 中的 basic-javascript 块配置)。建议上手验证方式:打开浏览器开发者工具 Console,直接粘贴本文解法代码并多次调用,即可直观看到输出始终均匀落在要求区间内。理解了「半开区间 + 向下取整」这一对组合,你就掌握了用 JavaScript 写出任意均匀随机整数的通用能力。

登录后查看全文
热门项目推荐
相关项目推荐