关于数学:JavaScript随机数

JavaScript Random Number

本问题已经有最佳答案,请猛点这里访问。

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

1
2
3
4
5
// 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;
}

如果我把这个复制粘贴到我的代码中,我会感到内疚。我不明白:为什么我们从max中减去min,加1,用math.random()乘以答案,然后再加min。我在纸上手工画了几个数字,结果很好!但我不明白为什么!


假设你已经了解了Math.floorMath.random的行为,下面是一步一步的:

  • Math.random()01之间的随机数(不含)
  • Math.random() * max号?0max之间的随机数(不含)
  • Math.floor(Math.random() * max)号?在0max之间的一个随机整数。
  • Math.floor(Math.random() * (max - min)) + min号?在minmax之间的一个随机整数。
  • Math.floor(Math.random() * ((max + 1) - min)) + min号?minmax+1之间(或minmax之间)的随机整数。

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]上怎么办?我不能再增加数字了…

我把所有的都乘以二怎么样?

这应该有效——函数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]映射到任意范围[最小值,最大值]。

这里的秘密是将其重新写入[最小值,最小值+范围],其中范围=最大值-最小值。

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

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

乘以范围!

F(x)=(x*范围)+最小值

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

F(x)=(x*(max min))+min

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

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


1
2
3
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中的所有数字。

我希望现在很清楚。


以下是您的代码说明:

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

所以,如果你有这个:

1
Math.floor(Math.random())

你总是会得到零。因为0到1(不包括1)之间的浮点值的Math.floor()总是截断为0。

然后,如果扩展范围:

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

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

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

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