I've been learning Java for only about a week and a half. At this point, the only way I could think of to include an element of randomness in my code is to use the Random class's nextInt method. Below is a portion of my method (I can give you the entire code on your request though it seems unnecessary)
Random random = new Random();
int randomInt = random.nextInt(10) 1;
[...]
if(randomInt <= 3){
System.out.println("Magnificent!");
} else if (randomInt >= 7){
System.out.println("Marvelous!");
} else {
System.out.println("Delectable!");
}
However, it's wordy and lacks flexibility. What I would like to be able to do is to distribute pieces of total probability of 1 to different scenarios in a concise way: thing A happens with a probability of 0.3, thing B happens with a probability of, say, 0.5, thing С happens with a probability of 0.2. How could I achieve that?
CodePudding user response:
In a general case, you have a set of weighted values, which can produce a random value based on a probability which is its weight as fraction of the total weights. The weights can be normalized (meaning their sum is 1), or non-normalized, which is the easier assumption.
When rolling a random value out of that set, it's most efficient to check for the values with the highest probability first. Roll a random number between 0 and the total weight, then go through the values in order of their weight (descending), and check if the random number is lower than the cumulative weight -> if so, return that value.
Code:
public class WeightedRandom<T> {
private final Comparator<WeightedValue<T>> byWeight =
Comparator.comparing(wv -> wv.weight);
private final Set<WeightedValue<T>> weightedValues =
new TreeSet<>(byWeight.reversed());
private double totalWeight;
void put(double weight, T value) {
if (weight <= 0) {
return;
}
totalWeight = weight;
weightedValues.add(new WeightedValue<>(weight, value));
}
public T next() {
if (weightedValues.isEmpty()) {
throw new NoSuchElementException();
}
double rnd = ThreadLocalRandom.current().nextDouble(totalWeight);
double sum = 0;
Iterator<WeightedValue<T>> iterator = weightedValues.iterator();
WeightedValue<T> result;
do {
result = iterator.next();
sum = result.weight;
} while (rnd > sum && iterator.hasNext());
return result.value;
}
private static class WeightedValue<T> {
final double weight;
final T value;
public WeightedValue(double weight, T value) {
this.weight = weight;
this.value = value;
}
}
}
Example:
public static void main(String[] args) {
WeightedRandom<String> random = new WeightedRandom<>();
random.put(3, "AAA");
random.put(2, "BBB");
random.put(5, "CCC");
for (int i = 0; i < 1000; i ) {
String value = random.next();
System.out.println(value);
}
}
CodePudding user response:
Usually, you're going to use Random.nextDouble(), and test e.g. random.nextDouble() < 0.3
to have a probability of 0.3.
To test more than one possibility, you will need to sum some things, e.g
double r = random.nextDouble();
if (r < 0.3) {
...0.3 probability
} else if (r < 0.8) {
...0.5 probability
} else {
...0.2 probability
}