4

这可能比编程更数学问题。在 JS 中,我想要一个在间隔中返回随机整数的函数,比如说 1-6,这就是我发现的:

// Returns a random integer between min and max
// Using Math.round() will give you a non-uniform distribution!
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

如果我将其复制并粘贴到我的代码中,我会感到内疚。我不明白:为什么我们从最大值中减去最小值,加 1,将答案乘以 Math.random(),然后加上最小值。我厌倦了在纸上手动输入几个数字,它工作得很好!但我不明白为什么!

4

4 回答 4

6

Assuming you already understand the behaviour of Math.floor and Math.random, here's the rest step by step:

  • Math.random() ↝ a random number between 0 (inclusive) and 1 (exclusive)
  • Math.random() * max ↝ a random number between 0 (inclusive) and max (exclusive)
  • Math.floor(Math.random() * max) ↝ a random integer between 0 (incl.) and max (excl.)
  • Math.floor(Math.random() * (max - min)) + min ↝ a random integer between min (incl.) and max (excl.)
  • Math.floor(Math.random() * ((max + 1) - min)) + min ↝ a random integer between min (incl.) and max+1 (excl.) (OR between min and max both inclusive)
于 2013-07-07T22:05:43.530 回答
3

Math.random()会给你一个从 0 到 1 的“真实”数字(不包括 1.0)。

这很酷,但是如果我想要一个从 1 到 2 的“真实”数字怎么办?

答案:将您的 [0,1)“转换”为 [1,2)。

实际上,这意味着将结果加 1。

试试看——Math.random()+1会给你一个从 1 到 2 的数字。

在数学中,这被称为“映射”。也就是说——对于 [0,1) 中的每个可能的实数,找到一种方法将该实数“映射”到 [1,2) 中的另一个实数。也就是说,如果我给您 [0,1) 之间的任何实数,您应该能够映射该数字——将该数字应用于将返回 [1,2) 之间的数字的函数。

在我们的例子中,函数 f(x) = x+1。

你看到这给了我们[1,2)之间的随机数吗?可视化两个相邻的区间,并想象一条线从 [0,1) 中的每个点到 [1,2) 中的对应地图。现在,在 [0,1) ... 上选择一个随机点,然后沿着这条线走。您将沿着这条线到达 [1,2) 中的一个随机点!

现在,从 [0,1) 到 [1,2) 的所有完整的一对一映射都会将 [0,1) 之间的随机数转换为 [1,2) 之间的随机数......但不是所有的他们会给你一个在 [1,2) 之间均匀分布的随机数。映射为您提供均匀分布的结果背后的数学有点复杂,但简而言之,如果您的映射只涉及加、减、乘和除以常数,那么从结果也将均匀分布的意义上说,它是“合法的”。

所以,现在我们知道如何将 [0,1) 转换为 [1,2)。

如果我想将 [0,1) 映射到 [0,2) 上怎么办?我不能再添加数字了...

我将所有内容乘以 2 怎么样?

这应该可行——函数 f(x) = x*2 确实将 [0,1) 上的每个点映射到 [0,2) 上的一个点 —— 因为它只涉及乘以常数 (2),它是一个保持分布的映射。

这行得通! Math.random()*2会给你一个 0 到 2 之间的随机数。

好的,现在有点复杂...将 [0,1) 转换为 [1,3)。

乘以 2 不起作用... 0*2 = 0,这不在您的目标范围内。添加一个不起作用...即使 0+1 在您的目标范围内并且 1+1 也在您的目标范围内,但您永远无法达到 3。

如果我们不能将 [0,1) 转换为 [1,3),让我们尝试看看是否可以将其他东西转换为 [1,3)。

[0,2) 怎么样?是的,我们可以这样做……函数 f(x) = x+1 完美地将 [0,2) 映射到 [1,3)。您可以将其+视为“向上移动”范围。

所以这里的解决方案很明确——首先,将[0,1)变为[0,2),然后将[0,2)变为[1,3)。

我们已经知道第一个 (f(x) = x*2),并且我们计算出了第二个 (f(x) = x+1)。所以“组合”变换/映射是 f(x) = (x*2)+1。

也就是说,Math.random()*2 + 1会给你一个从 0 到 3 的数字。

现在是最后一招……将 [0,1) 映射到任意范围 [min,max)。

这里的秘诀是将其重写为 [min,min+range),其中 range = max-min。

在这里您可以看到将范围 [0,range) 转换为 [min,min+range) 很简单——您只需将“min”添加到它。所以如果我有范围 [0,range),并且我想得到 [min,min+range),我会使用 f(x) = x+min。

那么我们如何从 [0,1) 到 [0,range) ?

乘以范围!

f(x) = (x*range) + min

现在使用 range = max-min 将内容写回原始术语

f(x) = (x*(max-min)) + min

将实数从 [0,1) 转换为实数 [min,max)

我会把剩下的(把它变成一个有用的整数)留给你

于 2013-07-07T22:13:22.433 回答
1
0 <= Math.random() < 1  =>
0 <= Math.random() * 6 < 6 =>
0 <= Math.floor( Math.random() * 6 ) <= 5   

然后你添加 'min' 所以它看起来像这样:

min <= Math.floor( Math.random() * 6 ) <= 5 + min

在您的示例中,对于 min = 1,您将拥有 1-6 中的所有数字。

我希望现在很清楚。

于 2013-07-07T22:13:05.803 回答
1

这是您的代码的解释:

  • Math.random()生成一个介于 0 和 1 之间的随机数(不包括 1)。
  • 您需要根据所需的数字范围来缩放该值。min你的范围是从你想要的数字到你max想要的数字有多远max - min
  • 如果要将max值包含在生成的数字范围内,请使用max - min + 1
  • 然后,您需要确保随机数从正确的基数开始,而不是0添加min到它。
  • 然后,如果您希望它是一个整数,您可以调用Math.floor()将其截断为下一个最低整数。

所以,如果你有这个:

Math.floor(Math.random())

你总是会得到零。由于Math.floor()介于 0 和 1(不包括 1)之间的浮点值将始终截断为 0。

然后,如果您使用以下方法扩展范围:

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

max - min您现在将获得一个介于 0 和包括较大值之间的随机数。

所以,为了让它从正确的基础开始,你可以min这样添加:

Math.floor(Math.random() * (max - min + 1)) + min
于 2013-07-07T22:03:58.730 回答