Java 如何获得第n个随机变量;nextInt";价值

Java 如何获得第n个随机变量;nextInt";价值,java,random,Java,Random,当使用类java.util.Random时,如何才能以更有效的方式(特别是在O(1)中)N次调用方法nextInt()获得值 例如,如果我用一个特定的种子值构造一个随机对象,并且我想快速获得第100000个“nextInt()值”(即调用方法nextInt()100000次后获得的值),我可以这样做吗 为了简单起见,假设JDK版本为1.7.06,因为可能需要知道Random类中某些私有字段的确切值。说到,我发现以下字段与计算随机值相关: private static final long mul

当使用类java.util.Random时,如何才能以更有效的方式(特别是在O(1)中)N次调用方法nextInt()获得值

例如,如果我用一个特定的种子值构造一个随机对象,并且我想快速获得第100000个“nextInt()值”(即调用方法nextInt()100000次后获得的值),我可以这样做吗

为了简单起见,假设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;
算法的相关行是获得下一个种子值的行:

nextseed = (oldseed * multiplier + addend) & mask;
那么,更具体地说,有没有一种方法可以推广这个公式,以获得“第n个nextseed”值?我在这里假设,在得到这个值之后,我可以简单地通过让变量“bits”为32来获得第n个int值(方法nextInt()只调用next(32)并返回结果)

提前谢谢

PS:也许这是一个更适合你的问题?

你可以在
O(logn)
时间内完成。从
s(0)
开始,如果我们暂时忽略模数(248),我们可以看到(使用
m
a
作为
乘法器
加数
的缩写)

现在,
m^N(mod 2^48)
可以在
O(logn)
步骤中通过重复平方的模幂运算轻松计算

另一部分比较复杂。再次忽略当前的模量,几何和为

(m^N - 1) / (m - 1)
计算模
2^48
有点不重要的是
m-1
不是模的互质。但是,

m = 0x5DEECE66DL
m-1
的最大公约数和模为4,
(m-1)/4
具有模逆
inv
2^48
。让

c = (m^N - 1) (mod 4*2^48)
然后

所以

  • 计算
    M≡ m^N(模块2^50)
  • 计算
    inv
取得

s(N) ≡ s(0)*M + ((M - 1)/4)*inv*a (mod 2^48)

我接受了Daniel Fischer的答案,因为它是正确的,并给出了一般的解决方案。根据Daniel的回答,下面是一个java代码的具体示例,它展示了公式的基本实现(我广泛使用了BigInteger类,因此它可能不是最优的,但我确认,与实际调用方法nextInt()N次的基本方式相比,它有了显著的加速):

import java.math.biginger;
导入java.util.Random;
公共类{
//从java.util.Random复制=========================
专用静态最终长乘数=0x5deec66dl;
专用静态最终长加数=0xBL;
专用静态最终长掩码=(1L>>(48-32));
}
// ======================================================
私有静态最终BigInteger mod=BigInteger.valueOf(掩码+1L);
私有静态final biginger inv=biginger.valueOf((乘数-1L)/4L).modInverse(mod);
/**
*返回从函数调用方法{@link Random#nextInt()}{@code n}次后获得的值
*{@link Random}对象用{@code seed}值初始化。
*
*此方法实际上并不创建任何{@code Random}实例,而是应用一个直接公式
*以更有效的方式计算期望值(接近O(logn))。
* 
*@param种子
*假定的{@code Random}对象的初始种子值
*@param n
*“nextInt()值”的索引(从1开始)
*@返回用给定种子值初始化的{@code Random}对象的第n个“nextInt()值”
*@galargumentException
*如果{@code n}不是正的
*/
公共静态长getNthNextInt(长种子,长n){
如果(n<1L){
抛出新的IllegalArgumentException(“n必须为正”);
}
final biginger seedZero=biginger.valueOf(initialScramble(seed));
最终BigInteger nthSeed=calculateNthSeed(seedZero,n);
返回getNextInt(nthSeed.longValue());
}
私有静态BigInteger计算种子(BigInteger种子0,长n){
final biginger largeM=calculateLargeM(n);
final biginger largeMmin1div4=largeM.subtract(biginger.ONE).divide(biginger.valueOf(4L));
返回seed0.multiply(largeM).add(largeMmin1div4.multiply(inv).multiply(biginger.valueOf(addend)).mod(mod);
}
私有静态BigInteger计算器argem(长n){

返回biginger.valueOf(multiplier.modPow(biginger.valueOf(n),biginger.valueOf(1L谢谢您的帮助。请参阅我的答案,了解java中的具体实现。很好。我现在已经太晚了,无法实际实现它;)请注意,由于模是2的幂,Java保证对
long
算术进行环绕,即算术模2^64,因此实际上不需要使用
biginger
,如果性能非常关键,使用纯
long
可能会更快。是的,我认为可能有一些捷径。不过,最终,什么首先让我使用BigInteger的是modPow(power,mod)和modInverse(mod)等函数的即时可用性.回想起来,看看代码,我仍然可以使用BigInteger作为modInverse函数,因为它只计算一次。是的,
BigInteger
减轻了您编写自己函数的负担。您根本不需要调用
modInverse
,您可以计算一次并让它
private static final long Reverse=0x11018AFE8493L;
。如果更改了乘数,则只需计算它,然后还需要调整2除以
乘数-1的幂。
c = (m^N - 1) (mod 4*2^48)
(c / 4) * inv ≡ (m^N - 1) / (m - 1) (mod 2^48)
s(N) ≡ s(0)*M + ((M - 1)/4)*inv*a (mod 2^48)
import java.math.BigInteger;
import java.util.Random;


public class RandomNthNextInt {

    // copied from java.util.Random =========================
    private static final long   multiplier  = 0x5DEECE66DL;
    private static final long   addend      = 0xBL;
    private static final long   mask        = (1L << 48) - 1;


    private static long initialScramble(long seed) {

        return (seed ^ multiplier) & mask;
    }

    private static int getNextInt(long nextSeed) {

        return (int)(nextSeed >>> (48 - 32));
    }
    // ======================================================

    private static final BigInteger mod = BigInteger.valueOf(mask + 1L);
    private static final BigInteger inv = BigInteger.valueOf((multiplier - 1L) / 4L).modInverse(mod);


    /**
     * Returns the value obtained after calling the method {@link Random#nextInt()} {@code n} times from a
     * {@link Random} object initialized with the {@code seed} value.
     * <p>
     * This method does not actually create any {@code Random} instance, instead it applies a direct formula which
     * calculates the expected value in a more efficient way (close to O(log N)).
     * 
     * @param seed
     *            The initial seed value of the supposed {@code Random} object
     * @param n
     *            The index (starting at 1) of the "nextInt() value"
     * @return the nth "nextInt() value" of a {@code Random} object initialized with the given seed value
     * @throws IllegalArgumentException
     *             If {@code n} is not positive
     */
    public static long getNthNextInt(long seed, long n) {

        if (n < 1L) {
            throw new IllegalArgumentException("n must be positive");
        }

        final BigInteger seedZero = BigInteger.valueOf(initialScramble(seed));
        final BigInteger nthSeed = calculateNthSeed(seedZero, n);

        return getNextInt(nthSeed.longValue());
    }

    private static BigInteger calculateNthSeed(BigInteger seed0, long n) {

        final BigInteger largeM = calculateLargeM(n);
        final BigInteger largeMmin1div4 = largeM.subtract(BigInteger.ONE).divide(BigInteger.valueOf(4L));

        return seed0.multiply(largeM).add(largeMmin1div4.multiply(inv).multiply(BigInteger.valueOf(addend))).mod(mod);
    }

    private static BigInteger calculateLargeM(long n) {

        return BigInteger.valueOf(multiplier).modPow(BigInteger.valueOf(n), BigInteger.valueOf(1L << 50));
    }

    // =========================== Testing stuff ======================================

    public static void main(String[] args) {

        final long n = 100000L; // change this to test other values
        final long seed = 1L; // change this to test other values

        System.out.println(n + "th nextInt (formula) = " + getNthNextInt(seed, n));
        System.out.println(n + "th nextInt (slow)    = " + getNthNextIntSlow(seed, n));
    }

    private static int getNthNextIntSlow(long seed, long n) {

        if (n < 1L) {
            throw new IllegalArgumentException("n must be positive");
        }

        final Random rand = new Random(seed);
        for (long eL = 0; eL < (n - 1); eL++) {
            rand.nextInt();
        }
        return rand.nextInt();
    }
}