当使用类java.util.Random时,如何才能获得通过N次调用nextInt()方法而获得的值,但是以一种更为有效的方式(特别是在O(1)中)?
例如,如果我构造一个具有特定种子值的Random对象,并且想要快速获得第100,000个“ nextInt()值”(即,调用方法nextInt()100,000次后获得的值) ,我可以吗?
为简单起见,假定JDK的版本1.7.06是因为可能需要知道类Random中某些私有字段的确切值。可以这样说,我发现以下字段与随机值的计算有关:
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
Run Code Online (Sandbox Code Playgroud)
在研究了一些随机性之后,我发现使用线性同余生成器可以获取随机值。执行算法的实际方法是next(int)方法:
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
Run Code Online (Sandbox Code Playgroud)
该算法的相关行是获得下一个种子值的行:
nextseed = (oldseed * multiplier + addend) & mask;
Run Code Online (Sandbox Code Playgroud)
因此,更具体地说,是否有一种方法可以概括该公式以获得“ n个下一个种子”值?我在这里假设,有了这个之后,我可以通过让变量“ bits”为32来简单地获取第n个int值(方法nextInt()只需调用next(32)并返回结果)。
提前致谢
PS:也许这是一个更适合Mathexchange的问题?
您可以O(log N)
及时完成。与开始s(0)
,如果我们忽略了模数(2 48的时刻),我们可以看到(使用m
并a
作为速记multiplier
和addend
)那
s(1) = s(0) * m + a
s(2) = s(1) * m + a = s(0) * m² + (m + 1) * a
s(3) = s(2) * m + a = s(0) * m³ + (m² + m + 1) * a
...
s(N) = s(0) * m^N + (m^(N-1) + ... + m + 1) * a
Run Code Online (Sandbox Code Playgroud)
现在,m^N (mod 2^48)
可以O(log N)
通过重复平方的模幂轻松地逐步计算。
另一部分比较复杂。暂时再次忽略模量,其几何和为
(m^N - 1) / (m - 1)
Run Code Online (Sandbox Code Playgroud)
使此模2^48
的计算有些微不足道的是,m - 1
它不是模数的互质数。但是,由于
m = 0x5DEECE66DL
Run Code Online (Sandbox Code Playgroud)
的最大公约数m-1
和模数为4,并且(m-1)/4
具有模逆inv
模2^48
。让
c = (m^N - 1) (mod 4*2^48)
Run Code Online (Sandbox Code Playgroud)
然后
(c / 4) * inv ? (m^N - 1) / (m - 1) (mod 2^48)
Run Code Online (Sandbox Code Playgroud)
所以
M ? m^N (mod 2^50)
inv
获得
s(N) ? s(0)*M + ((M - 1)/4)*inv*a (mod 2^48)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1130 次 |
最近记录: |