Livebook
The best way to learn is through the interactive Livebook tutorial
Quickstart
Randomness from outcomes and weights.
outcomes = 0..10
weight = %{target: 5, weight: 20}
r = WeightedRandom.preprocess(0..100, [weight])
count = 8
[4, 5, 5, 5, 4, 1, 5, 5] = WeightedRandom.take(r, count)Alternately, if you care less about performance, and only need to do it once:
[5, 5, 2] = WeightedRandom.rand(0..10, [%{target: 5, weight: 20}], take: 3)But please note the algorithm is optimized to take longer ( O(n) ) during preprocessing, in order to be very fast ( O(1) ) during the sampling step.
So it will be far better to use WeightedRandom.preprocess/3 once, and WeightedRandom.take/2 many times. rand/3 preprocesses EVERY time it is called.
Randomness from probabilities
Mirroring the functions above, we can also use the *_p functions to use probabilities instead of outcomes + weights
probabilities = [0.01, 0.01, 0.01, 0.95, 0.01, 0.01]
r = WeightedRandom.preprocess_p(probabilities)
count = 5
[3, 3, 3, 3, 3] = WeightedRandom.take(r, count)Or, in rand form (with the same disclaimer)
probabilities = [0.01, 0.01, 0.01, 0.95, 0.01, 0.01]
count = 5
[3, 3, 3, 3, 3] = WeightedRandom.rand_p(r, count)
Summary
Functions
Given a non-empty list (or range) of possible outcomes, and a list of weight maps, build a struct that can later be used for very fast random sampling.
Given a non-empty list of percentages (floats from 0.0 - 1.0), build the struct.
Returns a random value based on the weights given.
If you need a lot of random numbers over time, this is suboptimal and you should use preprocess + take instead.
similar to rand/3 but instead of a list of outcomes, and a list of weights, rand_p/3 accepts a list of probability floats.
If you need a lot of random numbers over time, this is suboptimal and you should use preprocess + take instead.
Given a WeightedRandom struct, return a single random value.
Given a WeightedRandom struct, return a list of random values
Functions
@spec preprocess( WeightedRandom.Utils.Types.outcomes(), [WeightedRandom.Utils.Types.weight_spec()], WeightedRandom.Utils.Types.opts() ) :: WeightedRandom.Backend.t()
Given a non-empty list (or range) of possible outcomes, and a list of weight maps, build a struct that can later be used for very fast random sampling.
Next, pass the resulting struct into WeightedRandom.take/2 to get rand
Examples
iex> r = WeightedRandom.preprocesses(0..10, [%{target: 2, amount: 1000}])
iex> li = WeightedRandom.take(r, 5)
[2, 2, 2, 2, 2]Supported options:
:backend(atom/0) - Required. Any module which implements the @behaviour:WeightedRandom.Backend. This is the core algorithm providing the randomness functionality.:precision(pos_integer/0) - The number of decimal places to use when rounding. Leave nil for no rounding.:outcome_type- When you take a random sample, will it return the index of an outcome, or the value?index:pick random indices from the list of outcomes. For example if your outcomes are125..130then the results will be between0and5.value:pick random values from the list of outcomes. For example if your outcomes are125..130then the results will be between125and130.
The default value is
:index.
@spec preprocess_p( WeightedRandom.Utils.Types.probabilities(), WeightedRandom.Utils.Types.opts() ) :: WeightedRandom.Backend.t()
Given a non-empty list of percentages (floats from 0.0 - 1.0), build the struct.
Next, pass the resulting struct into WeightedRandom.take/2 to get rand
Examples
iex> r = WeightedRandom.preprocess_p([0.01, 0.01, 0.98])
iex> li = WeightedRandom.take(r, 5)
[2, 2, 2, 2, 2]Supported options:
:backend(atom/0) - Required. Any module which implements the @behaviour:WeightedRandom.Backend. This is the core algorithm providing the randomness functionality.:precision(pos_integer/0) - The number of decimal places to use when rounding. Leave nil for no rounding.
@spec rand( outcomes :: WeightedRandom.Utils.Types.outcomes(), weights :: [WeightedRandom.Utils.Types.weight_spec()], WeightedRandom.Utils.Types.opts() ) :: any()
Returns a random value based on the weights given.
If you need a lot of random numbers over time, this is suboptimal and you should use preprocess + take instead.
Supported options:
:precision(pos_integer/0) - The number of decimal places to use when rounding. Leave nil for no rounding.:outcome_type- When you take a random sample, will it return the index of an outcome, or the value?index:pick random indices from the list of outcomes. For example if your outcomes are125..130then the results will be between0and5.value:pick random values from the list of outcomes. For example if your outcomes are125..130then the results will be between125and130.
The default value is
:index.:take(pos_integer/0) - If used, then instead of returning one random value, will return a list (size == :take) of random values:backend(atom/0) - Any module which implements the @behaviour:WeightedRandom.Backend. This is the core algorithm providing the randomness functionality.
@spec rand_p( WeightedRandom.Utils.Types.probabilities(), WeightedRandom.Utils.Types.opts() ) :: any()
similar to rand/3 but instead of a list of outcomes, and a list of weights, rand_p/3 accepts a list of probability floats.
If you need a lot of random numbers over time, this is suboptimal and you should use preprocess + take instead.
Supported options:
:backend(atom/0) - Any module which implements the @behaviour:WeightedRandom.Backend. This is the core algorithm providing the randomness functionality.:precision(pos_integer/0) - The number of decimal places to use when rounding. Leave nil for no rounding.:take(pos_integer/0) - If used, then instead of returning one random value, will return a list (size == :take) of random values
@spec take(WeightedRandom.Backend.t()) :: any()
Given a WeightedRandom struct, return a single random value.
Examples
iex> r = WeightedRandom.preprocesses(0..10, [%{target: 2, amount: 1000}])
iex> li = WeightedRandom.take(r)
2
@spec take(WeightedRandom.Backend.t(), count :: integer()) :: list()
Given a WeightedRandom struct, return a list of random values
Examples
iex> # Make the item at index 2 1000x more likely than any other single index.
iex> r = WeightedRandom.preprocesses(0..10, [%{target: 2, amount: 1000}])
iex> li = WeightedRandom.take(r, 3)
[2, 2, 2]