com.github.tommyettinger.random.SoloRandom Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of juniper Show documentation
Show all versions of juniper Show documentation
Serializable pseudo-random number generators and distributions.
The newest version!
/*
* Copyright (c) 2022 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.github.tommyettinger.random;
/**
* A subcycle generator with a counter, using only Add-Rotate-XOR operations.
* The whole nextLong() method can fit on one (lengthy) line, where a, b, and c can each be any long:
*
* {@code return a=(b=(b<<47|b>>>17)+(c+=0xD1B54A32D192ED03L))^c+(a<<23|a>>>41);}
*
* This has 192 bits of state. Period is at minimum 2 to the 64, and is always a multiple of 2 to the 64, but the
* expected period is much, much longer. This passes 64TB of PractRand with no anomalies. This takes more generations
* to decorrelate given initially similar starting states, but does completely decorrelate by 100 {@link #nextLong()}
* calls or earlier. Generators like {@link AceRandom} decorrelate by maybe 40-50 calls, and generators like
* {@link WhiskerRandom} effectively never decorrelate.
*
* On current HotSpot JVMs, this isn't as fast as AceRandom (SoloRandom gets roughly half the throughput as AceRandom),
* and the guaranteed minimum period is the same for each. AceRandom has a longer maximum period, and its only downside
* is it has 320 bits of state instead of 192. Both use only ARX operations. Still, SoloRandom may be useful just
* because it can fit in one line, when the states are declared already.
*
* Never tell me the odds!
*/
public class SoloRandom extends EnhancedRandom {
@Override
public String getTag() {
return "SolR";
}
/**
* The first state; can be any long.
*/
protected long stateA;
/**
* The second state; can be any long.
*/
protected long stateB;
/**
* The third state; can be any long.
*/
protected long stateC;
/**
* Creates a new SoloRandom with a random state.
*/
public SoloRandom() {
stateA = EnhancedRandom.seedFromMath();
stateB = EnhancedRandom.seedFromMath();
stateC = EnhancedRandom.seedFromMath();
}
/**
* Creates a new SoloRandom with the given seed; all {@code long} values are permitted.
* The seed will be passed to {@link #setSeed(long)} to attempt to adequately distribute the seed randomly.
*
* @param seed any {@code long} value
*/
public SoloRandom(long seed) {
setSeed(seed);
}
/**
* Creates a new SoloRandom with the given two states; all {@code long} values are permitted.
* These states will be used verbatim for stateA and stateB. stateC will be assigned 1.
*
* @param stateA any {@code long} value
* @param stateB any {@code long} value
*/
public SoloRandom(long stateA, long stateB) {
this.stateA = stateA;
this.stateB = stateB;
this.stateC = 1L;
}
/**
* Creates a new SoloRandom with the given three states; all {@code long} values are permitted.
* These states will be used verbatim.
*
* @param stateA any {@code long} value
* @param stateB any {@code long} value
* @param stateC any {@code long} value
*/
public SoloRandom(long stateA, long stateB, long stateC) {
this.stateA = stateA;
this.stateB = stateB;
this.stateC = stateC;
}
/**
* This generator has 3 {@code long} states, so this returns 3.
*
* @return 3 (three)
*/
@Override
public int getStateCount () {
return 3;
}
/**
* Gets the state determined by {@code selection}, as-is. The value for selection should be
* between 0 and 2, inclusive; if it is any other value this gets state C as if 2 was given.
*
* @param selection used to select which state variable to get; generally 0, 1, or 2
* @return the value of the selected state
*/
@Override
public long getSelectedState (int selection) {
switch (selection) {
case 0:
return stateA;
case 1:
return stateB;
default:
return stateC;
}
}
/**
* Sets one of the states, determined by {@code selection}, to {@code value}, as-is.
* Selections 0, 1, and 2 refer to states A, B, and C, and if the selection is anything
* else, this ignores it and sets nothing.
*
* @param selection used to select which state variable to set; generally 0, 1, or 2
* @param value the exact value to use for the selected state, if valid
*/
@Override
public void setSelectedState (int selection, long value) {
switch (selection) {
case 0:
stateA = value;
break;
case 1:
stateB = value;
break;
case 2:
stateC = value;
break;
}
}
/**
* This initializes all 3 states of the generator to random values based on the given seed.
* (2 to the 64) possible initial generator states can be produced here, though there are
* (2 to the 192) possible states in total.
*
* @param s the initial seed; may be any long
*/
@Override
public void setSeed (long s) {
s += 0xF1357AEA2E62A9C5L;
s = (s ^ (s << 23 | s >>> 41) ^ (s << 47 | s >>> 17)) ^ 0xC6BC279692B5C323L;
stateA = s;
s += 0xF1357AEA2E62A9C5L;
s = (s ^ (s << 3 | s >>> 61) ^ (s << 57 | s >>> 7)) ^ 0xC6BC279692B5C323L;
stateB = s;
s += 0xF1357AEA2E62A9C5L;
s = (s ^ (s << 43 | s >>> 21) ^ (s << 37 | s >>> 27)) ^ 0xC6BC279692B5C323L;
stateC = s;
}
public long getStateA () {
return stateA;
}
/**
* Sets the first part of the state.
*
* @param stateA can be any long
*/
public void setStateA (long stateA) {
this.stateA = stateA;
}
public long getStateB () {
return stateB;
}
/**
* Sets the second part of the state.
*
* @param stateB can be any long
*/
public void setStateB (long stateB) {
this.stateB = stateB;
}
public long getStateC () {
return stateC;
}
/**
* Sets the third part of the state.
*
* @param stateC can be any long
*/
public void setStateC (long stateC) {
this.stateC = stateC;
}
/**
* Equivalent to {@code setState(stateA, stateB, 1L)}.
*
* @param stateA the long value to use for stateA
* @param stateB the long value to use for stateB
*/
@Override
public void setState(long stateA, long stateB) {
setState(stateA, stateB, 1L);
}
/**
* Sets the state completely to the given three state variables.
* This is the same as calling {@link #setStateA(long)}, {@link #setStateB(long)},
* and {@link #setStateC(long)} as a group.
*
* @param stateA the first state; can be any long
* @param stateB the second state; can be any long
* @param stateC the third state; can be any long
*/
@Override
public void setState (long stateA, long stateB, long stateC) {
this.stateA = stateA;
this.stateB = stateB;
this.stateC = stateC;
}
@Override
public long nextLong () {
return stateA = (stateB = (stateB << 47 | stateB >>> 17) + (stateC += 0xD1B54A32D192ED03L))
^ stateC + (stateA << 23 | stateA >>> 41);
}
// above, one-line version
// return a=(b=(b<<47|b>>>17)+(c+=0xD1B54A32D192ED03L))^(a<<26|a>>>38)+c^(c<<23|c>>>41);
// variant, one-line version
// return a=(b=(b<<47|b>>>17)+(c+=0xD1B54A32D192ED03L))^c+(a<<23|a>>>41);
@Override
public long previousLong () {
final long a = stateA;
final long b = stateB;
final long c = stateC;
stateC -= 0xD1B54A32D192ED03L;
stateB = b - c;
stateB = (stateB << 17 | stateB >>> 47);
stateA = (a ^ b) - c;
stateA = (stateA << 41 | stateA >>> 23);
return a;
}
@Override
public int next (int bits) {
return (int)(stateA = (stateB = (stateB << 47 | stateB >>> 17) + (stateC += 0xD1B54A32D192ED03L))
^ stateC + (stateA << 23 | stateA >>> 41)) >>> (32 - bits);
}
/**
* Returns the next pseudorandom, uniformly distributed {@code int}
* value from this random number generator's sequence. The general
* contract of {@code nextInt} is that one {@code int} value is
* pseudorandomly generated and returned. All 232 possible
* {@code int} values are produced with (approximately) equal probability.
*
* @return the next pseudorandom, uniformly distributed {@code int}
* value from this random number generator's sequence
*/
@Override
public int nextInt() {
return (int)(stateA = (stateB = (stateB << 47 | stateB >>> 17) + (stateC += 0xD1B54A32D192ED03L))
^ stateC + (stateA << 23 | stateA >>> 41));
}
@Override
public SoloRandom copy () {
return new SoloRandom(stateA, stateB, stateC);
}
@Override
public boolean equals (Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SoloRandom that = (SoloRandom)o;
return stateA == that.stateA && stateB == that.stateB && stateC == that.stateC;
}
public String toString () {
return "SoloRandom{" + "stateA=" + (stateA) + "L, stateB=" + (stateB) + "L, stateC=" + (stateC) + "L}";
}
// public static void main(String[] args) {
// SoloRandom random = new SoloRandom(1L);
// {
// int n0 = random.nextInt();
// int n1 = random.nextInt();
// int n2 = random.nextInt();
// int n3 = random.nextInt();
// int n4 = random.nextInt();
// int n5 = random.nextInt();
// int p5 = random.previousInt();
// int p4 = random.previousInt();
// int p3 = random.previousInt();
// int p2 = random.previousInt();
// int p1 = random.previousInt();
// int p0 = random.previousInt();
// System.out.println(n0 == p0);
// System.out.println(n1 == p1);
// System.out.println(n2 == p2);
// System.out.println(n3 == p3);
// System.out.println(n4 == p4);
// System.out.println(n5 == p5);
// System.out.println(Base.BASE16.unsigned(n0) + " vs. " + Base.BASE16.unsigned(p0));
// System.out.println(Base.BASE16.unsigned(n1) + " vs. " + Base.BASE16.unsigned(p1));
// System.out.println(Base.BASE16.unsigned(n2) + " vs. " + Base.BASE16.unsigned(p2));
// System.out.println(Base.BASE16.unsigned(n3) + " vs. " + Base.BASE16.unsigned(p3));
// System.out.println(Base.BASE16.unsigned(n4) + " vs. " + Base.BASE16.unsigned(p4));
// System.out.println(Base.BASE16.unsigned(n5) + " vs. " + Base.BASE16.unsigned(p5));
// }
// {
// long n0 = random.nextLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long n1 = random.nextLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long n2 = random.nextLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long n3 = random.nextLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long n4 = random.nextLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long n5 = random.nextLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// System.out.println("Going back...");
// long p5 = random.previousLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long p4 = random.previousLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long p3 = random.previousLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long p2 = random.previousLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long p1 = random.previousLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// long p0 = random.previousLong(); System.out.printf("a: 0x%016XL, b: 0x%016XL, c: 0x%016XL\n", random.stateA, random.stateB, random.stateC);
// System.out.println(n0 == p0);
// System.out.println(n1 == p1);
// System.out.println(n2 == p2);
// System.out.println(n3 == p3);
// System.out.println(n4 == p4);
// System.out.println(n5 == p5);
// System.out.println(Base.BASE16.unsigned(n0) + " vs. " + Base.BASE16.unsigned(p0));
// System.out.println(Base.BASE16.unsigned(n1) + " vs. " + Base.BASE16.unsigned(p1));
// System.out.println(Base.BASE16.unsigned(n2) + " vs. " + Base.BASE16.unsigned(p2));
// System.out.println(Base.BASE16.unsigned(n3) + " vs. " + Base.BASE16.unsigned(p3));
// System.out.println(Base.BASE16.unsigned(n4) + " vs. " + Base.BASE16.unsigned(p4));
// System.out.println(Base.BASE16.unsigned(n5) + " vs. " + Base.BASE16.unsigned(p5));
// }
// }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy