Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/JohnJocoo/weighted_random
Elixir weighted random pick library optimised for quick take_one and take_n operations
https://github.com/JohnJocoo/weighted_random
Last synced: 3 months ago
JSON representation
Elixir weighted random pick library optimised for quick take_one and take_n operations
- Host: GitHub
- URL: https://github.com/JohnJocoo/weighted_random
- Owner: JohnJocoo
- License: apache-2.0
- Created: 2024-05-26T20:21:33.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2024-05-29T10:47:05.000Z (6 months ago)
- Last Synced: 2024-07-13T18:43:49.275Z (4 months ago)
- Language: Elixir
- Homepage: https://hexdocs.pm/better_weighted_random
- Size: 20.5 KB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-elixir - weighted_random - Weighted random picking. (Miscellaneous)
README
# WeightedRandom
Elixir weighted random pick library optimised for quick `take_one` and `take_n` operations.
`take_one\1` and `take_n\2` behaviours are compatible with `Enum.random\1` and `Enum.take_random\2` and can almost be drop-in replacements, except that `WeightedRandom` accepts elements in format `{element :: any(), weight :: number()}`.
## Installation
The package can be installed
by adding `better_weighted_random` to your list of dependencies in `mix.exs`:```elixir
def deps do
[
{:better_weighted_random, "~> 0.1.0"}
]
end
```## Usage
`WeightedRandom.take_one\1` returns one random element from input, with probability based on weights.
```elixir
iex> :rand.seed(:exsss, {100, 101, 102})
iex> WeightedRandom.take_one([{:'1', 0.5}, {:'2', 1.0}, {:'3', 2.0}])
:"3"
````WeightedRandom.take_n\1` returns n random elements from input, with probability based on weights. Similar to `Enum.take_random\2` element can not be picked twice.
```elixir
iex> :rand.seed(:exsss, {100, 101, 102})
iex> WeightedRandom.take_n([{:'1', 0.5}, {:'2', 1.0}, {:'3', 2.0}], 2)
[:"3", :"1"]
```## Probability and weight
Probability of picking an element directly depends on its weight. `Probability = element_weight / sum(all_weights)````elixir
iex> :rand.seed(:exsss, {100, 101, 102})
iex> Enum.map(1..1000, fn _ -> WeightedRandom.take_one([{:'1', 1.0}, {:'2', 2.0}, {:'3', 1.0}]) end) |> Enum.frequencies()
%{
"1": 231,
"2": 522,
"3": 247
}
```## Searcher and quick lookups
You can create searcher with `WeightedRandom.create_searcher\1` for optimised lookups with `take_one\1` and `take_n\2`.
```elixir
iex> searcher = WeightedRandom.create_searcher([{:'1', 1.0}, {:'2', 2.0}, {:'3', 1.0}])iex> :rand.seed(:exsss, {100, 101, 102})
iex> WeightedRandom.take_one(searcher)
:"3"iex> rand.seed(:exsss, {100, 101, 102})
iex> WeightedRandom.take_n(searcher, 2)
[:"3", :"1"]
```Using searcher is quicker if you do repetative calls.
```elixir
elements = Enum.map(1..10000, fn i -> {:"el#{i}", :rand.uniform()} end)
searcher = WeightedRandom.create_searcher(elements)
```### WeightedRandom.take_one/1
```
Name ips average deviation median 99th %
take_one with searcher 787.69 K 0.00127 ms ±2423.14% 0.00109 ms 0.00180 ms
take_one with list 0.137 K 7.29 ms ±17.57% 7.03 ms 11.42 msComparison:
take_one with searcher 787.69 K
take_one with list 0.137 K - 5740.48x slower +7.29 ms
```### WeightedRandom.take_n/2 take 100 elements
```
Name ips average deviation median 99th %
take_n 100 with searcher 7.26 K 0.138 ms ±30.77% 0.124 ms 0.32 ms
take_n 100 with list 0.106 K 9.43 ms ±12.97% 8.96 ms 12.29 msComparison:
take_n 100 with searcher 7.26 K
take_n 100 with list 0.106 K - 68.46x slower +9.30 ms
```## Docs
Docs are availiable at