/
DiverRNG.java
439 lines (404 loc) · 24.7 KB
/
DiverRNG.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
package sarong;
import sarong.util.CrossHash;
import sarong.util.StringKit;
import java.io.Serializable;
/**
* A very-high-quality StatefulRandomness that is the fastest 64-bit generator in this library that passes statistical
* tests and is one-dimensionally equidistributed across all 64-bit outputs. Has 64 bits of state and natively outputs
* 64 bits at a time, changing the state with an "XLCG" or xor linear congruential generator (XLCGs are very similar to
* normal LCGs but have slightly better random qualities on the high bits; the code for this XLCG is
* {@code state = (state ^ 7822362180758744021) * -4126379630918251389}, and the only requirements for an XLCG are that
* the constant used with XOR, when treated as unsigned and modulo 8, equals 5, while the multiplier, again treated as
* unsigned and modulo 8, equals 3). Starting with that XLCG's output, it bitwise-left-rotates by 27, multiplies by a
* very large negative long (see next), then returns a right-xorshift by 25. The large negative long is
* -2643881736870682267, which when treated as unsigned is 2 to the 64 divided by an irrational number that generalizes
* the golden ratio. This specific irrational number is the solution to {@code x}<sup>{@code 5}</sup>{@code = x + 1}.
* Other multipliers also seem to work well as long as they have enough set bits (fairly-small multipliers fail tests).
* For whatever reason, the output of this simple function passes all 32TB of PractRand with one anomaly ("unusual"
* at 256GB), meaning its statistical quality is excellent. {@link ThrustAltRNG} is slightly faster, but isn't
* equidistributed; unlike ThrustAltRNG, this can produce all long values as output. ThrustAltRNG bunches some outputs
* and makes producing them more likely, while others can't be produced at all. Notably, this generator is faster than
* {@link LinnormRNG}, which it is based on, while improving its quality, is faster than {@link LightRNG} while keeping
* the same or higher quality, and is also faster than {@link XoRoRNG} while passing tests that XoRoRNG always or
* frequently fails, such as binary matrix rank tests.
* <br>
* This generator is a StatefulRandomness but not a SkippingRandomness, so it can't (efficiently) have the skip() method
* that LightRNG has. A method could be written to run the generator's state backwards, though, as well as to get the
* state from an output of {@link #nextLong()}.
* <br>
* The static determine() methods in this class are currently identical to the ones in LinnormRNG, and haven't been
* checked with PractRand 0.94 (only 0.93, which doesn't have a TMFn test). They may change if the methods from Linnorm
* turn out to fail like its {@link LinnormRNG#nextLong()} and other instance methods.
* <br>
* The name comes in a roundabout way from Xmulzencab, Maya mythology's bee god who is also called the Diving God,
* because the state transition is built around Xor and MUL. I was also listening to a Dio song, Holy Diver, at the
* time, and Diver is much more reasonable to pronounce than Xmulzencab.
* <br>
* Written December 14, 2018 by Tommy Ettinger. Thanks to M.E. O'Neill for her insights into the family of generators
* both this and her PCG-Random fall into, and to the team that worked on SplitMix64 for SplittableRandom in JDK 8.
* Chris Doty-Humphrey's work on PractRand has been invaluable, and I wouldn't know about XLCGs without his findings.
* Martin Roberts showed the technique for generalizing the golden ratio that produced the high-quality multiplier this
* uses in a few places. Other constants were found empirically or via searching for probable primes with desirable
* values for use in an XLCG.
* @author Tommy Ettinger
*/
public final class DiverRNG implements StatefulRandomness, Serializable {
private static final long serialVersionUID = 153186732328748834L;
private long state; /* The state can be seeded with any value. */
/**
* Creates a new generator seeded using Math.random.
*/
public DiverRNG() {
this((long) ((Math.random() - 0.5) * 0x10000000000000L)
^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L));
}
public DiverRNG(final long seed) {
state = seed;
}
public DiverRNG(final String seed) {
state = CrossHash.hash64(seed);
}
@Override
public final int next(int bits)
{
long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
z = (z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L;
return (int)(z ^ z >>> 25) >>> (32 - bits);
}
/**
* Can return any long, positive or negative, of any size permissible in a 64-bit signed integer.
*
* @return any long, all 64 bits are random
*/
@Override
public final long nextLong() {
long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
z = (z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L;
return (z ^ z >>> 25);
}
/**
* Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
* copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
* copy the state so it isn't shared, usually, and produce a new value with the same exact state.
*
* @return a copy of this RandomnessSource
*/
@Override
public DiverRNG copy() {
return new DiverRNG(state);
}
/**
* Can return any int, positive or negative, of any size permissible in a 32-bit signed integer.
*
* @return any int, all 32 bits are random
*/
public final int nextInt() {
long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
z = (z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L;
return (int)(z ^ z >>> 25);
}
/**
* Exclusive on the outer bound. The inner bound is 0.
* The bound can be negative, which makes this produce either a negative int or 0.
*
* @param bound the upper bound; should be positive
* @return a random int between 0 (inclusive) and bound (exclusive)
*/
public final int nextInt(final int bound) {
long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
z = (z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L;
return (int)((bound * ((z ^ z >>> 25) & 0xFFFFFFFFL)) >> 32);
}
/**
* Inclusive inner, exclusive outer.
*
* @param inner the inner bound, inclusive, can be positive or negative
* @param outer the outer bound, exclusive, can be positive or negative, usually greater than inner
* @return a random int between inner (inclusive) and outer (exclusive)
*/
public final int nextInt(final int inner, final int outer) {
return inner + nextInt(outer - inner);
}
/**
* Exclusive on bound (which may be positive or negative), with an inner bound of 0.
* If bound is negative this returns a negative long; if bound is positive this returns a positive long. The bound
* can even be 0, which will cause this to return 0L every time.
* <br>
* Credit for this method goes to <a href="https://oroboro.com/large-random-in-range/">Rafael Baptista's blog</a>,
* with some adaptation for signed long values and a 64-bit generator. This method is drastically faster than the
* previous implementation when the bound varies often (roughly 4x faster, possibly more). It also always gets at
* most one random number, so it advances the state as much as {@link #nextInt(int)}.
* @param bound the outer exclusive bound; can be positive or negative
* @return a random long between 0 (inclusive) and bound (exclusive)
*/
public long nextLong(long bound) {
long rand = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
rand = (rand << 27 | rand >>> 37) * 0xDB4F0B9175AE2165L;
rand ^= rand >>> 25;
final long randLow = rand & 0xFFFFFFFFL;
final long boundLow = bound & 0xFFFFFFFFL;
rand >>>= 32;
bound >>= 32;
final long z = (randLow * boundLow >> 32);
final long t = rand * boundLow + z;
final long tLow = t & 0xFFFFFFFFL;
return rand * bound + (t >>> 32) + (tLow + randLow * bound >> 32) - (z >> 63) - (bound >> 63);
}
/**
* Inclusive inner, exclusive outer; lower and upper can be positive or negative and there's no requirement for one
* to be greater than or less than the other.
*
* @param lower the lower bound, inclusive, can be positive or negative
* @param upper the upper bound, exclusive, can be positive or negative
* @return a random long that may be equal to lower and will otherwise be between lower and upper
*/
public final long nextLong(final long lower, final long upper) {
return lower + nextLong(upper - lower);
}
/**
* Gets a uniform random double in the range [0.0,1.0)
*
* @return a random double at least equal to 0.0 and less than 1.0
*/
public final double nextDouble() {
long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
z = (z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L;
return ((z ^ z >>> 25) & 0x1FFFFFFFFFFFFFL) * 0x1p-53;
}
/**
* Gets a uniform random double in the range [0.0,outer) given a positive parameter outer. If outer
* is negative, it will be the (exclusive) lower bound and 0.0 will be the (inclusive) upper bound.
*
* @param outer the exclusive outer bound, can be negative
* @return a random double between 0.0 (inclusive) and outer (exclusive)
*/
public final double nextDouble(final double outer) {
long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
z = (z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L;
return ((z ^ z >>> 25) & 0x1FFFFFFFFFFFFFL) * 0x1p-53 * outer;
}
/**
* Gets a uniform random float in the range [0.0,1.0)
*
* @return a random float at least equal to 0.0 and less than 1.0
*/
public final float nextFloat() {
final long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
return ((z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L >>> 40) * 0x1p-24f;
}
/**
* Gets a random value, true or false.
* Calls nextLong() once.
*
* @return a random true or false value.
*/
public final boolean nextBoolean() {
final long z = (state = (state ^ 0x6C8E9CF570932BD5L) * 0xC6BC279692B5CC83L);
return ((z << 27 | z >>> 37) * 0xDB4F0B9175AE2165L) < 0;
}
/**
* Given a byte array as a parameter, this will fill the array with random bytes (modifying it
* in-place). Calls nextLong() {@code Math.ceil(bytes.length / 8.0)} times.
*
* @param bytes a byte array that will have its contents overwritten with random bytes.
*/
public final void nextBytes(final byte[] bytes) {
int i = bytes.length, n;
while (i != 0) {
n = Math.min(i, 8);
for (long bits = nextLong(); n-- != 0; bits >>>= 8) bytes[--i] = (byte) bits;
}
}
/**
* Sets the seed (also the current state) of this generator.
*
* @param seed the seed to use for this DiverRNG, as if it was constructed with this seed.
*/
@Override
public final void setState(final long seed) {
state = seed;
}
/**
* Gets the current state of this generator.
*
* @return the current seed of this DiverRNG, changed once per call to nextLong()
*/
@Override
public final long getState() {
return state;
}
@Override
public String toString() {
return "DiverRNG with state 0x" + StringKit.hex(state) + 'L';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return state == ((DiverRNG) o).state;
}
@Override
public int hashCode() {
return (int) (state ^ (state >>> 32));
}
/**
* Fast static randomizing method that takes its state as a parameter; state is expected to change between calls to
* this. It is recommended that you use {@code DiverRNG.determine(++state)} or {@code DiverRNG.determine(--state)}
* to produce a sequence of different numbers, and you may have slightly worse quality with increments or decrements
* other than 1. All longs are accepted by this method, and all longs can be produced; unlike several other classes'
* determine() methods, passing 0 here does not return 0.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state any long; subsequent calls should change by an odd number, such as with {@code ++state}
* @return any long
*/
public static long determine(long state)
{
return (state = ((state = (((state * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ state >>> 27) * 0xAEF17502108EF2D9L) ^ state >>> 25;
}
/**
* High-quality static randomizing method that takes its state as a parameter; state is expected to change between
* calls to this. It is suggested that you use {@code DiverRNG.randomize(++state)} or
* {@code DiverRNG.randomize(--state)} to produce a sequence of different numbers, but any increments are allowed
* (even-number increments won't be able to produce all outputs, but their quality will be fine for the numbers they
* can produce). All longs are accepted by this method, and all longs can be produced; unlike several other classes'
* determine() methods, passing 0 here does not return 0.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state any long; subsequent calls should change by an odd number, such as with {@code ++state}
* @return any long
*/
public static long randomize(long state)
{
return (state = ((state = (state ^ (state << 41 | state >>> 23) ^ (state << 17 | state >>> 47) ^ 0xD1B54A32D192ED03L) * 0xAEF17502108EF2D9L) ^ state >>> 43 ^ state >>> 31 ^ state >>> 23) * 0xDB4F0B9175AE2165L) ^ state >>> 28;
}
/**
* Fast static randomizing method that takes its state as a parameter and limits output to an int between 0
* (inclusive) and bound (exclusive); state is expected to change between calls to this. It is recommended that you
* use {@code DiverRNG.determineBounded(++state, bound)} or {@code DiverRNG.determineBounded(--state, bound)} to
* produce a sequence of different numbers. All longs are accepted
* by this method, but not all ints between 0 and bound are guaranteed to be produced with equal likelihood (for any
* odd-number values for bound, this isn't possible for most generators). The bound can be negative.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state any long; subsequent calls should change by an odd number, such as with {@code ++state}
* @param bound the outer exclusive bound, as an int
* @return an int between 0 (inclusive) and bound (exclusive)
*/
public static int determineBounded(long state, final int bound)
{
return (int)((bound * (((state = ((state = (((state * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ state >>> 27) * 0xAEF17502108EF2D9L) ^ state >>> 25) & 0xFFFFFFFFL)) >> 32);
}
/**
* Fast static randomizing method that takes its state as a parameter and limits output to an int between 0
* (inclusive) and bound (exclusive); state is expected to change between calls to this. It is recommended that you
* use {@code DiverRNG.randomizeBounded(++state, bound)} or {@code DiverRNG.randomizeBounded(--state, bound)} to
* produce a sequence of different numbers, but any increments are allowed (even-number increments won't be able to
* produce all outputs, but their quality will be fine for the numbers they can produce). All longs are accepted
* by this method, but not all ints between 0 and bound are guaranteed to be produced with equal likelihood (for any
* odd-number values for bound, this isn't possible for most generators). The bound can be negative.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state any long; subsequent calls should change by an odd number, such as with {@code ++state}
* @param bound the outer exclusive bound, as an int
* @return an int between 0 (inclusive) and bound (exclusive)
*/
public static int randomizeBounded(long state, final int bound)
{
return (int)((bound * (((state = ((state = (state ^ (state << 41 | state >>> 23) ^ (state << 17 | state >>> 47) ^ 0xD1B54A32D192ED03L) * 0xAEF17502108EF2D9L) ^ state >>> 43 ^ state >>> 31 ^ state >>> 23) * 0xDB4F0B9175AE2165L) ^ state >>> 28) & 0xFFFFFFFFL)) >> 32);
}
/**
* Returns a random float that is deterministic based on state; if state is the same on two calls to this, this will
* return the same float. This is expected to be called with a changing variable, e.g.
* {@code determineFloat(++state)}, where the increment for state should generally be 1. The period is 2 to the 64
* if you increment or decrement by 1, but there are only 2 to the 30 possible floats between 0 and 1.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state a variable that should be different every time you want a different random result;
* using {@code determineFloat(++state)} is recommended to go forwards or
* {@code determineFloat(--state)} to generate numbers in reverse order
* @return a pseudo-random float between 0f (inclusive) and 1f (exclusive), determined by {@code state}
*/
public static float determineFloat(long state) {
return ((((state = (((state * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ state >>> 27) * 0xAEF17502108EF2D9L) >>> 40) * 0x1p-24f;
}
/**
* Returns a random float that is deterministic based on state; if state is the same on two calls to this, this will
* return the same float. This is expected to be called with a changing variable, e.g.
* {@code randomizeFloat(++state)}, where the increment for state can be any value and should usually be odd
* (even-number increments reduce the period). The period is 2 to the 64 if you increment or decrement by any odd
* number, but there are only 2 to the 30 possible floats between 0 and 1.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state a variable that should be different every time you want a different random result;
* using {@code randomizeFloat(++state)} is recommended to go forwards or
* {@code randomizeFloat(--state)} to generate numbers in reverse order
* @return a pseudo-random float between 0f (inclusive) and 1f (exclusive), determined by {@code state}
*/
public static float randomizeFloat(long state) {
return (((state = (state ^ (state << 41 | state >>> 23) ^ (state << 17 | state >>> 47) ^ 0xD1B54A32D192ED03L) * 0xAEF17502108EF2D9L) ^ state >>> 43 ^ state >>> 31 ^ state >>> 23) * 0xDB4F0B9175AE2165L >>> 40) * 0x1p-24f;
}
/**
* Returns a random double that is deterministic based on state; if state is the same on two calls to this, this
* will return the same float. This is expected to be called with a changing variable, e.g.
* {@code determineDouble(++state)}, where the increment for state should generally be 1. The period is 2 to the 64
* if you increment or decrement by 1, but there are only 2 to the 62 possible doubles between 0 and 1.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state a variable that should be different every time you want a different random result;
* using {@code determineDouble(++state)} is recommended to go forwards or
* {@code determineDouble(--state)} to generate numbers in reverse order
* @return a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive), determined by {@code state}
*/
public static double determineDouble(long state) {
return (((state = ((state = (((state * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ state >>> 27) * 0xAEF17502108EF2D9L) ^ state >>> 25) & 0x1FFFFFFFFFFFFFL) * 0x1p-53;
}
/**
* Returns a random double that is deterministic based on state; if state is the same on two calls to this, this
* will return the same float. This is expected to be called with a changing variable, e.g.
* {@code randomizeDouble(++state)}, where the increment for state can be any number but should usually be odd
* (even-number increments reduce the period). The period is 2 to the 64 if you increment or decrement by 1, but
* there are only 2 to the 62 possible doubles between 0 and 1.
* <br>
* You have a choice between determine() and randomize() in this class; determine() is the same as
* {@link LinnormRNG#determine(long)} and will behave well when the inputs are sequential, while randomize() is the
* same as {@link PelicanRNG#determine(long)} and will have excellent quality regardless of patterns in input but
* will be about 30% slower than determine(). Each method will produce all long outputs if given all possible longs
* as input.
* @param state a variable that should be different every time you want a different random result;
* using {@code randomizeDouble(++state)} is recommended to go forwards or
* {@code randomizeDouble(--state)} to generate numbers in reverse order
* @return a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive), determined by {@code state}
*/
public static double randomizeDouble(long state) {
return (((state = ((state = (((state * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ state >>> 27) * 0xAEF17502108EF2D9L) ^ state >>> 25) & 0x1FFFFFFFFFFFFFL) * 0x1p-53;
}
}