8

好的,所以我正在研究如何使用 Math.random 方法生成随机数。到目前为止,我了解到它以“随机”种子开始,并将该种子插入一些复杂的方程以创建随机数。如果种子永远一样,结果会永远一样吗?

我听说 Math.random 的种子是通过当前时间生成的,对吗?他们必须使用当前时间一直到毫秒或其他时间,因为如果你不这样做,你会得到相同的结果。

种子究竟是什么?是诸如“10:45”之类的时间还是诸如“10:45 11/8/12”之类的时间和日期或某种组合?

我怎样才能找到种子,所以我可以预测输出?

我希望能够插入这个:

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

进入我的网址栏,并能够预测结果。那可能吗?

4

4 回答 4

15

我查看了 Rhino源代码以找出它们使用的伪随机函数。显然,它们回退到Java 标准库Math.random中定义的函数。

的文档Math.random说:

返回一个带正号的双精度值,大于或等于 0.0 且小于 1.0。返回值是伪随机选择的,从该范围(近似)均匀分布。

当这个方法第一次被调用时,它会创建一个新的伪随机数生成器,就像通过表达式一样

new java.util.Random

这个新的伪随机数生成器随后用于对该方法的所有调用,并且不会在其他任何地方使用。

此方法已正确同步,以允许多个线程正确使用。但是,如果许多线程需要以很高的速率生成伪随机数,则可能会减少每个线程对拥有自己的伪随机数生成器的争用。

所以我检查了文档java.util.Random并找到了这个(对于默认构造函数):

创建一个新的随机数生成器。它的种子根据当前时间初始化为一个值:

public Random() { this(System.currentTimeMillis()); }

在同一毫秒内创建的两个 Random 对象将具有相同的随机数序列。

所以现在我们确定种子是当前时间(以毫秒为单位)。此外,第二个构造函数的文档说:

使用单个长种子创建一个新的随机数生成器:

public Random(long seed) { setSeed(seed); }

由方法 next 用来保存伪随机数生成器的状态。

该方法的文档setSeed说:

使用单个长种子设置此随机数生成器的种子。setSeed 的一般约定是,它会更改此随机数生成器对象的状态,使其处于与刚刚使用参数种子作为种子创建时完全相同的状态。方法 setSeed 由 Random 类实现,如下所示:

synchronized public void setSeed(long seed) {
    this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
    haveNextNextGaussian = false;
}

由 Random 类实现的 setSeed 恰好只使用给定种子的 48 位。然而,一般来说,覆盖方法可以使用长参数的所有 64 位作为种子值。注意:虽然种子值是一个 AtomicLong,但这个方法仍然必须同步,以确保 haveNextNextGaussian 的语义正确。

用于生成随机数的实际方法nextDouble是:

从该随机数生成器的序列中返回 0.0 到 1.0 之间的下一个伪随机、均匀分布的双精度值。

nextDouble函数的实现如下:

public double nextDouble() {
    return (((long)next(26) << 27) + next(27))
        / (double)(1L << 53);
}

显然它取决于功能next

生成下一个伪随机数。子类应该覆盖它,因为它被所有其他方法使用。

next函数的实现如下:

synchronized protected int next(int bits) {
    seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
    return (int)(seed >>> (48 - bits));
}

这就是您正在寻找的伪随机函数。正如文档中所说:

这是一个线性同余伪随机数生成器,由 DH Lehmer 定义并由 Donald E. Knuth 在计算机编程艺术,第 2 卷:半数值算法,第 3.2.1 节中描述。

但是请注意,这只是 Rhino 使用的随机数生成器。Spidermonkey 和 V8 等其他实现可能有自己的伪随机数生成器。

于 2012-11-09T06:12:31.310 回答
6

种子可能比毫秒计数更多,因为您可以在同一毫秒内多次调用 Math.random() 并且每次都会返回不同的值。

for (var i = 0; i < 3; i++) {
    console.log(Math.random(), (new Date()).getTime());
};

我的输出:

0.0617244818713516 1352433709108
0.8024995378218591 1352433709108
0.2409922298975289 1352433709108

如果我正在实现它,我可能会根据毫秒计数创建初始种子,然后在每次调用它时加 1,这样您就不会两次获得相同的种子值。

这是一种 100% 准确的预测输出的方法Math.random()

Math.random = function () { return .5; };

现在Math.random()总会回来.5的。

于 2012-11-09T03:55:31.537 回答
0

种子是一个数值,所以我的猜测是,如果你调用它Date.now()(或者new Date().getTime()对于旧浏览器),它会是你得到的。

但是,我不确定何时获取该种子,或者该种子是否与当前页面隔离或对整个浏览器进程通用。预测随机数应该是非常困难或不可能的,这就是随机数的全部意义所在。

于 2012-11-09T03:52:03.433 回答
0

不,您无法预测种子,但您可以先发制人地生成足够的数字,以便准确地蛮力匹配。

无论如何,首先阅读 RNG 上的 wiki 页面 - http://en.wikipedia.org/wiki/Random_number_generation,看看 PRNG 的实际实现。

于 2012-11-09T05:34:03.407 回答