简体   繁体   English

预测Javascript的Math.random的种子

[英]Predict the Seed of Javascript's Math.random

Okay, so I'm doing some research on how random numbers are generated with the Math.random method. 好的,所以我正在研究如何使用Math.random方法生成随机数。 So far I learned that it starts with a "random" seed, and that seed is plugged into some complex equation to create a random number. 到目前为止,我学会了它从一个“随机”种子开始,并将该种子插入到一些复杂的方程中以创建一个随机数。 If the seed is always the same, will the outcome always be the same? 如果种子总是一样,结果总是一样吗?

I heard that the seeds for Math.random are generated through the current time, is that correct? 我听说Math.random的种子是通过当前时间生成的,这是正确的吗? They must use the current time all the way down to the mili-seconds or something, because if you didn't you would get the same outcome. 他们必须使用当前时间一直到mili-seconds或其他东西,因为如果你没有,你会得到相同的结果。

What exactly is the seed? 种子究竟是什么? Is it the time such as "10:45" or the time AND date such as "10:45 11/8/12" or some combination? 是“10:45”之类的时间,还是“10月11日10:45”或某种组合的时间和日期?

How can I find the seed, so I can predict the output? 我怎样才能找到种子,所以我可以预测输出?

I want to be able to plug this: 我希望能够插入这个:

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

into my url bar, and be able to predict the result. 进入我的网址栏,并能够预测结果。 Is that possible? 那可能吗?

I looked through the Rhino source code to find out which pseudo-random function they use. 我浏览了Rhino 源代码 ,找出他们使用的伪随机函数。 Apparently they fall back to the Math.random function defined in the Java standard library . 显然,它们回退到 Java标准库中定义的Math.random函数。

The documentation for Math.random says: Math.random的文档说:

Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0. 返回带有正号的double值,大于或等于0.0且小于1.0。 Returned values are chosen pseudorandomly with (approximately) uniform distribution from that range. 返回值是伪随机选择的,具有来自该范围的(近似)均匀分布。

When this method is first called, it creates a single new pseudorandom-number generator, exactly as if by the expression 首次调用此方法时,它会创建一个新的伪随机数生成器,就像表达式一样

new java.util.Random

This new pseudorandom-number generator is used thereafter for all calls to this method and is used nowhere else. 此后,此新伪随机数生成器用于对此方法的所有调用,并且在其他任何地方都不使用。

This method is properly synchronized to allow correct use by more than one thread. 此方法已正确同步,以允许多个线程正确使用。 However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator. 但是,如果许多线程需要以很高的速率生成伪随机数,则可以减少每个线程拥有自己的伪随机数生成器的争用。

So I checked the documentation for java.util.Random and found this (for the default constructor): 所以我检查了java.util.Random的文档并找到了这个 (对于默认的构造函数):

Creates a new random number generator. 创建一个新的随机数生成器。 Its seed is initialized to a value based on the current time: 其种子初始化为基于当前时间的值:

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

Two Random objects created within the same millisecond will have the same sequence of random numbers. 在同一毫秒内创建的两个随机对象将具有相同的随机数序列。

So now we know for sure that the seed is the current time in milliseconds. 所以现在我们确定种子是以毫秒为单位的当前时间。 Also, the documentation for the second constructor says: 此外, 第二个构造函数的文档说:

Creates a new random number generator using a single long seed: 使用单个长种子创建新的随机数生成器:

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

Used by method next to hold the state of the pseudorandom number generator. 由旁边的方法用于保持伪随机数生成器的状态。

The documentation for the setSeed method says: setSeed方法的文档说:

Sets the seed of this random number generator using a single long seed. 使用单个长种子设置此随机数生成器的种子。 The general contract of setSeed is that it alters the state of this random number generator object so as to be in exactly the same state as if it had just been created with the argument seed as a seed. setSeed的一般契约是它改变了这个随机数生成器对象的状态,以便与刚刚用参数种子作为种子创建的状态完全相同。 The method setSeed is implemented by class Random as follows: 方法setSeed由Random类实现,如下所示:

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

The implementation of setSeed by class Random happens to use only 48 bits of the given seed. 类Random的setSeed的实现恰好只使用给定种子的48位。 In general, however, an overriding method may use all 64 bits of the long argument as a seed value. 然而,通常,重写方法可以使用长参数的所有64位作为种子值。 Note: Although the seed value is an AtomicLong, this method must still be synchronized to ensure correct semantics of haveNextNextGaussian. 注意:虽然种子值是AtomicLong,但仍必须同步此方法以确保hasNextNextGaussian的正确语义。

The actual method used to generate the random number is nextDouble : 用于生成随机数的实际方法nextDouble

Returns the next pseudorandom, uniformly distributed double value between 0.0 and 1.0 from this random number generator's sequence. 返回下一个伪随机数,从此随机数生成器的序列中均匀分布介于0.0和1.0之间的double值。

The implementation of the nextDouble function is as follows: nextDouble函数的实现如下:

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

Clearly it depends on the next function: 显然它取决于 next功能:

Generates the next pseudorandom number. 生成下一个伪随机数。 Subclass should override this, as this is used by all other methods. 子类应该覆盖它,因为所有其他方法都使用它。

The implementation of the next function is as follows: next功能的实现如下:

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

That's the pseudo-random function you are looking for. 那是你正在寻找的伪随机函数。 As it's said in the documentation: 正如文档中所述:

This is a linear congruential pseudorandom number generator, as defined by DH Lehmer and described by Donald E. Knuth in The Art of Computer Programming, Volume 2: Seminumerical Algorithms, section 3.2.1. 这是一个线性同余伪随机数发生器,由DH Lehmer定义并由Donald E. Knuth在The Computer of Computer Programming,Volume 2:Seminumerical Algorithms,3.2.1节中描述。

Note however that this is only the random number generator used by Rhino. 但请注意,这只是Rhino使用的随机数生成器。 Other implementations like Spidermonkey and V8 may have their own pseudo-random number generators. 其他实现如Spidermonkey和V8可能有自己的伪随机数生成器。

it's likely that there's more to the seed than the millisecond count, because you can call Math.random() many times in the same millisecond and it will return a different value each time. 种子可能比毫秒计数更多,因为你可以在相同的毫秒内多次调用Math.random(),并且每次都会返回不同的值。

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

My output: 我的输出:

0.0617244818713516 1352433709108
0.8024995378218591 1352433709108
0.2409922298975289 1352433709108

If I were implementing it I might make the initial seed based on a millisecond count, and then add 1 each time it's called, so that you wouldn't get the same seed value twice. 如果我正在实现它,我可能会根据毫秒计数生成初始种子,然后在每次调用时添加1,这样您就不会获得两次相同的种子值。

Here's a 100% accurate way of predicting the output from Math.random() : 这是一种100%准确的方法来预测Math.random()的输出:

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

Now Math.random() will always return .5 . 现在Math.random()将始终返回.5

The seed is a numeric value, so my guess is that it would be what you get if you call Date.now() (or new Date().getTime() for older browsers). 种子是一个数值,所以我的猜测是,如果你调用Date.now() (或旧的浏览器的new Date().getTime() ,它将是你得到的。

However, I'm not sure when that seed is taken, or if the seed is isolated to the current page or common to the entire browser process. 但是,我不确定何时使用该种子,或者种子是否被隔离到当前页面或整个浏览器进程是否共同。 Predicting random numbers is supposed to be very hard or impossible, that's the whole point of them being random. 预测随机数应该是非常困难或不可能的,这就是它们的随机性。

No, you cannot predict the seed, but you can preemtively generate enough numbers in order to accurately brute force a match. 不,你无法预测种子,但你可以预先生成足够的数字,以便准确地强制匹配。

Anyhow, start of by reading the wiki page on RNG's - http://en.wikipedia.org/wiki/Random_number_generation , the look at the practical implementations of PRNG's. 无论如何,通过阅读RNG的维基页面开始 - http://en.wikipedia.org/wiki/Random_number_generation ,看看PRNG的实际实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM