|
| 1 | +/* |
| 2 | + * java-math-library is a Java library focused on number theory, but not necessarily limited to it. It is based on the PSIQS 4.0 factoring project. |
| 3 | + * Copyright (C) 2018-2025 Tilman Neumann - tilman.neumann@web.de |
| 4 | + * |
| 5 | + * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License |
| 6 | + * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. |
| 7 | + * |
| 8 | + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
| 9 | + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 10 | + * |
| 11 | + * You should have received a copy of the GNU General Public License along with this program; |
| 12 | + * if not, see <http://www.gnu.org/licenses/>. |
| 13 | + */ |
| 14 | +package de.tilman_neumann.jml.random; |
| 15 | + |
| 16 | +import static org.junit.Assert.assertTrue; |
| 17 | + |
| 18 | +import java.util.Random; |
| 19 | + |
| 20 | +/** |
| 21 | + * 64 bit random number generator using Random.nextLong() and the strategy from Java17 for Random.nextLong(long maxValue). |
| 22 | + */ |
| 23 | +public abstract class Random64 { |
| 24 | + |
| 25 | + private static final boolean DEBUG = false; |
| 26 | + |
| 27 | + private Random random; |
| 28 | + |
| 29 | + public Random64() { |
| 30 | + random = new Random(); |
| 31 | + } |
| 32 | + |
| 33 | + public Random64(long seed) { |
| 34 | + random = new Random(seed); |
| 35 | + } |
| 36 | + |
| 37 | + public long nextLong() { |
| 38 | + return random.nextLong(); |
| 39 | + } |
| 40 | + |
| 41 | + /** |
| 42 | + * Creates a random long from the uniform distribution U[0, maxValue-1], with maxValue > 0. |
| 43 | + * This is similar to what Java 17 does in RandomGenerator.nextLong(long). |
| 44 | + * |
| 45 | + * @param maxValue |
| 46 | + * @return random long in the desired range |
| 47 | + * |
| 48 | + * @see https://github.com/openjdk/jdk17/blob/master/src/java.base/share/classes/java/util/random/RandomGenerator.java |
| 49 | + */ |
| 50 | + public long nextLong(long maxValue) { |
| 51 | + if (Long.bitCount(maxValue) == 1) { |
| 52 | + // maxValue is a power of 2 |
| 53 | + return nextLong() >>> Long.numberOfLeadingZeros(maxValue); |
| 54 | + } |
| 55 | + |
| 56 | + long r = nextLong(); |
| 57 | + long u = r >>> 1; |
| 58 | + while (true) { |
| 59 | + r = u % maxValue; // now 0 <= r <= u and r < maxValue |
| 60 | + if (DEBUG) { |
| 61 | + assertTrue(u >= 0); |
| 62 | + assertTrue(r >= 0); |
| 63 | + assertTrue(r <= u); |
| 64 | + assertTrue(r < maxValue); |
| 65 | + } |
| 66 | + // Using the modulus r = u % maxValue to obtain random numbers < maxValue has its caveats... |
| 67 | + // If u is close to 2^63, then r much smaller than maxValue are over-represented. |
| 68 | + // We only accept r that are close to maxValue then |
| 69 | + if (u + maxValue - r >= 0) break; |
| 70 | + // Otherwise reject the last random number and try another one |
| 71 | + u = nextLong() >>> 1; |
| 72 | + } |
| 73 | + |
| 74 | + return r; |
| 75 | + } |
| 76 | + |
| 77 | + /** |
| 78 | + * Creates a random long from the uniform distribution U[minValue, maxValue-1]. |
| 79 | + * Works also for negative arguments; the only requirement is maxValue > minValue. |
| 80 | + * |
| 81 | + * @param minValue |
| 82 | + * @param maxValue |
| 83 | + * @return random long in the desired range |
| 84 | + */ |
| 85 | + public long nextLong(long minValue, long maxValue) { |
| 86 | + return minValue + nextLong(maxValue - minValue); |
| 87 | + } |
| 88 | +} |
0 commit comments