3

So I am writing a program in part where a user can define as many random outcomes as they want. They also define the probability of each (so no, they are not equal). What is they best way to check which occured. Note: My program happens to be a Minecraft plugin, but the question is more of a general java one, so I am trying to make the code reflect that:

Map<String,Integer> possibilities = new HashMap<String,Integer>();

int x = (int) (Math.random() * 100)

My idea was to create another variable, and add the previous probability checked to it every time, and check if that was less than x. If it wasn't rinse and repeat, but I'm unsure of how to structure this.

So for is example: if the user configured it so he has 3 different outcomes, with a 30, 20, 50 percent chance respectively, how would I do this?

Allen F.
  • 151
  • 20
  • So the user gives an input <30,20,50> for possible outcomes <1,2,3> and you want to know how to draw an outcome according to these probabilities? – Tomer Levinboim Feb 27 '16 at 03:44
  • Exactly, but the user can specify any number of possibilities. The map represents the caches dataset. The probabilities just need to add up to 100. So it could be 1, 2, 3, 20, etc. – Allen F. Feb 27 '16 at 03:46

2 Answers2

2

Use a NavigableMap, which will allow you to retrieve the correct outcome with one clean and simple lookup. (And internally, this uses an efficient O(log n) lookup—not that your maps will be large enough to matter.)

import java.util.NavigableMap;
import java.util.TreeMap;
import static java.util.concurrent.ThreadLocalRandom.current;

final class LoadedDie {

  public static void main(String... argv) {

    /* One-time setup */
    NavigableMap<Integer, String> loot = new TreeMap<>();
    int cumulative = 0;
    loot.put(cumulative += 20, "Gold");
    loot.put(cumulative += 30, "Iron");
    loot.put(cumulative += 50, "Coal");

    /* Repeated use */
    System.out.println(loot.higherEntry(current().nextInt(cumulative)).getValue());
    System.out.println(loot.higherEntry(current().nextInt(cumulative)).getValue());
  }

}
erickson
  • 265,237
  • 58
  • 395
  • 493
  • I tried what you said, but I am getting a null pointer when attempting to get the result of the random. pastebin.com/ibcMqKqj – Allen F. Feb 28 '16 at 12:48
  • @AllenF. In your code, what is the value of `count` after the loop where you set up the map? It should be 100, because you might "roll" a 99 when picking a value, and there must be a key in the map higher than your roll. A better expression would be `random.nextInt(count)` (where `random` is a `java.util.Random` object). You could also do something like you have now: `(int) (Math.random() * count)`. The first option is better because you won't have to worry about any bias in the rolls. The second is *probably* okay, but I can't rule out a bias. – erickson Feb 28 '16 at 21:24
-2

Here's one way to do it.

public static String getOutcome(Map<String, Integer> possibilities) {
    int x = (int) (Math.random() * 100);

    for (Map.Entry<String, Integer> possibility : possibilities.entrySet()) {
        if (x <= possibility.getValue()) {
            return possibility.getKey();
        }
        x -= possibility.getValue();
    }

    // unreachable if probabilities are correctly mapped
    return null;
}
Andrew Jenkins
  • 1,590
  • 13
  • 16