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


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


1486
2017-10-06 20:05


起源


这是一个有用的要点: gist.github.com/kerimdzhanov/7529623 - Dan K.K.
作为旁注:对于那些使用npm并寻找快速,可靠和现成的解决方案的人来说 lodash.random 这可以通过超小的占地面积轻松实现(它只会导入方法本身而不是整个lodash)。 - Nobita
如果它需要加密安全 developer.mozilla.org/en-US/docs/Web/API/RandomSource/... - happy


答案:


有一些例子 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)
 * Using Math.round() will give you a non-uniform distribution!
 */
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

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

Math.random() 返回一个 Number 0(含)和1(不含)之间。所以我们有这样的间隔:

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

现在,我们想要一个数字 min (包括)和 max (独家):

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

我们可以使用 Math.random 在[最小,最大]间隔中获取通讯记录。但是,首先我们应该通过减去一点来解决这个问题 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的第一个功能。第二个,返回一个整数 min 和 max,包容性。

现在获取整数,你可以使用 roundceil 要么 floor

你可以用 Math.round(Math.random() * (max - min)) + min然而,这给出了非均匀分布。都, min 和 max 只有大约一半的机会滚动:

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... min+2 ... max-1... max.... max+1 (is excluded from interval)
|        |        |         |        |        |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘   ← Math.floor()
   min     min+1               max-1    max

你不能用 ceil() 和 -1 在那个等式中因为 max 现在滚动的机会略少,但你可以滚动(不需要的) min-1 结果也是。


3055
2017-10-06 20:08



它只是这样做,因为它正在呼唤 floor,向下舍入。 - Josh Stodola
@ thezachperson31你可以用 round,但两者都是, min 和 max 只有一半的机会像其他数字一样滚动。你也可以减去一个并采取 ceil。然而这离开了 max 数量最少的机会由于 [0,1) 间隔。 - Christoph
如果有人想测试这个方法的分布,我创建了一个JSFiddle: jsfiddle.net/F9UTG/1 - ahren
@JackFrost是的,没错。你不是傻瓜,你只是在学习:) - Ionuț G. Stan
这个问题很老了,但是理解这个答案花了我太多时间O.o,我认为在下一个JavaScript版本上扩展math.random会有点用处 - Jonathan Ortega


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

463
2017-10-06 20:09



我知道这是一个非常古老的答案,但使用 (Math.random() * (maximum - minimum + 1) ) << 0 是比较快的。 - Ismael Miguel
@IsmaelMiguel使用二元运算符(x << 0, x | 0, ~~x) 代替 Math.floor() 转换 x 成为一个双补,范围小于 Number.MAX_SAFE_INTEGER (2³²-1vs.2⁵³),因此您必须谨慎使用它! - le_m


的Math.random()

来自 Mozilla的 开发者网络文档:

// Returns a random integer between min (include) and max (include)

Math.floor(Math.random() * (max - min + 1)) + 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;

72
2018-03-25 01:58





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.
}

分解:

我们正在返回一个函数(借用函数式编程),当调用它时,将返回值之间的随机整数 bottom 和 top, 包括的。我们说'包容'因为我们希望在可返回的数字范围内包括底部和顶部。这条路, getRandomizer( 1, 6 ) 将返回1,2,3,4,5或6。

(底部是较低的数字,顶部是较大的数字)

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

Math.random() 返回0到1之间的随机双精度,如果我们将它乘以1加上之间的差值 top 和 bottom,我们会在两者之间得到一个双倍 0 和 1+b-a

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

Math.floor 将数字向下舍入到最接近的整数。所以我们现在拥有所有的整数 0 和 top-bottom。 1看起来令人困惑,但它需要在那里,因为我们总是向下舍入,所以如果没有它,最高号将永远不会实现。我们生成的随机小数需要在范围内 0 至 (1+top-bottom) 所以我们可以向下舍入并获得范围内的int 0 至 top-bottom

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

上一个示例中的代码为我们提供了范围内的整数 0 和 top-bottom,所以我们现在需要做的就是添加 bottom 到那个结果得到范围内的整数 bottom 和 top 包括的。 :d


注意:如果你传入一个非整数值或更大的数字,你会得到不良行为,但除非有人要求它,我不会深入研究参数检查代码,因为它远离原始问题的意图。


48
2017-10-06 20:08



我意识到这是大约两年半之后,但是对于输入1和6,你的函数返回值1,2,3,4和5,但从不是6,就像它是“包容性”一样。 - some
@some,可能会更糟,我是2年半+ 1天后^^ - ajax333221
+1,我测试了你的代码,它似乎创建了一个正确的值。用于处理可能在代码中重复的固定方案的创造性结构。 - Chris
为什么在函数中有一个函数呢? - Alph.Dev
@ Alph.Dev将使用随机数生成器的逻辑与确定要使用的随机数分布的逻辑分离。当使用随机数生成器的代码将其作为参数(始终返回新随机数的0参数函数)接受时,它可以与任何类型的随机数生成器一起使用。 - Gordon Gustafson


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

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

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

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

27
2018-03-28 11:31



你的“介于”包容性还是排他性?即是[1,10],[1,10),(1,10)还是(1,10)? - evandrix
它具有部分包容性:[1,*) - Ivan Z
函数结束时+ 1的需要是什么?我觉得它完美无缺。 - Shachi


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

如果您正在使用,请选择 Underscore.js 您可以使用

_.random(min, max)

26
2018-02-09 19:51



Lodash也提供此功能。 - Walter Roman
Underscore实际上提供了一个 _.uniqueId() 您可以调用客户端模型的功能。 - obfk
使用二元运算符(x << 0, x | 0, ~~x) 代替 Math.floor() 转换 x 成为一个双补,范围小于 Number.MAX_SAFE_INTEGER (2³²-1vs.2⁵³),因此您必须谨慎使用它! - le_m


其他答案没有考虑到完全合理的参数 0 和 1。相反,你应该使用 round  代替 的 ceil 要么 floor

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

12
2017-07-21 16:42



你的答案是对的,但我认为你的例子是错的.. console.log(randomNumber(5,6)); # 9 6 6 5 7 7 9和7是5到6之间吗? ......你应该纠正它或解释.. - Sachin


在使用计算机程序生成随机数之后,如果所选择的数字是初始数字的一部分或全部,则仍将其视为随机数。但如果它改变了,那么 数学家不接受它作为随机数,他们可以称之为有偏差的数字。 但是如果你正在为一个简单的任务开发一个程序,那么这不是一个需要考虑的案例。 但是,如果您正在开发一个程序来为有价值的东西(如彩票计划或赌博游戏)生成随机数,那么如果您不考虑上述情况,您的程序将被管理层拒绝。

所以对于那些人,这是我的建议:

使用生成随机数 Math.random()。(说这个 n

Now for [0,10) ==>  n*10 (i.e. one digit) and for[10,100) ==> n*100 (i.e. two digits) and so on. Here squire bracket indicates that boundary is inclusive and round bracket indicates boundary is exclusive.
Then remove the rest after the decimal point. (i.e. get floor) - using Math.floor(), this can be done.

如果你知道如何读的随机数表选择一个随机数,要知道上述过程(由1,10,100等相乘)是不是违反了我在开头提到的一个。(因为它只是改变小数点的位置。)

研究以下示例并根据您的需求进行开发。

如果您需要样本[0,9],那么n * 10的楼层是您的答案,如果需要[0,99]那么n * 100的楼层是您的答案,依此类推。

现在让我们进入你的角色:

您已经询问了特定范围内的数字。 (在这种情况下,你在这个范围内有偏差。 - 通过掷骰子从[1,6]中取一个数字,然后你偏向[1,6]但是它仍然是随机的,只有当死是无偏的时候。)

所以考虑你的范围==> [78,247] 范围的元素数= 247 - 78 + 1 = 170; (因为两个边界都是包容性的。

/*Mthod 1:*/
    var i = 78, j = 247, k = 170, a = [], b = [], c, d, e, f, l = 0;
    for(; i <= j; i++){ a.push(i); }
    while(l < 170){
        c = Math.random()*100; c = Math.floor(c);
        d = Math.random()*100; d = Math.floor(d);
        b.push(a[c]); e = c + d;
        if((b.length != k) && (e < k)){  b.push(a[e]); }
        l = b.length;
    }
    console.log('Method 1:');
    console.log(b);
/*Method 2:*/

    var a, b, c, d = [], l = 0;
    while(l < 170){
        a = Math.random()*100; a = Math.floor(a);
        b = Math.random()*100; b = Math.floor(b);
        c = a + b;
        if(c <= 247 || c >= 78){ d.push(c); }else{ d.push(a); }
        l = d.length;
    }
    console.log('Method 2:');
    console.log(d);

注意:在方法一中,首先我创建了一个包含所需数字的数组,然后将它们随机地放入另一个数组中。 在方法二中,随机生成数字并检查它们是否在您需要的范围内。然后把它放到一个数组中。在这里,我生成了两个随机数,并使用它们的总和,通过最小化获得有用数字的失败率来最大化程序的速度。但是,添加生成的数字也会带来一些双重性。所以我建议我的第一种方法在特定范围内生成随机数。

在这两种方法中,您的控制台都会显示结果。(在Chrome中按f12打开控制台)


9
2018-02-10 13:22



“随机”并不一定意味着“均匀分布”。 “有偏见”并不意味着“非随机”。从概率分布中抽取的随机方法。 - syzygy
几乎无法说出这个答案想说的是什么。但是,如果您需要随机数字用于彩票号码和赌博等用途。首先,您可能不应该在客户端上生成它们。其次,您需要一个加密安全的随机数生成器,并且提供的算法是不够的。反复调用随机不会使结果“更随机”。作者似乎关注偏见,但并未提供防止它的好算法。实际上,提供的其他简短答案产生无偏的随机数(假设基础随机生成器是无偏的)。 - Jeff Walker Code Ranger
我试了但是索引不行(解决方案1)。 - loretoparisi
@JeffWalkerCodeRanger我认为他的意思是使用“正常”算法[即 Math.floor(Math.random() * (6 - 1 + 1) + 1)数字1和6必然会比2,3,4和5更少次滚动。但是,差异基本上是微不足道的。 - Anthony