Related Articles

# Java Program to Implement Inversion Method for Random Number Generation

• Last Updated : 23 Jun, 2021

Here we will be going through the inversion method for random number generation in java. So basically we will be illustrating two approaches which are as follows:

1. Shuffling elements in an array
2. Using Collection.shuffle() method

Important Method here namely is setSeed(). It is used to set the seed via setSeed() method of Random class sets of the random number generator using a single long seed.

Attention reader! Don’t stop learning now. Get hold of all the important Java Foundation and Collections concepts with the Fundamentals of Java and Java Collections Course at a student-friendly price and become industry ready. To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

Syntax:

`setSeed()`

Example 1:

## Java

 `// Java Program to implement inversion for random number``// generation`` ` `// Importing Random class from java.util package``import` `java.util.Random;`` ` `// Main class``class` `GFG {`` ` `    ``// Method 1``    ``// To calculate seed value``    ``public` `static` `long` `calcSeed(``long` `nextLong)``    ``{`` ` `        ``// Declaring and initializing variables``        ``// Custom initialization``        ``final` `long` `x = 0x5DEECE66DL;``        ``final` `long` `xinv = 0xdfe05bcb1365L;``        ``final` `long` `y = 0xBL;``        ``final` `long` `mask = ((1L << ``48``) - ``1``);`` ` `        ``long` `a = nextLong >>> ``32``;``        ``long` `b = nextLong & ((1L << ``32``) - ``1``);`` ` `        ``if` `((b & ``0x80000000``) != ``0``)``            ``a++;`` ` `        ``// b had a sign bit, so we need to restore a``        ``long` `q = ((b << ``16``) - y - (a << ``16``) * x) & mask;`` ` `        ``for` `(``long` `k = ``0``; k <= ``5``; k++) {`` ` `            ``long` `rem = (x - (q + (k << ``48``))) % x;``            ``long` `d = (rem + x) % x; ``// force positive`` ` `            ``if` `(d < ``65536``) {`` ` `                ``long` `c = ((q + d) * xinv) & mask;`` ` `                ``if` `(c < ``65536``) {``                    ``return` `((((a << ``16``) + c) - y) * xinv)``                        ``& mask;``                ``}``            ``}``        ``}`` ` `        ``// Throws keyword pops up the message``        ``// as exception is encountered during runtime``        ``throw` `new` `RuntimeException(``"Failed!!"``);``    ``}`` ` `    ``// Method 2``    ``// Main driver method``    ``public` `static` `void` `main(String[] args)``    ``{`` ` `        ``// Setting a random number in main() method by``        ``// creating an object of Random class``        ``Random r = ``new` `Random();`` ` `        ``// Getting the next random number``        ``long` `next = r.nextLong();`` ` `        ``// Print and display the next long value``        ``System.out.println(``"Next long value: "` `+ next);`` ` `        ``// Calling method 1``        ``long` `seed = calcSeed(next);`` ` `        ``// Print and display the ssed value``        ``System.out.println(``"Seed "` `+ seed);`` ` `        ``// setSeed mangles the input,``        ``// so demangling here to get the right output by``        ``// creating another object of Random class``        ``Random r2 = ``new` `Random((seed ^ 0x5DEECE66DL)``                               ``& ((1L << ``48``) - ``1``));`` ` `        ``// Printing the next ssed value``        ``// using nextLong() method``        ``System.out.println(``"Next long value from seed: "``                           ``+ r2.nextLong());``    ``}``}`
Output
```Next long value: 3738641717178320652
Seed 158514749661962
Next long value from seed: 3738641717178320652```

Output explanation:

Random uses a 48-bit seed and a linear congruential generator. These are not cryptographically safe generators, because of the tiny state size and the fact that the output just isn’t that random (many generators will exhibit small cycle length in certain bits, meaning that those bits can be easily predicted even if the other bits seem random).

Random’s seed update is as follows:

`nextseed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)`

This is a very simple function, and it can be inverted if you know all the bits of the seed by calculating

`seed = ((nextseed - 0xBL) * 0xdfe05bcb1365L) & ((1L << 48) - 1)`

Since 0x5DEECE66DL * 0xdfe05bcb1365L = 1 mod 248. With this, a single seed value at any point in time suffices to recover all past and future seeds.

Random has no functions that reveal the whole seed, though, so we’ll have to be a bit clever.

Now, obviously, with a 48-bit seed, you have to observe at least 48 bits of output or you clearly don’t have an injective (and thus invertible) function to work with. We’re in luck: nextLong returns ((long)(next(32)) << 32) + next(32);, so it produces 64 bits of output (more than we need). Indeed, we could probably make do with nextDouble (which produces 53 bits), or just repeated calls of any other function. Note that these functions cannot output more than 248 unique values because of the seed’s limited size (hence, for example, there are 264-248 longs that nextLong will never produce).

Let’s specifically look at nextLong. It returns a number (a << 32) + b where a and b are both 32-bit quantities. Let s be the seed before nextLong is called. Then, let t = s * 0x5DEECE66DL + 0xBL, so that a is the high 32 bits of t, and let u = t * 0x5DEECE66DL + 0xBL so that b is the high 32 bits of u. Let c and d be the low 16 bits of t and u respectively.

Note that since c and d are 16-bit quantities, we can just bruteforce them (since we only need one) and be done with it. That’s pretty cheap since 216 is only 65536 — tiny for a computer. But let’s be a bit more clever and see if there’s a faster way.

We have (b << 16) + d = ((a << 16) + c) * 0x5DEECE66DL + 11. Thus, doing some algebra, we obtain (b << 16) – 11 – (a << 16)*0x5DEECE66DL = c*0x5DEECE66DL – d, mod 248. Since c and d are both 16-bit quantities, c*0x5DEECE66DL has at most 51 bits. This usefully means that

`(b << 16) - 11 - (a << 16)*0x5DEECE66DL + (k<<48)`

is equal to c*0x5DEECE66DL – d for some k at most 6. (There are more sophisticated ways to compute c and d, but because the bound on k is so tiny, it’s easier to just brute force).

We can just test all the possible values for k until we get a value whose negated remainder mod 0x5DEECE66DL is 16 bits (mod 248 again), so that we recover the lower 16 bits of both t and u. At that point, we have a full seed, so we can either find future seeds using the first equation, or past seeds using the second equation. It is as shown in the example given below which is as follows:

Example 2:

## Java

 `// Java Program to Implement Inversion Method for``// Random Number Generation`` ` `// Importing required classes ``import` `java.util.Arrays;``import` `java.util.Random;``import` `javax.sql.RowSetEvent;``import` `javax.sql.RowSetListener;``import` `javax.sql.rowset.JdbcRowSet;``import` `javax.sql.rowset.RowSetProvider;`` ` `// Main class``// To test random reverse``class` `GFG {`` ` `    ``// The secret seed that we want to find``    ``private` `static` `long` `SEED = 782634283105L;`` ` `    ``// Number of random numbers to be generated``    ``private` `static` `int` `NUM_GEN = ``5``;`` ` `    ``// Method 1``    ``private` `static` `int``[] genNum(``long` `seed)``    ``{``        ``Random rand = ``new` `Random(seed);``        ``int` `arr[] = ``new` `int``[NUM_GEN];``        ``for` `(``int` `i = ``0``; i < arr.length; i++) {``            ``arr[i] = rand.nextInt();``        ``}`` ` `        ``return` `arr;``    ``}`` ` `    ``// Method 2``    ``// Main driver method``    ``public` `static` `void` `main(String args[])``    ``{`` ` `        ``int` `arr[] = genNum(SEED);``        ``System.out.println(Arrays.toString(arr));`` ` `        ``Long result = reverse(arr);`` ` `        ``if` `(result != ``null``) {``            ``System.out.println(``                ``Arrays.toString(genNum(result)));``        ``}``        ``else` `{``            ``System.out.println(``"Seed not found"``);``        ``}``    ``}`` ` `    ``// Method 3``    ``private` `static` `long` `combine(``int` `rand, ``int` `suffix)``    ``{``        ``return` `(unsignedIntToLong(rand) << ``16``)``            ``| (suffix & ((1L << ``16``) - ``1``));``    ``}`` ` `    ``// Method 4``    ``private` `static` `long` `unsignedIntToLong(``int` `num)``    ``{``        ``return` `num & ((1L << ``32``) - ``1``);``    ``}`` ` `    ``// Method 5``    ``// To find the seed of a sequence of``    ``// integer, generated by nextInt() Can be easily``    ``// modified to find the seed of a sequence of long,``    ``// generated by nextLong()``    ``private` `static` `Long reverse(``int` `arr[])``    ``{`` ` `        ``// Need at least 2 numbers.``        ``assert` `(arr.length > ``1``);`` ` `        ``int` `end = arr.length - ``1``;`` ` `        ``// Brute force lower 16 bits, then compare``        ``// upper 32 bit of the previous seed generated``        ``// to the previous number.``        ``for` `(``int` `i = ``0``; i < (``1` `<< ``16``); i++) {`` ` `            ``long` `candidateSeed = combine(arr[end], i);``            ``long` `previousSeed``                ``= getPreviousSeed(candidateSeed);`` ` `            ``if` `((previousSeed >>> ``16``)``                ``== unsignedIntToLong(arr[end - ``1``])) {`` ` `                ``System.out.println(``"Testing seed: "``                                   ``+ previousSeed + ``" --> "``                                   ``+ candidateSeed);`` ` `                ``for` `(``int` `j = end - ``1``; j >= ``0``; j--) {``                    ``candidateSeed = previousSeed;``                    ``previousSeed``                        ``= getPreviousSeed(candidateSeed);`` ` `                    ``if` `(j > ``0``                        ``&& (previousSeed >>> ``16``)``                               ``== unsignedIntToLong(``                                   ``arr[j - ``1``])) {`` ` `                        ``System.out.println(``                            ``"Verifying: "` `+ previousSeed``                            ``+ ``" --> "` `+ candidateSeed);``                    ``}``                    ``else` `if` `(j == ``0``) {`` ` `                        ``// The XOR is done when the seed is``                        ``// set, need to reverse it``                        ``System.out.println(``                            ``"Seed found: "``                            ``+ (previousSeed ^ MULTIPLIER));`` ` `                        ``return` `previousSeed ^ MULTIPLIER;``                    ``}``                    ``else` `{``                        ``// Print statement``                        ``System.out.println(``"Failed"``);`` ` `                        ``// break keyword``                        ``break``;``                    ``}``                ``}``            ``}``        ``}`` ` `        ``return` `null``;``    ``}`` ` `    ``private` `static` `long` `ADDEND = 0xBL;``    ``private` `static` `long` `MULTIPLIER = 0x5DEECE66DL;`` ` `    ``// Method 6``    ``private` `static` `long` `getPreviousSeed(``long` `currentSeed)``    ``{`` ` `        ``long` `seed = currentSeed;`` ` `        ``// Reverse the addend from the seed``        ``// Reversing the addend``        ``seed -= ADDEND;`` ` `        ``long` `result = ``0``;`` ` `        ``// Iterating through the seeds bits``        ``for` `(``int` `i = ``0``; i < ``48``; i++) {`` ` `            ``long` `mask = 1L << i;``            ``// find the next bit``            ``long` `bit = seed & mask;``            ``// add it to the result`` ` `            ``result |= bit;`` ` `            ``if` `(bit == mask) {``                ``// if the bit was 1, subtract its effects``                ``// from the seed``                ``seed -= MULTIPLIER << i;``            ``}``        ``}`` ` `        ``return` `result & ((1L << ``48``) - ``1``);``    ``}``}`

Output:

```[826100673, 702667566, 238146028, -1525439028, -133372456]
Testing seed: 181503804585936 --> 272734279476123
Verifying: 15607138131897 --> 181503804585936
Verifying: 46050021665190 --> 15607138131897
Verifying: 54139333749543 --> 46050021665190
Seed found: 782634283105
[826100673, 702667566, 238146028, -1525439028, -133372456]```

Output explanation: The program will brute force on the lower 16

• -bit discarded by nextInt(), use the algorithm provided in the blog by James Roper to find the previous seed, then check that the upper 32 bit of the 48-bit seed is the same as the previous number. We need at least 2 integers to derive the previous seed. Otherwise, there will be 216 possibilities for the previous seed, and all of them are equally valid until we have at least one more number.
• It can be extended for nextLong() easily, and 1 long number is enough to find the seed, since we have 2 pieces of upper 32-bit of the seed in one long, due to the way it is generated.

Note: There are cases where the result is not the same as what you set as secret seed in the SEED variable. If the number you set as secret seed occupies more than 48-bit (which is the number of bits used for generating random numbers internally), then the upper 16 bits of 64 bit of long will be removed in the setSeed() method. In such cases, the result returned will not be the same as what you have set initially, it is likely that the lower 48-bit will be the same.

My Personal Notes arrow_drop_up