Mix.install(
[
{:weighted_random, "~> 1.0.0-alpha.2"}
]
)Tutorial
The most optimal workflow is to:
- Define your requirements (outcomes, weights, probabilities, other options)
- Preprocess it to create a struct that is optimized for making future sampling faster
- Take n random values
Randomness from probabilities
# Probabilities are floats representing percentages, that should add up to 1.0
probabilities = [0.1, 0.6, 0.2, 0.1]
optimized_struct = WeightedRandom.preprocess_p(probabilities)
n = 5
WeightedRandom.take(optimized_struct, n)Alternately, if you won't need the same probabilities again:
opts = [take: 5]
WeightedRandom.rand_p(probabilities, opts)
# Try calling this function *without* opts tooRandomness from outcomes and weights
When given 4 equal probabilities (25% each), you could also write them as [1/4, 1/4, 1/4, 1/4].
What if we simplified it into a list of [1, 1, 1, 1], and automatically normalized it to divide each by the whole?
That is exactly what a weight is.
One benefit over probabilities is that you can adjust one weight without needing to manually recalculate all of them.
To use this, we must now decouple the outcomes from the weights. Before, the index of the probability WAS the outcome.
outcomes = 0..3
weight = %{target: 1, weight: 2}
opts = []
# under the hood, this creates the probabilities: `[1/5, 2/5, 1/5, 1/5]`
optimized_struct = WeightedRandom.preprocess(outcomes, [weight], opts)
n = 5
WeightedRandom.take(optimized_struct, n)
#=> [2, 1, 1, 0, 1]Be careful to call the right function. The _p suffix means it expects probabilities.
| Weights | Probabilities |
|---|---|
WeightedRandom.preprocess/3 | WeightedRandom.preprocess_p/2 |
WeightedRandom.rand/3 | WeightedRandom.rand_p/2 |
WeightedRandom.take/2 | WeightedRandom.take/2 |
Another benefit of weights is that you can do more than random numbers
outcomes = ["a", "b", :c, 3.14]
weights = [%{target: 2, amount: 20}]
WeightedRandom.rand(outcomes, weights)Using the :outcome_type option, you can even target the value of an outcome, rather than the index.
outcomes = ["a", "b", :c, 3.14]
weights = [%{target: "b", amount: 10}]
WeightedRandom.rand(outcomes, weights, outcome_type: :value)outcomes = 100..110
weights = [%{target: 104, amount: 20}]
WeightedRandom.rand(outcomes, weights, outcome_type: :value)