JS随机数怎么生成:Math.random() 的用法与范围控制

技术教程13小时前更新 jdkjadmin
0 00

JavaScript 内置的 Math.random() 可以生成伪随机数,常用于抽取列表项、随机排序、游戏中的非关键性随机事件,以及生成随机颜色等。要正确控制范围,关键是先弄清它返回值的边界,再选择合适的缩放和取整方式。

Math.random() 返回什么

Math.random() 不接收参数,返回一个大于或等于 0、小于 1 的数字,也就是范围 [0, 1)。左边界可能取到,右边界不会取到。返回值是伪随机数:由运行环境生成,适合一般随机效果,但不适用于密码、令牌等安全场景。

const value = Math.random();

console.log(value); // 例如:0.372814...

给它传入最小值或最大值并不会改变范围,参数会被忽略。范围控制需要通过乘法和加法完成。

生成指定范围的随机小数

要生成从 min 到 max 之间的随机小数(包含 min,不包含 max),使用下面的公式:

function randomFloat(min, max) {
  return Math.random() * (max - min) + min;
}

console.log(randomFloat(2, 5)); // 2(含)到 5(不含)

计算过程是:先将 [0, 1) 乘以区间宽度 max - min,得到 [0, max - min),再加上 min。例如 randomFloat(2, 5) 的结果始终大于或等于 2,且小于 5。随机结果是浮点数,通常包含小数部分。

生成指定范围的随机整数

常见需求是生成一个包含上下界的整数,例如掷出 1 到 6 点。公式为:

function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

console.log(randomInt(1, 6)); // 1、2、3、4、5 或 6

这里的 + 1 用于把整数上界纳入可能结果。若不加,Math.floor(Math.random() * (max - min)) + min 得到的是 [min, max) 对应的整数,最大值不会出现。例如 min 为 1、max 为 6 时,结果只有 1 到 5。

如果业务需要的是“最小值包含、最大值不包含”,可以这样写:

function randomIntExclusiveMax(min, max) {
  return Math.floor(Math.random() * (max - min)) + min;
}

console.log(randomIntExclusiveMax(1, 6)); // 1 到 5

参数限制与边界处理

Math.random() 本身没有范围参数,也不会替调用者检查业务参数。对于整数范围函数,建议明确要求上下界都是安全整数,并处理上下界颠倒或区间宽度溢出等情况:

function randomIntInclusive(min, max) {
  if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
    throw new TypeError("min 和 max 必须是安全整数");
  }

  if (min > max) {
    throw new RangeError("min 不能大于 max");
  }

  const width = max - min + 1;
  if (!Number.isSafeInteger(width)) {
    throw new RangeError("整数范围过大");
  }

  return Math.floor(Math.random() * width) + min;
}

console.log(randomIntInclusive(10, 20));

Number.isSafeInteger() 检查的是 JavaScript 能精确表示的安全整数。即使两个端点分别是安全整数,它们之间的跨度也可能过大,因此示例还检查了区间宽度。不要把这个实现用于超出安全整数精度的范围;这类需求应使用适合大整数的方案。对于浮点数函数,也应根据业务需要验证参数是否为有限数,并决定是否允许 min > max。默默交换上下界虽然方便,却可能掩盖调用错误。

浮点数使用二进制表示,计算结果可能带有精度误差。如果要显示两位小数,可以在展示时格式化,例如 randomFloat(0, 10).toFixed(2)。注意,toFixed() 返回的是字符串;若后续要继续做数值计算,应按需转换,且格式化并不意味着内部数值变得精确。

常见错误

  • 把 Math.random() 当成闭区间。它不会返回 1,因此缩放后上界通常不包含;生成含上界的整数要使用 max - min + 1。
  • 用 Math.round() 取整。Math.round(Math.random() * n) 会让两端整数对应的取值区间与中间值不同,容易造成分布偏差。常规整数范围应使用 Math.floor() 和正确的区间宽度。
  • 不检查输入。空值、字符串、无穷大、非整数或颠倒的范围可能产生意外结果。按函数约定校验,并在不合法时明确报错。
  • 误以为每次结果都不同。随机生成不保证连续两次不重复,也不保证一小批样本恰好均匀分布。测试时应检查范围和统计特性,而不是要求每次都出现新值。

安全用途不要使用 Math.random()

Math.random() 不提供密码学安全保证,也没有可移植的种子设置接口。不要用它生成密码重置令牌、会话标识、验证码密钥、密钥材料或抽奖等对安全性有要求的结果。

浏览器中应使用 Web Crypto API 的 crypto.getRandomValues()。如果要把随机字节映射到有限整数范围,不要简单地对字节取模,因为当字节取值数量不能被目标范围宽度整除时,会产生分布偏差。应采用拒绝采样等方法,或使用经过审查的实现。Node.js 中可使用内置 node:crypto 提供的安全随机 API。选择方式时应依据运行环境和目标范围,不能把普通的 Math.random() 包装后当成安全随机数。

总结来说,普通界面效果和非安全随机选择可以用 Math.random():小数范围采用缩放,整数范围用 Math.floor() 并明确上界是否包含;同时校验输入。凡是随机结果关系到身份验证、秘密信息或公平性要求较高的场景,都应改用密码学安全随机源。

© 版权声明

相关文章