Skip to content

Commit 2cd116d

Browse files
committed
added arrayMask methods to RandomIndexer
1 parent 5d61553 commit 2cd116d

File tree

3 files changed

+211
-1
lines changed

3 files changed

+211
-1
lines changed

lib/jpt1.jar

302 Bytes
Binary file not shown.

src/org/cicirello/math/rand/RandomIndexer.java

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* from the motivating case, the case of efficiently generating random indexes into an array.
3434
*
3535
* @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>
36-
* @version 1.19.5.22
36+
* @version 1.19.5.31
3737
* @since 1.4
3838
*
3939
*/
@@ -890,5 +890,132 @@ public static int[] nextIntTriple(int n, int[] result, Random gen) {
890890
return result;
891891
}
892892

893+
/**
894+
* <p>Generates an "array mask" of a specified length,
895+
* where an "array mask" is an array of boolean values of the same length as another array.
896+
* Each position in the result is equally likely true or false.</p>
897+
* <p>Runtime: O(n).</p>
898+
* <p>This method uses ThreadLocalRandom as the source of randomness,
899+
* and is thus safe for use with threads.</p>
900+
*
901+
* @param n The length of the array mask.
902+
* @return An array of n randomly generated boolean values.
903+
*/
904+
public static boolean[] arrayMask(int n) {
905+
boolean[] result = new boolean[n];
906+
ThreadLocalRandom gen = ThreadLocalRandom.current();
907+
for (int i = 0; i < n; i++) {
908+
result[i] = gen.nextBoolean();
909+
}
910+
return result;
911+
}
912+
913+
/**
914+
* <p>Generates an "array mask" of a specified length,
915+
* where an "array mask" is an array of boolean values of the same length as another array.
916+
* Each position in the result is equally likely true or false.</p>
917+
* <p>Runtime: O(n).</p>
918+
*
919+
* @param n The length of the array mask.
920+
* @param gen The source of randomness.
921+
* @return An array of n randomly generated boolean values.
922+
*/
923+
public static boolean[] arrayMask(int n, SplittableRandom gen) {
924+
boolean[] result = new boolean[n];
925+
for (int i = 0; i < n; i++) {
926+
result[i] = gen.nextBoolean();
927+
}
928+
return result;
929+
}
930+
931+
/**
932+
* <p>Generates an "array mask" of a specified length,
933+
* where an "array mask" is an array of boolean values of the same length as another array.
934+
* Each position in the result is equally likely true or false.</p>
935+
* <p>Runtime: O(n).</p>
936+
*
937+
* @param n The length of the array mask.
938+
* @param gen The source of randomness.
939+
* @return An array of n randomly generated boolean values.
940+
*/
941+
public static boolean[] arrayMask(int n, Random gen) {
942+
boolean[] result = new boolean[n];
943+
for (int i = 0; i < n; i++) {
944+
result[i] = gen.nextBoolean();
945+
}
946+
return result;
947+
}
948+
949+
950+
/**
951+
* <p>Generates an "array mask" of a specified length and specified number of true values,
952+
* where an "array mask" is an array of boolean values of the same length as another array.</p>
953+
* <p>Runtime: O(min(n, k<sup>2</sup>)), and it uses O(min(k, n-k)) random numbers.</p>
954+
* <p>This method uses ThreadLocalRandom as the
955+
* pseudorandom number generator, and is thus safe for use with threads.</p>
956+
*
957+
* @param n The length of the array mask.
958+
* @param k The desired number of true values, which must be no greater than n.
959+
* @return An array of n boolean values, exactly k of which are equal to true.
960+
*/
961+
public static boolean[] arrayMask(int n, int k) {
962+
boolean[] result = new boolean[n];
963+
if (k >= n) {
964+
for (int i = 0; i < n; i++) result[i] = true;
965+
} else if (k > 0) {
966+
int[] indexes = sample(n, k, null);
967+
for (int i = 0; i < k; i++) {
968+
result[indexes[i]] = true;
969+
}
970+
}
971+
return result;
972+
}
973+
974+
/**
975+
* <p>Generates an "array mask" of a specified length and specified number of true values,
976+
* where an "array mask" is an array of boolean values of the same length as another array.</p>
977+
* <p>Runtime: O(min(n, k<sup>2</sup>)), and it uses O(min(k, n-k)) random numbers.</p>
978+
*
979+
* @param n The length of the array mask.
980+
* @param k The desired number of true values, which must be no greater than n.
981+
* @param gen The source of randomness.
982+
* @return An array of n boolean values, exactly k of which are equal to true.
983+
*/
984+
public static boolean[] arrayMask(int n, int k, SplittableRandom gen) {
985+
boolean[] result = new boolean[n];
986+
if (k >= n) {
987+
for (int i = 0; i < n; i++) result[i] = true;
988+
} else if (k > 0) {
989+
int[] indexes = sample(n, k, null, gen);
990+
for (int i = 0; i < k; i++) {
991+
result[indexes[i]] = true;
992+
}
993+
}
994+
return result;
995+
}
996+
997+
/**
998+
* <p>Generates an "array mask" of a specified length and specified number of true values,
999+
* where an "array mask" is an array of boolean values of the same length as another array.</p>
1000+
* <p>Runtime: O(min(n, k<sup>2</sup>)), and it uses O(min(k, n-k)) random numbers.</p>
1001+
*
1002+
* @param n The length of the array mask.
1003+
* @param k The desired number of true values, which must be no greater than n.
1004+
* @param gen The source of randomness.
1005+
* @return An array of n boolean values, exactly k of which are equal to true.
1006+
*/
1007+
public static boolean[] arrayMask(int n, int k, Random gen) {
1008+
boolean[] result = new boolean[n];
1009+
if (k >= n) {
1010+
for (int i = 0; i < n; i++) result[i] = true;
1011+
} else if (k > 0) {
1012+
int[] indexes = sample(n, k, null, gen);
1013+
for (int i = 0; i < k; i++) {
1014+
result[indexes[i]] = true;
1015+
}
1016+
}
1017+
return result;
1018+
}
1019+
8931020

8941021
}

tests/org/cicirello/math/rand/RandomIndexerTests.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,89 @@ public class RandomIndexerTests {
3636

3737
private static double EPSILON = 1e-10;
3838

39+
@Test
40+
public void testArrayMask() {
41+
SplittableRandom r1 = new SplittableRandom(42);
42+
Random r2 = new Random(42);
43+
for (int n = 1; n <= 5; n++) {
44+
for (int k = 0; k <= n; k++) {
45+
boolean[] mask = RandomIndexer.arrayMask(n, k);
46+
assertEquals("length incorrect", n, mask.length);
47+
int count = 0;
48+
for (int i = 0; i < mask.length; i++) {
49+
if (mask[i]) count++;
50+
}
51+
assertEquals("wrong number of true values", k, count);
52+
53+
mask = RandomIndexer.arrayMask(n, k, r1);
54+
assertEquals("length incorrect", n, mask.length);
55+
count = 0;
56+
for (int i = 0; i < mask.length; i++) {
57+
if (mask[i]) count++;
58+
}
59+
assertEquals("wrong number of true values", k, count);
60+
61+
mask = RandomIndexer.arrayMask(n, k, r2);
62+
assertEquals("length incorrect", n, mask.length);
63+
count = 0;
64+
for (int i = 0; i < mask.length; i++) {
65+
if (mask[i]) count++;
66+
}
67+
assertEquals("wrong number of true values", k, count);
68+
69+
mask = RandomIndexer.arrayMask(n);
70+
assertEquals("length incorrect", n, mask.length);
71+
72+
mask = RandomIndexer.arrayMask(n, r1);
73+
assertEquals("length incorrect", n, mask.length);
74+
75+
mask = RandomIndexer.arrayMask(n, r2);
76+
assertEquals("length incorrect", n, mask.length);
77+
}
78+
}
79+
80+
final int TRIALS = 120000;
81+
for (int n = 1; n <= 5; n++) {
82+
final int MAX = TRIALS / n;
83+
int[] buckets = {0, 0};
84+
for (int t = 0; t < MAX; t++) {
85+
boolean[] mask = RandomIndexer.arrayMask(n);
86+
for (int i = 0; i < n; i++) {
87+
if (mask[i]) buckets[1]++;
88+
else buckets[0]++;
89+
}
90+
}
91+
double chi = chiSquare(buckets);
92+
assertTrue("Using ThreadLocalRandom, chi square goodness of fit test: " + chi, chi <= 3.841);
93+
}
94+
for (int n = 1; n <= 5; n++) {
95+
final int MAX = TRIALS / n;
96+
int[] buckets = {0, 0};
97+
for (int t = 0; t < MAX; t++) {
98+
boolean[] mask = RandomIndexer.arrayMask(n, r1);
99+
for (int i = 0; i < n; i++) {
100+
if (mask[i]) buckets[1]++;
101+
else buckets[0]++;
102+
}
103+
}
104+
double chi = chiSquare(buckets);
105+
assertTrue("Using SplittableRandom, chi square goodness of fit test: " + chi, chi <= 3.841);
106+
}
107+
for (int n = 1; n <= 5; n++) {
108+
final int MAX = TRIALS / n;
109+
int[] buckets = {0, 0};
110+
for (int t = 0; t < MAX; t++) {
111+
boolean[] mask = RandomIndexer.arrayMask(n, r2);
112+
for (int i = 0; i < n; i++) {
113+
if (mask[i]) buckets[1]++;
114+
else buckets[0]++;
115+
}
116+
}
117+
double chi = chiSquare(buckets);
118+
assertTrue("Using Random, chi square goodness of fit test: " + chi, chi <= 3.841);
119+
}
120+
}
121+
39122
@Test
40123
public void testRandInt_ThreadLocalRandom() {
41124
final int REPS_PER_BUCKET = 100;

0 commit comments

Comments
 (0)