Home > Blockchain >  How do you introduce probability in your Java methods – in a flexible and concise fashion?
How do you introduce probability in your Java methods – in a flexible and concise fashion?

Time:12-14

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
}
  • Related