在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个回答

4806

Mozilla开发者网络页面上有一些示例:

/**
 * Returns a random number between min (inclusive) and max (exclusive)
 */
function getRandomArbitrary(min, max) {
    return Math.random() * (max - min) + min;
}

/**
 * Returns a random integer between min (inclusive) and max (inclusive).
 * The value is no lower than min (or the next integer greater than min
 * if min isn't an integer) and no greater than max (or the next integer
 * lower than max if max isn't an integer).
 * Using Math.round() will give you a non-uniform distribution!
 */
function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

以下是翻译:

这是其背后的逻辑。它是一个简单的三比一规则:

Math.random() 返回一个介于0(包括)和1(不包括)之间的Number。因此,我们有了这样一个区间:

[0 .................................... 1)

现在,我们需要一个介于min(包括)和max(不包括)之间的数字:

[0 .................................... 1)
[min .................................. max)

我们可以使用Math.random来获取[min,max)区间中的对应值。但首先我们应该通过减去min从第二个区间中分解出一点问题。
[0 .................................... 1)
[min - min ............................ max - min)

这将会得到:
[0 .................................... 1)
[0 .................................... max - min)

我们现在可以应用 Math.random 然后计算相应的结果。让我们选择一个随机数:
                Math.random()
                    |
[0 .................................... 1)
[0 .................................... max - min)
                    |
                    x (what we need)

因此,为了找到x,我们需要执行以下操作:

x = Math.random() * (max - min);

不要忘记添加 min,这样我们就可以获得在 [min, max) 区间内的数字。
x = Math.random() * (max - min) + min;

这是来自MDN的第一个函数。第二个函数会返回介于minmax 之间(包含两端)的整数。

现在要获取整数,你可以使用roundceilfloor

你可以使用Math.round(Math.random() * (max - min)) + min,但这将导致非均匀分布。因为minmax只有约一半的机会被选中:

min...min+0.5...min+1...min+1.5   ...    max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘   ← Math.round()
   min          min+1                          max

在排除 max 的情况下,它滚动的机会比 min 还要少。

使用 Math.floor(Math.random() * (max - min +1)) + min,您可以获得完全均匀的分布。

 min...  min+1...    ...      max-1... max....   (max+1 is excluded from interval)
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘   ← Math.floor()
   min     min+1               max-1    max

在该等式中,您不能使用 ceil()-1,因为 max 现在掷出的概率略微降低,但您仍然可能会掷出(不需要的)min-1 的结果。


18
它之所以这样做,是因为它调用了“floor”,该函数会向下取整。 - Josh Stodola
6
您可以使用“ round”函数,但是minmax仅有其他数字一半的机会来投掷。您也可以减去1并使用“ ceil”函数。但这会使得max数字由于在[0,1)区间内而变得更难被投掷到。 - Christoph
19
如果有人想测试这种方法的分布情况,我已经创建了一个 JSFiddle:http://jsfiddle.net/F9UTG/1/ - ahren
11
没错,@JackFrost。你不傻,只是在学习 :) - Ionuț G. Stan
4
这个问题很老了,但是理解这个答案花费了我太多时间O.o,我认为在下一个JavaScript版本中扩展math.random可能会有点用处。 - David Ortega
显示剩余31条评论

647
var randomnumber = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;

33
我知道这是一个非常老的答案,但使用(Math.random() * (maximum - minimum + 1) ) << 0更快。 - Ismael Miguel
24
使用二进制运算符(x << 0, x | 0, ~~x) 替代 Math.floor() 将会将 x 转换成一个范围比 Number.MAX_SAFE_INTEGER(2⁵³) 小得多的二补数形式(2³²⁻¹),因此使用时必须小心谨慎! - le_m
@IsmaelMiguel 嗨,我刚在控制台中尝试了你的方法,结果随机得到了一个负值!Math.randRange = (minimum, maximum) => (Math.random() * (maximum - minimum + 1) ) << 0 Math.randRange(2,657348096152) -1407373159 - B''H Bi'ezras -- Boruch Hashem
@bluejayke 因为657348096152(二进制中的1001100100001100111111111111000010011000)有40位,而按位算术使用32位。如果您执行657348096152|0,则会得到218099864(二进制中的1100111111111111000010011000)。 - Ismael Miguel
3
这是一个聪明的回答。将范围内部设为[min,max+1)实际上实现了[min,max]同时包含的期望结果。谢谢! :) - Mladen B.

240

Math.random()

返回一个介于最小值(包括)和最大值(包括)之间的整数随机数
function randomInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

或者在最小值(包含)和最大值(不包含)之间选择任意随机数
function randomNumber(min, max) {
  return Math.random() * (max - min) + min;
}

有用的例子(整数):

// 0 -> 10
Math.floor(Math.random() * 11);

// 1 -> 10
Math.floor(Math.random() * 10) + 1;

// 5 -> 20
Math.floor(Math.random() * 16) + 5;

// -10 -> (-2)
Math.floor(Math.random() * 9) - 10;

** 始终不要忘记(Mozilla):

Math.random()不能提供加密安全的随机数。不要在与安全相关的任何事情上使用它们。请改用Web Crypto API,并更精确地使用window.crypto.getRandomValues()方法。


5
如果你使用Math.ceil函数,则可以避免使用+1 - Alexander Farber

64

使用:

function getRandomizer(bottom, top) {
    return function() {
        return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
    }
}

使用方法:

var rollDie = getRandomizer( 1, 6 );

var results = ""
for ( var i = 0; i<1000; i++ ) {
    results += rollDie() + " ";    // Make a string filled with 1000 random numbers in the range 1-6.
}

简述:

我们正在返回一个函数(借鉴于函数式编程),当调用该函数时,它将返回介于bottomtop值之间的随机整数, 包括bottomtop。 我们说“包含”,因为我们想在可返回的数字范围内包括底部和顶部。这样,getRandomizer( 1, 6 )将返回1、2、3、4、5或6中的任意一个。

('bottom'是较小的数字,'top'是较大的数字)

Math.random() * ( 1 + top - bottom )

Math.random()返回一个介于0和1之间的随机双精度浮点数,如果我们将其乘以topbottom之差加1,我们将得到一个介于01+b-a之间的双精度浮点数。

Math.floor( Math.random() * ( 1 + top - bottom ) )

Math.floor函数向下取整到最近的整数。因此,我们现在拥有0top-bottom之间的所有整数。1看起来很困惑,但它必须存在,因为我们总是向下取整,所以实际上永远不会达到最大值,除非加上它。我们生成的随机小数需要在 0(1+top-bottom) 范围内,这样我们才能向下取整并得到 0top-bottom 范围内的整数:

Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom

在前面的示例代码中,我们得到了一个介于0top-bottom之间的整数,现在我们只需要将bottom加到该结果中,以获得一个范围在bottomtop(包括两端)之间的整数。 :D


注意:如果您传入非整数值或者先传入较大的数字,则会出现不良行为,但除非有人要求,否则我不会深入探讨参数检查代码,因为这与原始问题的意图相去甚远。


1
我意识到这是大约2年半之后的事了,但是对于输入1和6,你的函数返回值为1、2、3、4和5,但从未返回6,如果是“包括”的话就应该返回6。 - some
10
@some,情况可能更糟,我比原先晚了2年半零1天^^。 - ajax333221
+1,我测试了你的代码,它似乎创建了一个正确的值。创造性地构建处理在代码中可能重复出现的固定场景的结构。 - Chris
为什么你要在这个函数里面再嵌套一个函数? - Slava
1
@Alph.Dev 为了将使用随机数生成器的逻辑与决定使用什么样的随机数分布的逻辑解耦。当使用随机数生成器的代码将其作为参数接受(一个始终返回新随机数的0个参数函数),它可以使用任何类型的随机数生成器。 - Gordon Gustafson
显示剩余2条评论

51

所有这些解决方案都使用了过多的火力。您只需要调用一个函数:Math.random();

Math.random() * max | 0;

这将返回一个介于0(包含)和max(不包含)之间的随机整数。


2
这非常干净。谢谢! - Moritz Schmidt
2
OP想要的是4到8之间的范围,而不是8到0。 - avalanche1
4
那么它就不起作用了。Math.random() * 10 | 5 只会输出 5 | 7 | 13 - avalanche1
注意:此答案不可靠。Max: 5和Min: 1返回:1、3、5。 - Nishant Ghodke
3
答案没有问题。显然,人们不知道 | 位或运算符是什么意思。按照现有的陈述,此解决方案适用于下限为 0,上限为非包含性的最大值之间的数字。 - aefxx
显示剩余4条评论

43

返回1到10之间的随机数:

Math.floor((Math.random()*10) + 1); 

返回1到100之间的随机数:

Math.floor((Math.random()*100) + 1)

你的“between”是包含还是不包含?即是[1,10],[1,10),(1,10],还是(1,10)? - evandrix
1
它是部分包含的:[1, *) - Ivan Z
函数末尾为什么需要+1呢?我猜它已经完美运行了。 - Shachi
@Shachi:这是下限(一个糟糕的例子)。像问题中的4一样会更好。 - Peter Mortensen
1太特殊了。这会对其他数字造成破坏,例如问题中的4和8(范围将(大约)为[4;12],而不是[4;8])。 - Peter Mortensen
请问能否概括一下你的回答,使其适用于问题中提到的四和八这样的数字?(即使字面上的数字插入保持不变)。但是请不要加上“编辑:”、“更新:”或类似的内容——回答应该看起来像是今天写的。 - Peter Mortensen

39
function randomRange(min, max) {
  return ~~(Math.random() * (max - min + 1)) + min
}

如果你正在使用Underscore.js,那么可以使用以下替代方案:

_.random(min, max)

2
Underscore实际上提供了一个_.uniqueId()函数,您可以为客户端模型调用该函数。 - originalhat
使用二进制运算符(x << 0x | 0~~x)而不是 Math.floor()x 转换为一个二补数,其范围比 Number.MAX_SAFE_INTEGER(2³²⁻¹ vs. 2⁵³)小得多,因此必须谨慎使用! - le_m

33

如果您需要一个介于0和最大值之间的变量,可以使用:

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

最大值是否包含在内? - Tree
3
@Tree 使用 Math.floor,max 是不包括的。如果你想让 max 包括在内,可以使用 Math.round。 - Luke
1
和其他答案一样,这并没有回答问题(我强调):“如何在JavaScript中生成介于两个指定变量之间的随机整数,例如x = 4和y = 8将输出4、5、6、7、8中的任何一个?” 换句话说,是在指定的*范围/闭区间*(例如[4;8])。即使标题也说“在特定范围内”。这应该放在评论中。 - Peter Mortensen
@Tree 为了使 max 包含在内,请使用 (max + 1)。不要使用 Math.round(),因为这会破坏范围两端的 _随机分布_。 - Dem Pilafian

22

其他答案没有考虑到 01 这些完全合理的参数。相反,您应该使用 round 而不是 ceilfloor

function randomNumber(minimum, maximum){
    return Math.round( Math.random() * (maximum - minimum) + minimum);
}

console.log(randomNumber(0,1));  # 0 1 1 0 1 0
console.log(randomNumber(5,6));  # 5 6 6 5 5 6
console.log(randomNumber(3,-1)); # 1 3 1 -1 -1 -1

1
你的回答是正确的,但我认为你的例子是错误的。console.log(randomNumber(5,6)); # 9 6 6 5 7 7 9和7是否在5和6之间?......你应该纠正它或解释一下。 - Sachin
最后一个例子可以被认为是一个空范围。例如,无效的输入参数。如果结果为空,则会抛出错误或类似的情况。 - Peter Mortensen

21

密码学强度

要在区间[x,y]内获取一个密码学强度的随机整数,请尝试:

let cs = (x,y) => x + (y - x + 1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32 | 0

console.log(cs(4, 8))


我会推荐这个。 - user10294268
我在这个问题上陷入了一个兔子洞,试图学习什么是加密安全的意思。最终在这里找到了答案:https://crypto.stackexchange.com/questions/39186/what-does-it-mean-for-a-random-number-generator-to-be-cryptographically-secure/39188#39188?newreg=6e4d42032f4f452f8257c311c81413bd - 55 Cancri
5
+1,这是最好的选择!不过,我使用了(x, y) => x + crypto.getRandomValues(new Uint32Array(1))[0] % (y - x + 1)(整数取模而非浮点除法)。 - user2297550
能否解释一下什么情况下加密强度很重要,以及为什么这个答案与 Math.random() 不同? - Bryan K

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