Skip to content

Commit

Permalink
Linear Probability Generator (#86)
Browse files Browse the repository at this point in the history
Generators:
* Create LinearShortElement - add constructor with Random argument, implement generate method, store number of LinearStates as member variable
* Create ShortElementInterface

Tests:
* Create LinearShortElementAnalysis - measure distributions of outcomes, combine length test cases into parameterized test
* Create LinearShortElementPerformance - measure performance of different outcomes
* Create LinearShortElementStaticTest - add test cases for the countLinearStates method
* Create LinearShortElementTest
* Update RandomProvider - update code style
  • Loading branch information
DK96-OS committed Apr 7, 2022
1 parent cc6781b commit ea0b7fe
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package mathtools.generators.elements.shorts;

import java.security.SecureRandom;
import java.util.Random;

import javax.annotation.Nonnull;

/** An element with a linearly changing probability.
* Max Length (Number of outcomes) = 180
* Max Rate of Change (Linear Slope) = 200
* @author DK96-OS : 2022 */
public final class LinearShortElement
implements ShortElementInterface {

private short mLength;

private int mLengthStates;

private final Random mRNG;

/** Create a new Linear Short Element with SecureRandom RNG
* @param length The number of outcomes
* @throws IllegalArgumentException if length is negative or zero, or rate is negative or zero
*/
public LinearShortElement(
final short length
) throws IllegalArgumentException {
// Validate arguments
if (1 > length || 180 < length)
throw new IllegalArgumentException("Invalid Length");
//
mLength = length;
mLengthStates = countLinearStates(length);
mRNG = new SecureRandom();
}

/** Create a new Linear Short Element with a given RNG instance
* @param length The number of outcomes
* @param rng The Random number generator to use
* @throws IllegalArgumentException if length is negative or zero, or rate is negative or zero
*/
public LinearShortElement(
final short length,
@Nonnull final Random rng
) { // Validate arguments
if (1 > length || 180 < length)
throw new IllegalArgumentException("Invalid Length");
//
mLength = length;
mLengthStates = countLinearStates(length);
mRNG = rng;
}

/** Obtain the Range Length, or the number of outcomes */
public short getLength() {
return mLength;
}

/** Set the length of the Element, or the number of outcomes
* @param length The new number of outcomes
* @return Whether the new value is valid, and has been updated */
public boolean setLength(
final short length
) {
if (1 > length ||
180 < length ||
mLength == length
) return false;
//
mLength = length;
mLengthStates = countLinearStates(length);
return true;
}

@Override
public short generate() {
// Select one of the Element micro-states
final int selectedState = mRNG.nextInt(mLengthStates);
//
int sCounter = mLength - 1;
short tCounter = 1;
for (;
selectedState > sCounter && mLength > tCounter;
++tCounter
) {
sCounter += mLength - tCounter;
}
return tCounter;
}

/** Calculate the minimum number of Microstates for a Linear element
* @param length The number of Macrostates. Must be less than 181.
* @return The minimum number of Microstates required */
public static int countLinearStates(
short length
) {
// Zero or negative lengths are invalid
if (1 > length) return 0;
// Max length is 180
if (180 < length) return 16290;
// Count the states
int states;
// Split the range of lengths in half
if (80 <= length) { // 80+
if (150 <= length) { // 150+
for (states = 11325; // State count for 150
150 < length;
--length
) states += length;
} else { // 80 -> 149
for (states = 3240;// State count for 80
80 < length;
--length
) states += length;
}
} else if (40 <= length) { // 40 -> 79
for (states = 820; // State count for 40
40 < length;
--length
) states += length;
} else { // 1 -> 39
for (states = 1;
1 < length;
--length
) states += length;
}
return states;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package mathtools.generators.elements.shorts;

/** An Element that produces 16-bit Short integers */
public interface ShortElementInterface {

/** Generate a new short */
short generate();

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ public static Random firstThenSecondValue(
private final int mSecond = second;

@Override
public int nextInt(int bound) { return nextInt(); }
public int nextInt(
final int bound
) {
return nextInt();
}

@Override
public int nextInt() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package mathtools.generators.elements.shorts;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.List;

import mathtools.generators.counters.ints.IntCounter32000;

/** Inspection of the generated values of [LinearShortElement]
* @author DK96-OS : 2022 */
public final class LinearShortElementAnalysis {

private LinearShortElement mElement;
private IntCounter32000 mCounter;

@AfterEach
public void testCleanup() {
mElement = null;
mCounter = null;
}

@ParameterizedTest
@ValueSource(shorts = { 9, 25, 100, 150 })
public void testLengths(
final short length
) {
mElement = new LinearShortElement(length);
mCounter = new IntCounter32000(1, length);
//
for (int i = 0; 160_000 > i; ++i) {
assertTrue(
mCounter.count(mElement.generate()));
}
final List<Integer> results = mCounter.toList();
// Print the results
for (int i = 0; i < results.size(); ++i) {
final int outcome = i + 1;
System.out.printf(
"%d : %d\n", outcome, results.get(i)
);
}
assert 0 < mCounter.getCountOf(1);
assert 0 < mCounter.getCountOf(length);
assertEquals(
length, results.size());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package mathtools.generators.elements.shorts;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static mathtools.generators.elements.shorts.LinearShortElement.countLinearStates;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.Random;

import mathtools.generators.RandomProvider;

/** Compare the Performance of [LinearShortElement] scenarios */
public final class LinearShortElementPerformance {

private LinearShortElement mElement1;
private LinearShortElement mElementLarge;

private static final short largeLength = 150;
private static final int mStateCountLarge = countLinearStates(largeLength);

private static final int trials = 1_000_000;

private final Random mFixedRandom1 = RandomProvider.fixedValue(1);
private final Random mFixedRandomLarge = RandomProvider.fixedValue(mStateCountLarge);

@BeforeEach
public void testSetup() {
mElement1 = new LinearShortElement(
largeLength, mFixedRandom1
);
mElementLarge = new LinearShortElement(
largeLength, mFixedRandomLarge
);
}

@Test
public void compareStateSelection() {
for (int i = 0; trials > i; ++i) {
mElement1.generate();
mElementLarge.generate();
}
}

@Test
public void compareStateSelection1() {
for (int i = 0; trials > i; ++i) {
assertEquals(
1, mElement1.generate());
}
}

@Test
public void compareStateSelectionLarge() {
for (int i = 0; trials > i; ++i) {
assertEquals(
largeLength, mElementLarge.generate());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package mathtools.generators.elements.shorts;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static mathtools.generators.elements.shorts.LinearShortElement.countLinearStates;

import org.junit.jupiter.api.Test;

/** Testing [LinearShortElement] static methods
* @author DK96-OS : 2022 */
public final class LinearShortElementStaticTest {

@Test
public void testCountLinearStatesNormal() {
short i = 1;
assertEquals(
1, countLinearStates(i++));
assertEquals(
3, countLinearStates(i++));
assertEquals(
6, countLinearStates(i++));
//
int previous = 6;
for (;
181 > i;
++i
) {
final int next = countLinearStates(i);
assertEquals(
previous + i, next);
previous = next;
}
}

@Test
public void testCountLinearStatesTooLarge() {
assertEquals(
16290, countLinearStates((short) 181));
assertEquals(
16290, countLinearStates((short) 200));
assertEquals(
16290, countLinearStates((short) 2000));
}

@Test
public void testCountLinearStatesNegative() {
assertEquals(
0, countLinearStates((short) -1));
assertEquals(
0, countLinearStates((short) 0));
assertEquals(
0, countLinearStates((short) -180));
}

}
Loading

0 comments on commit ea0b7fe

Please sign in to comment.