RandomWeightedChooser { public E choose() { double rand = RepeatableRandom.nextDouble() * sumOfWeights; double cumulativeSum = 0; for (Entry<E, Double> entry : itemsByWeight.entrySet()) { cumulativeSum += entry.getValue(); if (cumulativeSum > rand) { return entry.getKey(); } } throw Failer.fail("Failed to make a random choice between specified elements"); } RandomWeightedChooser(ImmutableMap<E, Double> itemsByWeight); E choose();  }
@Test public void whenWeightIsZero_thenDoesNotChooseItem() { RandomWeightedChooser chooser = new RandomWeightedChooser<>(ImmutableMap.of("A", 0d, "B", 1d, "C", 1d)); RepeatableRandom.initialiseWithFixedValue(0); Assertions.assertNotEquals(chooser.choose(), "A"); RepeatableRandom.initialiseWithFixedValue(0.01); Assertions.assertNotEquals(chooser.choose(), "A"); }
@Test public void whenRepeatableRandomReturnsSpecificValue_thenReturnsAppropriateItem() { ImmutableMap<String, Double> weightedMap = ImmutableMap.of("A", 1d, "B", 2d, "C", 3d); RandomWeightedChooser chooser = new RandomWeightedChooser<>(weightedMap); double sum = weightedMap.values().stream().mapToDouble(w -> w).sum(); RepeatableRandom.initialiseWithFixedValue(0 / sum); Assertions.assertEquals(chooser.choose(), "A"); RepeatableRandom.initialiseWithFixedValue(0.9 / sum); Assertions.assertEquals(chooser.choose(), "A"); RepeatableRandom.initialiseWithFixedValue(1.1 / sum); Assertions.assertEquals(chooser.choose(), "B"); RepeatableRandom.initialiseWithFixedValue(2.9 / sum); Assertions.assertEquals(chooser.choose(), "B"); RepeatableRandom.initialiseWithFixedValue(3.1 / sum); Assertions.assertEquals(chooser.choose(), "C"); RepeatableRandom.initialiseWithFixedValue(5.9 / sum); Assertions.assertEquals(chooser.choose(), "C"); }