Skip to content

Commit

Permalink
Merge pull request #263 from cicirello/array-free
Browse files Browse the repository at this point in the history
Added array-free version of nextIntPair methods in RandomIndexer and EnhancedRandomGenerator
  • Loading branch information
cicirello authored Mar 29, 2024
2 parents 9447901 + 9cb1b95 commit 6fb19c1
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
**BREAKING CHANGES: See Removed and Changed sections for details.**

### Added
* IndexPair record class for use in methods generating pairs of random indexes.
* Versions of nextIntPair method that utilize the new IndexPair class added to both RandomIndexer and EnhancedRandomGenerator classes.

### Changed
* Changed ZigguratGaussian to a package-access class (BREAKING CHANGE)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* rho mu - A Java library of randomization enhancements and other math utilities.
* Copyright (C) 2017-2023 Vincent A. Cicirello, <https://www.cicirello.org/>.
* Copyright (C) 2017-2024 Vincent A. Cicirello, <https://www.cicirello.org/>.
*
* This file is part of the rho mu library.
*
Expand Down Expand Up @@ -465,6 +465,20 @@ public final int[] nextIntPair(int n, int[] result) {
return RandomIndexer.nextIntPair(n, result, generator);
}

/**
* Generates a random sample of 2 integers, without replacement, from the set of integers in the
* interval [0, n). All n choose 2 combinations are equally likely. <b>Enhanced Functionality.</b>
*
* <p>The runtime is O(1).
*
* @param n The number of integers to choose from.
* @return A pair of randomly chosen integers from the interval [0, n).
* @throws IllegalArgumentException if n &lt; 2.
*/
public final IndexPair nextIntPair(int n) {
return RandomIndexer.nextIntPair(n, generator);
}

/**
* Generates a random sample of 3 integers, without replacement, from the set of integers in the
* interval [0, n). All n choose 3 combinations are equally likely. <b>Enhanced Functionality.</b>
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/org/cicirello/math/rand/IndexPair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* rho mu - A Java library of randomization enhancements and other math utilities.
* Copyright (C) 2017-2024 Vincent A. Cicirello, <https://www.cicirello.org/>.
*
* This file is part of the rho mu library.
*
* The rho mu library is free software: you can
* redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* The rho mu library is distributed in the hope
* that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with the rho mu library. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.cicirello.math.rand;

/**
* A pair of indexes. Instances of IndexPair are returned by some methods of other classes, such as
* {@link RandomIndexer} and {@link EnhancedRandomGenerator}, that generate random pairs of indexes.
*
* @param i one of the indexes of the pair
* @param j the other index of the pair
* @author <a href=https://www.cicirello.org/ target=_top>Vincent A. Cicirello</a>, <a
* href=https://www.cicirello.org/ target=_top>https://www.cicirello.org/</a>
*/
public record IndexPair(int i, int j) {}
36 changes: 35 additions & 1 deletion src/main/java/org/cicirello/math/rand/RandomIndexer.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* rho mu - A Java library of randomization enhancements and other math utilities.
* Copyright (C) 2017-2023 Vincent A. Cicirello, <https://www.cicirello.org/>.
* Copyright (C) 2017-2024 Vincent A. Cicirello, <https://www.cicirello.org/>.
*
* This file is part of the rho mu library.
*
Expand Down Expand Up @@ -278,6 +278,40 @@ public static int[] nextIntPair(int n, int[] result, RandomGenerator gen) {
return result;
}

/**
* Generates a random sample of 2 integers, without replacement, from the set of integers in the
* interval [0, n). All n choose 2 combinations are equally likely.
*
* <p>The runtime is O(1).
*
* <p>This method uses ThreadLocalRandom as the pseudorandom number generator, and is thus safe,
* and efficient (i.e., non-blocking), for use with threads.
*
* @param n The number of integers to choose from.
* @return A pair of randomly chosen integers from the interval [0, n).
* @throws IllegalArgumentException if n &lt; 2.
*/
public static IndexPair nextIntPair(int n) {
return nextIntPair(n, ThreadLocalRandom.current());
}

/**
* Generates a random sample of 2 integers, without replacement, from the set of integers in the
* interval [0, n). All n choose 2 combinations are equally likely.
*
* <p>The runtime is O(1).
*
* @param n The number of integers to choose from.
* @param gen Source of randomness.
* @return A pair of randomly chosen integers from the interval [0, n).
* @throws IllegalArgumentException if n &lt; 2.
*/
public static IndexPair nextIntPair(int n, RandomGenerator gen) {
int i = nextInt(n, gen);
int j = nextInt(n - 1, gen);
return new IndexPair(i, i == j ? n - 1 : j);
}

/**
* Generates a random sample of 3 integers, without replacement, from the set of integers in the
* interval [0, n). All n choose 3 combinations are equally likely.
Expand Down
11 changes: 10 additions & 1 deletion src/test/java/org/cicirello/math/rand/ERGPairsTriplesTests.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* rho mu - A Java library of randomization enhancements and other math utilities.
* Copyright (C) 2017-2022 Vincent A. Cicirello, <https://www.cicirello.org/>.
* Copyright (C) 2017-2024 Vincent A. Cicirello, <https://www.cicirello.org/>.
*
* This file is part of the rho mu library.
*
Expand Down Expand Up @@ -42,6 +42,15 @@ public void testNextIntPair() {
validateCombo(6, 2, result);
}

@Test
public void testNextIntPair_IndexPair() {
EnhancedRandomGenerator erg = new EnhancedRandomGenerator();
IndexPair result = erg.nextIntPair(6);
assertTrue(result.i() < 6);
assertTrue(result.j() < 6);
assertNotEquals(result.i(), result.j());
}

@Test
public void testNextIntTriple() {
EnhancedRandomGenerator erg = new EnhancedRandomGenerator();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* rho mu - A Java library of randomization enhancements and other math utilities.
* Copyright 2017-2022 Vincent A. Cicirello, <https://www.cicirello.org/>.
* Copyright 2017-2024 Vincent A. Cicirello, <https://www.cicirello.org/>.
*
* This file is part of the rho mu library.
*
Expand Down Expand Up @@ -423,7 +423,7 @@ public void testPair_ThreadLocalRandom() {

for (int n = 2; n <= 6; n++) {
for (int i = 0; i < 10; i++) {
int[] result = RandomIndexer.nextIntPair(n, null);
int[] result = RandomIndexer.nextIntPair(n, (int[]) null);
assertEquals(2, result.length, "Length of result should be 2");
assertNotEquals(result[0], result[1], "integers should be different");
assertTrue(result[0] >= 0, "integers should be at least 0");
Expand All @@ -446,7 +446,7 @@ public void testPair_ThreadLocalRandom() {
int[][] buckets = new int[n][n];
int numBuckets = n * (n - 1); // /2;
for (int i = 0; i < REPS_PER_BUCKET * numBuckets; i++) {
int[] result = RandomIndexer.nextIntPair(n, null);
int[] result = RandomIndexer.nextIntPair(n, (int[]) null);
buckets[result[0]][result[1]]++;
}
double chi = chiSquareAll(buckets, numBuckets);
Expand All @@ -457,6 +457,40 @@ public void testPair_ThreadLocalRandom() {
}
}

@Test
public void testPair_IndexPair_ThreadLocalRandom() {
final int REPS_PER_BUCKET = 200;
final int TRIALS = 200;

for (int n = 2; n <= 6; n++) {
for (int i = 0; i < 10; i++) {
IndexPair result = RandomIndexer.nextIntPair(n);
assertNotEquals(result.i(), result.j(), "integers should be different");
assertTrue(result.i() >= 0, "integers should be at least 0");
assertTrue(result.j() < n, "integers should be less than " + n);
assertTrue(result.j() >= 0, "integers should be at least 0");
assertTrue(result.i() < n, "integers should be less than " + n);
}
}

if (DISABLE_CHI_SQUARE_TESTS) return;
for (int n = 2; n <= 6; n++) {
int countH = 0;
for (int trial = 0; trial < TRIALS; trial++) {
int[][] buckets = new int[n][n];
int numBuckets = n * (n - 1); // /2;
for (int i = 0; i < REPS_PER_BUCKET * numBuckets; i++) {
IndexPair result = RandomIndexer.nextIntPair(n);
buckets[result.i()][result.j()]++;
}
double chi = chiSquareAll(buckets, numBuckets);
if (chi > limit95[numBuckets - 1]) countH++;
}
assertTrue(
countH <= TRIALS * 0.1, "chi square too high too often, countHigh=" + countH + " n=" + n);
}
}

@Test
public void testTriple_ThreadLocalRandom() {
final int REPS_PER_BUCKET = 200;
Expand Down Expand Up @@ -666,6 +700,39 @@ public void testPair_SplittableRandom() {
assertEquals(2, actual.length);
}

@Test
public void testPair_IndexPair_SplittableRandom() {
SplittableRandom gen = new SplittableRandom(42);
final int REPS_PER_BUCKET = 100;
final int TRIALS = 100;

for (int n = 2; n <= 6; n++) {
for (int i = 0; i < 10; i++) {
IndexPair result = RandomIndexer.nextIntPair(n, gen);
assertNotEquals(result.i(), result.j());
assertTrue(result.i() >= 0);
assertTrue(result.j() < n);
assertTrue(result.j() >= 0);
assertTrue(result.i() < n);
}
}
for (int n = 2; n <= 6; n++) {
int countH = 0;
for (int trial = 0; trial < TRIALS; trial++) {
int[][] buckets = new int[n][n];
int numBuckets = n * (n - 1); // /2;
for (int i = 0; i < REPS_PER_BUCKET * numBuckets; i++) {
IndexPair result = RandomIndexer.nextIntPair(n, gen);
buckets[result.i()][result.j()]++;
}
double chi = chiSquareAll(buckets, numBuckets);
if (chi > limit95[numBuckets - 1]) countH++;
}
assertTrue(
countH <= TRIALS * 0.1, "chi square too high too often, countHigh=" + countH + " n=" + n);
}
}

@Test
public void testSampleInsertion_ThreadLocalRandom() {
for (int n = 1; n <= 6; n++) {
Expand Down

0 comments on commit 6fb19c1

Please sign in to comment.