Here's a general solution that will return one of any number of events, where you specify the relative weights of the events. The weights could be probabilities, but they don't have to; they don't have to add up to 1. For example, if you have three events, and you want the first one to have probability 20%, the second 30%, and the third 50%, you could call addEvent
on each event with 2, 3, and 5 as the second parameter, or 20, 30, and 50, or 0.2, 0.3, and 0.5, or any other combination of numbers that has those ratios. For your case, you could make the generic parameter an interval and add two events with weights 3 and 7 (or 30 and 70, or whatever); then, when you call randomEvent
, and it returns an interval with endpoints m
and n
inclusive, you then generate another random number in that interval:
value = m + random.nextInt(n - m + 1);
where random
is your own instance of Random
.
class RandomDistribution<T> {
private class Event {
public final T event;
public final double relativeWeight;
public Event(T event, double relativeWeight) {
this.event = event;
this.relativeWeight = relativeWeight;
}
}
private double totalWeight = 0D;
private ArrayList<Event> events = new ArrayList<>();
private Random generator = new Random();
public void addEvent(T event, double relativeWeight) {
events.add(new Event(event, relativeWeight));
totalWeight += relativeWeight;
}
public T randomEvent() {
double random = generator.nextDouble() * totalWeight;
for (Event event : events) {
random -= event.relativeWeight;
if (random < 0D) {
return event.event;
}
}
// It's possible to get here due to rounding errors
return events.get(events.size() - 1).event;
}
}