import java.util.*;
/** The StringChooser interface requires classes that implement the interface to implement
* a method called getNext that is defined in terms of zero parameters and returns a value
* of type String.
*/
interface StringChooser {
String getNext();
}
/** The PseudoRandomNumbers class and the classes it depends on define a program that
* outputs a series of values where each value is a fixed length string denoting an
* integer. The values in the series may contain leading zeros and are each followed by
* a delimiter value, which may be an empty string. If the array of String values passed
* to the program's main method is not null and the length of the array is greater than
* zero, then the delimiter is the value of the first String value in the array;
* otherwise, the delimiter is a String value comprised of a single space character.
*
* Example 1
*
* At 3 AM (PST) on 7-FEB-2019, the author used PseudoRandomNumbers to generate the
* following series of values.
*
* 853 927 068 972 146 503 149 431 675 802 802 068 853 503 149 972
*
* Questions
*
* 1. In what ways are the value of the sequence shown in Example 1 not random?
* 2. What simple change can you make to the main method so that the program produces
* sequences of length 16 that are more random than the one shown in Example 1?
* 3. What command line argument(s), if any, were used to generate the following output?
*
* 6120078098105940923042307410070065803650423036505940741065800780
*/
public class PseudoRandomNumbers
{
private static final int numCount = 16;
private static final int numLength = 3;
public static void main(String args[]) {
final String delimiter = args != null && args.length > 0 ? args[0] : " ";
final StringChooser chooser = new FixedLengthRandomIntegerChooser(numLength);
for (int n = 1; n <= numCount; n++) {
System.out.print(chooser.getNext() + delimiter);
}
}
}
/** The FixedLengthRandomIntegerChooser class implements the StringChooser interface: it
* defines a type of object that has a getNext method, which always returns a String
* value of fixed length denoting an integer. When a FixedLengthRandomIntegerChooser
* object is constructed, the length of the String values returned by getNext must be
* specified; optionally, the maximum number of String values in a pool of values that
* strings are randomly selected from may also be specified. When all of the strings
* in the pool have been consumed as a result of calling getNext, the pool of strings
* is replenished with the same set of strings that it contained when the
* FixedLengthRandomIntegerChooser object was constructed.
*/
final class FixedLengthRandomIntegerChooser implements StringChooser
{
private static final int defaultNumberPoolSize = 10;
private static final String digitPool = "0123456789";
private final RandomStringChooser chooser;
public FixedLengthRandomIntegerChooser(final int numberLength, final int numberPoolSize) {
final String[] numberPool = getIntegersAsStrings(numberLength, numberPoolSize);
chooser = new RandomStringChooser(numberPool);
}
public FixedLengthRandomIntegerChooser(final int numberLength) {
this(numberLength, defaultNumberPoolSize);
}
public String getNext() {
String next = chooser.getNext();
if (next.equals("NONE")) {
chooser.reset();
next = chooser.getNext();
}
return next;
}
public static String[] getIntegersAsStrings(final int length, final int count) {
final String[] numbers = new String[count];
RandomDigitChooser digitChooser = new RandomDigitChooser(digitPool);
for (int i = 0; i < count; i++) {
String number = "";
while (number.length() < length) {
Integer digit = digitChooser.getNext();
if (digit == null) {
digitChooser.reset();
digit = digitChooser.getNext();
}
number += digit;
}
numbers[i] = number;
}
return numbers;
}
}
/** The ResettableItemChooser<T> interface requires classes that implement the interface
* to implement: a method called reset that returns no value (i.e. has a void return type)
* and is defined in terms of zero parameters; a method called getNext that returns a value
* of type T and is defined in terms of zero parameters.
*/
interface ResettableItemChooser<T> {
void reset();
T getNext();
}
/** The RandomItemChooser<T> class implements the ResettableItemChooser<T> interface:
* it defines a generic type of object that maintains a set of available items of type T,
* any one of which may be chosen at random and removed from the set using the object's
* getNext method, which is defined in terms of zero parameters. A RandomItemChooser<T>
* object is constructed from either a non-null array of items of type T or from a non-null
* array of items of type T and a value of type T (passed to the constructor in that order),
* the latter of which is the value returned by getNext when the set of items maintained by
* the RandomItemChooser<T> object is empty; if not otherwise specified, the value null
* is returned by getNext when the set is empty. An object of type RandomItemChooser<T>
* has a method called reset (defined in terms of zero parameters) that, when called,
* removes all items from the set of available items and then adds all of the items that
* were originally included in the set when the object was first created, assuming the array
* of items that was passed to the constructor function was not subsequently modified.
*/
class RandomItemChooser<T> implements ResettableItemChooser<T>
{
private final T none;
private final T[] items;
private final List<T> list;
public RandomItemChooser(final T[] items, final T none) {
this.items = items;
this.none = none;
list = new ArrayList<T>(items.length);
reset();
}
public RandomItemChooser(final T[] items) {
this(items, null);
}
final public void reset() {
list.clear();
for (T item : items) {
list.add(item);
}
}
public T getNext() {
final int n = list.size();
final int i = (int)(Math.random() * n);
return n > 0 ? list.remove(i) : none;
}
}
/** The RandomStringChooser class extends RandomItemChooser<T>. A RandomStringChooser
* object is constructed from an array of String values. When the set of String values
* maintained by a RandomStringChooser object is empty, the value returned by invoking
* the object's getNext method is the String value "NONE".
*/
class RandomStringChooser extends RandomItemChooser<String>
{
public RandomStringChooser(final String[] strings) {
super(strings, "NONE");
}
}
/** The RandomIntegerChooser class extends RandomItemChooser<T>. A RandomIntegerChooser
* object is constructed from a non-null array of Integer values. When the set of Integer
* values maintained by a RandomIntegerChooser object is empty, the value returned by
* invoking the object's getNext method is the value null.
*/
class RandomIntegerChooser extends RandomItemChooser<Integer>
{
public RandomIntegerChooser(final Integer[] integers) {
super(integers);
}
}
/** The RandomDigitChooser class extends RandomIntegerChooser. A RandomDigitChooser object
* is constructed from a non-null String value composed of one or more characters in the
* set {'0', '1', '2', ...., '9'}. The class has a static method called getDigits that
* takes a String of digit characters as input and returns a corresponding array of
* Integer values.
*/
final class RandomDigitChooser extends RandomIntegerChooser
{
public RandomDigitChooser(final String digits) {
super(getDigits(digits));
}
public static Integer[] getDigits(final String digits) {
final int n = digits.length();
final Integer[] array = new Integer[n];
for (int i = 0; i < n; i++) {
final String digit = digits.substring(i, i + 1);
array[i] = Integer.parseInt(digit);
}
return array;
}