在JavaScript中生成特定范围内的随机整数

2506

我该如何在 JavaScript 中生成在两个指定变量之间的随机整数,例如 x = 4y = 8 会输出 4, 5, 6, 7, 8 中的任意一个?


2
这里有一个有用的代码片段:https://gist.github.com/kerimdzhanov/7529623 - Dan K.K.
15
顺便提一下:对于那些使用npm并寻找快速、可靠和现成解决方案的人,可以使用lodash.random。它只需很小的空间(它仅会导入方法本身而非整个lodash),可轻松调用。 - Aurelio
如果需要加密安全,请使用https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues。 - happy
你能在关于数字范围的问题中更加明确吗?特别是零。负数呢?(“有些文献将自然数排除零,有时将自然数与零一起称为整数”。)(但是不要包含“编辑:”,“更新:”或类似内容——问题应该看起来像今天写的。) - Peter Mortensen
2
这里有很多答案回答了一些不同的问题(它们并不是真正的答案)。就像一些用户只阅读了“生成随机整数”,却从未看到“在特定范围内”的部分(甚至是带有[4; 8]示例的正文)。 - Peter Mortensen
Java的对应问题(包括删除的102个答案):*如何在Java中生成特定范围内的随机整数?* - Peter Mortensen
40个回答

6

我在 W3Schools 上找到了这个简单的方法:

Math.floor((Math.random() * max) + min);

1
Math.floor((Math.random() * 1) + 0); 总是返回0。 - madprops
1
@madprops 因为最大数是不包括在内的。要获取0或1,您应该将2设置为最大数。 - NutCracker
2
或者你可以在调用这个方法的函数中加上 +1。 - Pietro Coelho
你在哪里找到的?这将不会输出在范围 [min; max] 内。例如,如果 min = 3000 和 max = 7000,则它将(大约)输出在范围 *[3000; 10000]*(而不是 *[3000; 7000]*)。 - Peter Mortensen

6

Math.random()很快,适用于许多目的,但如果您需要加密安全值(它不安全),或者您需要来自完全均匀无偏分布的整数(其他答案中使用的乘法方法会比其他一些值稍微频繁地产生某些值),那么就不合适。

在这种情况下,我们可以使用crypto.getRandomValues()生成安全整数,并拒绝我们无法将其均匀映射到目标范围的任何生成值。这会更慢,但除非您要生成极大量的值,否则不应该有太大影响。

为了澄清偏向分布问题,请考虑这样一个情况:我们想生成介于1和5之间的值,但是我们有一个随机数生成器,它会生成介于1和16之间的值(4位值)。我们希望有相同数量的生成值映射到每个输出值,但是16不能被5整除:留下余数1。因此,我们需要拒绝可能生成的1个值,只有在获得15个较小的值并且能够均匀映射到我们的目标范围时才继续执行。 我们的行为可能看起来像是这样的伪代码:

Generate a 4-bit integer in the range 1-16.
If we generated  1,  6, or 11 then output 1.
If we generated  2,  7, or 12 then output 2.
If we generated  3,  8, or 13 then output 3.
If we generated  4,  9, or 14 then output 4.
If we generated  5, 10, or 15 then output 5.
If we generated 16 then reject it and try again.

以下代码使用类似的逻辑,但生成一个32位整数,因为这是JavaScript标准number类型可以表示的最大常规整数大小。 (如果需要更大的范围,可以修改为使用BigInt。)无论选择什么范围,被拒绝的生成值的比例总是小于0.5,所以期望的拒绝数量总是小于1.0,并且通常接近于0.0;您不需要担心它会永远循环下去。

const randomInteger = (min, max) => {
  const range = max - min;
  const maxGeneratedValue = 0xFFFFFFFF;
  const possibleResultValues = range + 1;
  const possibleGeneratedValues = maxGeneratedValue + 1;
  const remainder = possibleGeneratedValues % possibleResultValues;
  const maxUnbiased = maxGeneratedValue - remainder;

  if (!Number.isInteger(min) || !Number.isInteger(max) ||
       max > Number.MAX_SAFE_INTEGER || min < Number.MIN_SAFE_INTEGER) {
    throw new Error('Arguments must be safe integers.');
  } else if (range > maxGeneratedValue) {
    throw new Error(`Range of ${range} (from ${min} to ${max}) > ${maxGeneratedValue}.`);
  } else if (max < min) {
    throw new Error(`max (${max}) must be >= min (${min}).`);
  } else if (min === max) {
    return min;
  } 

  let generated;
  do {
    generated = crypto.getRandomValues(new Uint32Array(1))[0];
  } while (generated > maxUnbiased);

  return min + (generated % possibleResultValues);
};

console.log(randomInteger(-8, 8));          // -2
console.log(randomInteger(0, 0));           // 0
console.log(randomInteger(0, 0xFFFFFFFF));  // 944450079
console.log(randomInteger(-1, 0xFFFFFFFF));
// Error: Range of 4294967296 covering -1 to 4294967295 is > 4294967295.
console.log(new Array(12).fill().map(n => randomInteger(8, 12)));
// [11, 8, 8, 11, 10, 8, 8, 12, 12, 12, 9, 9]


这里使用的 crypto.getRandomValuesCrypto.getRandomValues 有区别吗? - Peter Mortensen

6

在最小值和最大值之间随机生成一个整数:

function randomRange(low, high) {
  var range = (high-low);
  var random = Math.floor(Math.random()*range);
  if (random === 0) {
    random += 1;
  }
  return low + random;
}

这并不是最优雅的解决方案,但可以快速实现。


在“+=”中的“+”似乎是多余的。在“(high-low)”中的括号似乎是多余的。 - Peter Mortensen
考虑特殊情况(if),可以说随机数的分布情况如何? - Peter Mortensen

5
以下是一个 JavaScript 函数示例,可以生成任意指定长度的随机数,而不使用 Math.random():
function genRandom(length)
{
  const t1 = new Date().getMilliseconds();
  var min = "1", max = "9";
  var result;
  var numLength = length;
  if (numLength != 0)
  {
     for (var i = 1; i < numLength; i++)
     {
        min = min.toString() + "0";
        max = max.toString() + "9";
     }
  }
  else
  {
     min = 0;
     max = 0;
     return;
  }

  for (var i = min; i <= max; i++)
  {
       // Empty Loop
  }

  const t2 = new Date().getMilliseconds();
  console.log(t2);
  result = ((max - min)*t1)/t2;
  console.log(result);
  return result;
}

t1/t2总是非常接近于1。因此,当函数被重复调用时,您的函数返回相同的数字.. http://jsbin.com/xogufacera/edit?js,console - Sunil B N
请检查jsbin网址,您将自己看到输出。 - Sunil B N
当长度在4-10之间时(我已在我的机器上测试过),它的效果非常好,因为常量T1和T2的值应该具有适当的距离。 - Nilesh Pawar
需要解释一下。例如,时间的作用是什么(计时器/当前日期时间?)?主要思想是什么?空循环的目的是什么?为了让时间流逝?有什么示例输出?根据帮助中心的说法:“始终解释您提出的解决方案为什么合适以及它是如何工作的”。请通过编辑(更改)您的答案,而不是在此处进行评论(不包括“Edit:”,“Update:”或类似内容 - 答案应该看起来像今天写的)。 - Peter Mortensen

3
这是我对于范围内的随机数的处理,例如我想获得基数到指数范围内的随机数。例如,基数为10,指数为2,则会理想地给出从0到100的随机数等等。
如果有帮助,请使用以下代码:
// Get random number within provided base + exponent
// By Goran Biljetina --> 2012

function isEmpty(value) {
    return (typeof value === "undefined" || value === null);
}

var numSeq = new Array();

function add(num, seq) {
    var toAdd = new Object();
    toAdd.num = num;
    toAdd.seq = seq;
    numSeq[numSeq.length] = toAdd;
}

function fillNumSeq (num, seq) {
    var n;
    for(i=0; i<=seq; i++) {
        n = Math.pow(num, i);
        add(n, i);
    }
}

function getRandNum(base, exp) {
    if (isEmpty(base)) {
        console.log("Specify value for base parameter");
    }
    if (isEmpty(exp)) {
        console.log("Specify value for exponent parameter");
    }

    fillNumSeq(base, exp);

    var emax;
    var eseq;
    var nseed;
    var nspan;
    emax = (numSeq.length);
    eseq = Math.floor(Math.random()*emax) + 1;
    nseed = numSeq[eseq].num;
    nspan = Math.floor((Math.random())*(Math.random()*nseed)) + 1;
    return Math.floor(Math.random()*nspan) + 1;
}

console.log(getRandNum(10, 20), numSeq);

//Testing:
//getRandNum(-10, 20);
//console.log(getRandNum(-10, 20), numSeq);
//console.log(numSeq);

3

使用:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>

    <body>
        <script>
            /*
                Assuming that window.crypto.getRandomValues
                is available, the real range would be from
                0 to 1,998 instead of 0 to 2,000.

                See the JavaScript documentation
                for an explanation:

                  https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues
            */
            var array = new Uint8Array(2);
            window.crypto.getRandomValues(array);
            console.log(array[0] + array[1]);
        </script>
    </body>
</html>

Uint8Array创建一个由数字填充的数组,最多可达三位数,即最大值为999。这段代码非常简短。


Uint8Array 的语法高亮确实很奇怪。” - Peter Mortensen
需要解释一下。例如,这个想法/要点是什么?这个 window.crypto 是什么东西?为什么它嵌入到网页中?它能在 Node.js 下工作吗?根据 帮助中心 的说法:*"...总是解释你所提出的解决方案为什么合适以及如何工作". 请通过编辑(更改)您的答案来回应,而不是在评论区回复(不需要* "Edit:", "Update:" 或类似文字 - 答案应该看起来像今天写的)。 - Peter Mortensen
和其他答案一样,这并没有回答问题。它回答了另一个问题——“如何在JavaScript中生成介于两个指定变量之间的随机整数,例如x = 4和y = 8将输出4、5、6、7、8中的任意一个?” 换句话说,是在指定的*范围/闭区间*(例如[4;8])内生成一个随机数。甚至标题也说了“在特定范围内”。 - Peter Mortensen
请注意,这种说法还有另一种错误。"最大为999"是不正确的,范围应该是从0到255(包括255)。将这些数字相加也会对255产生偏见,因为有许多不同的数字相加得到255(例如36 + 219),但只有两个特定的数字相加得到510(255 + 255)或0(0 + 0)。 - undefined

2

Ionuț G. Stan写了一个很好的答案,但对我来说有点太复杂了。因此,我在Jason Anello的解释中找到了更简单的相同概念的解释。

注意:在阅读Jason的解释之前,您唯一需要知道的重要事情是“截断”的定义。他在描述Math.floor()时使用该术语。牛津词典将“截断”定义为:

通过切断顶部或末端缩短(某物)。


2
我猜这可能是所有贡献中最简化的。
maxNum = 8,
minNum = 4

console.log(Math.floor(Math.random() * (maxNum - minNum) + minNum))

console.log(Math.floor(Math.random() * (8 - 4) + 4))

这将在控制台中记录介于4和8之间(包括4和8)的随机数。


1
这对于 maxNum = 2, minNum = 1 不起作用,结果总是1。实际上,我认为任何仅相差1个数字的min和max都是一样的;较低的边界始终是结果。 - devklick

1
一个名为 randUpTo 的函数,接受一个数字并返回在 0 和该数字之间的随机整数:
var randUpTo = function(num) {
    return Math.floor(Math.random() * (num - 1) + 0);
};

一个名为randBetween的函数,接受两个代表范围的数字,并返回这两个数字之间的随机整数:

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

一个名为randFromTill的函数,接受两个数字表示范围,并返回介于最小值(包括)和最大值(不包括)之间的随机数。

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

一个名为randFromTo的函数,接受两个数字表示范围,并返回在最小值(包括)和最大值(包括)之间的随机整数:
var randFromTo = function (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
};

1
美丽的,你能否注意一下randBetween是(排除)(排除)的吗? - 650aa6a2

1
你可以使用这段代码片段,
let randomNumber = function(first, second) {
    let number = Math.floor(Math.random()*Math.floor(second));
    while(number < first) {

        number = Math.floor(Math.random()*Math.floor(second));
    }
    return number;
}

2
这里有一行不必要的重复代码。只需要使用 do-while 而不是 while 即可。 - Javi Marzán
如果first1,000,000,000,而second1,000,000,042,那么这将非常慢。也许可以在你的答案中添加一些警告语句?甚至可以添加一些基准测试来演示性能影响何时变得显著(以量化它)?(但是不要包含"Edit:"、"Update:"或类似的内容——答案应该看起来像是今天写的。) - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接