Java中如何无需调用100次nextInt()获取指定种子的第100个随机整数
nextInt() Calls: Directly Get the 100th Random Value from a Seed Awesome question! Let's dig into how Java's Random class works under the hood to figure out a way to skip those 100 iterations entirely.
Background: How Java's Random Generates Values
Java's Random uses a Linear Congruential Generator (LCG) under the hood. Every time you call nextInt(), it first updates its internal seed using this formula:
nextSeed = (currentSeed * MULTIPLIER + ADDEND) & MASK
Where:
MULTIPLIER = 0x5DEECE66DLADDEND = 0xBLMASK = (1L << 48) - 1(limits the seed to 48 bits)
Then, nextInt() extracts the top 32 bits of this new seed (via (int)(nextSeed >>> 16)) to return as the integer value.
The Key Insight: Compute the 100th Seed Directly
Instead of iterating 100 times to update the seed step-by-step, we can use the mathematical properties of LCGs to calculate the 100th seed directly. The LCG follows a recursive formula that has a closed-form solution:
For an LCG defined as S(n) = a*S(n-1) + c (where a=MULTIPLIER, c=ADDEND), the nth seed is:
S(n) = (aⁿ * S(0) + c*(aⁿ - 1)/(a - 1)) mod MASK
All operations are performed modulo MASK to keep the seed within 48 bits.
Implementation Code
Here's a complete Java method that computes the 100th nextInt() value without looping 100 times. It works for any n (not just 100):
public static int getNthRandomInt(long initialSeed, int n) { final long MULTIPLIER = 0x5DEECE66DL; final long ADDEND = 0xBL; final long MASK = (1L << 48) - 1; // Adjust initial seed to match Random's constructor logic long seed = (initialSeed ^ MULTIPLIER) & MASK; // Compute aⁿ mod MASK using fast exponentiation (O(log n) time) long aPower = powMod(MULTIPLIER, n, MASK); // Calculate the sum of the geometric series: c*(1 + a + a² + ... + a^(n-1)) mod MASK long sum = geometricSeriesSum(MULTIPLIER, n, MASK); sum = (sum * ADDEND) % MASK; // Compute the nth seed using the closed-form formula long finalSeed = (aPower * seed + sum) % MASK; // Generate the int value exactly like nextInt() does return (int)(finalSeed >>> 16); } // Fast exponentiation to compute (base^exponent) mod modu private static long powMod(long base, int exponent, long modu) { long result = 1; base = base % modu; while (exponent > 0) { if (exponent % 2 == 1) { result = (result * base) % modu; } base = (base * base) % modu; exponent /= 2; } return result; } // Compute sum_{k=0 to n-1} base^k mod modu (handles even denominators for LCG) private static long geometricSeriesSum(long base, int n, long modu) { if (n == 0) return 0; if (base == 1) return n % modu; long numerator = (powMod(base, n, modu) - 1 + modu) % modu; long denominator = base - 1; // Factor out trailing zeros from denominator (since it's even) int shift = Long.numberOfTrailingZeros(denominator); denominator >>= shift; numerator >>= shift; // Compute modular inverse of the odd denominator long invDenominator = modInverse(denominator, 1L << (48 - shift)); return (numerator * invDenominator) % (1L << (48 - shift)); } // Extended Euclidean algorithm to find modular inverse (for odd mod) private static long modInverse(long a, long mod) { long m0 = mod; long y = 0, x = 1; if (mod == 1) return 0; while (a > 1) { long q = a / mod; long t = mod; mod = a % mod; a = t; t = y; y = x - q * y; x = t; } if (x < 0) x += m0; return x; }
How to Use It
For your specific case (seed=0, n=100), just call:
int result = getNthRandomInt(0, 100); System.out.println(result);
This will give you exactly the same value as running your original loop 100 times.
Important Notes
- Only for Java's
Random: This method relies on the exact LCG implementation ofjava.util.Random. It won't work for other random number generators likeSecureRandom(which uses a different algorithm). - Efficiency: For large
n(like 1,000,000), this method runs in O(log n) time, which is way faster than loopingntimes.
内容的提问来源于stack exchange,提问作者Kandera

