# Fortuna
##### Fast & Flexible Random Value Generator
Copyright (c) 2018 Robert Sharp
## Primary Functions
`Fortuna.random_range(int A, int B) -> int` \
Returns a random integer within the range (A, B), inclusive uniform distribution.
Input order doesnt matter (A, B) == (B, A).
Nearly ten times faster than random.randrange() or random.randint().
`Fortuna.d(int sides) -> int` \
Returns a random integer in the range (1, sides), inclusive uniform distribution.
Represents a single die roll.
`Fortuna.dice(int rolls, int sides) -> int` \
Returns a geometric distribution based on number and size of dice rolled.
Represents the sum of multiple die rolls.
`Fortuna.plus_or_minus(int N) -> int` \
Returns random integer in the range (-N, N), inclusive uniform distribution.
`Fortuna.plus_or_minus_linear(int N) -> int` \
Returns random integer in the range (-N, N), inclusive zero peak geometric distribution.
`Fortuna.plus_or_minus_curve(int N) -> int` \
Returns random integer in the range (-N, N), inclusive zero peak gaussian distribution.
`Fortuna.percent_true(int N) -> bool` \
Returns a random Bool based on N: the probability of True as a percentage.
`Fortuna.random_value(list) -> value` \
Returns a random value from a list or tuple, non-destructive. Replaces random.choice().
## Abstractions
### Mostly:
- Constructor will take a sequence of unique values.
- Sequence must have 3 or more items, works best with 10 or more.
- Values can be any object, not just strings as in the example below.
- Provides a variety of methods for choosing a random value.
<pre>
some_sequence = ["Alpha", "Beta", "Delta", "Eta", "Gamma", "Kappa", "Zeta"]
random_monty = Fortuna.Mostly(some_sequence)
</pre>
`random_monty.mostly_front() -> value` \
Returns a random value, mostly from the front of the list (geometric)
`random_monty.mostly_middle() -> value` \
Returns a random value, mostly from the middle of the list (geometric)
`random_monty.mostly_back() -> value` \
Returns a random value, mostly from the back of the list (geometric)
`random_monty.mostly_flat() -> value` \
Returns a random value, mostly flat uniform distribution (boring)
`random_monty.mostly_first() -> value` \
Returns a random value, mostly from the very front of the list (gaussian)
`random_monty.mostly_center() -> value` \
Returns a random value, mostly from the very center of the list (gaussian)
`random_monty.mostly_last() -> value` \
Returns a random value, mostly from the very back of the list (gaussian)
`random_monty() -> value` \
Returns a random value, calls a random method above (complex)
### Random Cycle: Truffle Shuffle
- Constructor takes a sequence (list or tuple) of unique arbitrary values.
- Sequence must have 3 or more items. Works best with 10 or more.
- Values can be virtually any Python object that can be passed around... string, int, list, function etc.
- Features continuous smart micro-shuffling: The Truffle Shuffle.
<pre>
some_sequence = ["Alpha", "Beta", "Delta", "Eta", "Gamma", "Kappa", "Zeta"]
random_cycle = Fortuna.RandomCycle(some_sequence)
random_cycle() -> value
</pre>
Returns a random value, produces a uniform distribution with no consecutive duplicates and relatively few nearby duplicates.
Output sequences from RandomCycle() may seem much less mechanical compared to output of other random_value methods.
### Weighted Choice: Custom Rarity by Relative or Cumulative Weight
- Constructors will take a 2d sequence (list or tuple) of weighted values... `[(weight, value), ... ]`
- Sequence must not be empty.
- Weights must be integers.
- Values can be virtually any Python object that can be passed around... string, int, list, function etc.
The following examples produce equivalent distributions.
#### - Relative Weights:
- Returns a random value, produces a custom distribution based on relative weights.
<pre>
relative_weighted_table = (
(7, "Apple"),
(4, "Banana"),
(2, "Cherry"),
(10, "Grape"),
(3, "Lime"),
(4, "Orange"),
)
relative_weighted_choice = Fortuna.RelativeWeightedChoice(relative_weighted_table)
relative_weighted_choice() -> value
</pre>
#### - Cumulative Weights:
- Logic dictates Cumulative Weights must be unique.
- Returns a random value, produces a custom distribution based on cumulative weights.
<pre>
cumulative_weighted_table = (
(7, "Apple"),
(11, "Banana"),
(13, "Cherry"),
(23, "Grape"),
(26, "Lime"),
(30, "Orange"),
)
cumulative_weighted_choice = Fortuna.CumulativeWeightedChoice(cumulative_weighted_table)
cumulative_weighted_choice() -> value
</pre>
## Sample Distribution and Performance Test Suite
<pre>
.../fortuna_extras/fortuna_tests.py
Running 100000 cycles...
Base Case:
random.randrange(10) x 100000: 109.64 ms
0: 10.225
1: 9.959
2: 10.07
3: 10.159
4: 9.91
5: 9.901
6: 9.899
7: 9.911
8: 9.987
9: 9.979
Base Case:
random.randint(1, 10) x 100000: 131.81 ms
1: 9.867
2: 10.022
3: 9.968
4: 9.951
5: 10.078
6: 10.023
7: 9.924
8: 10.005
9: 9.996
10: 10.166
random_range(1, 10) x 100000: 10.05 ms
1: 9.903
2: 9.995
3: 9.913
4: 10.07
5: 10.066
6: 9.958
7: 10.016
8: 10.122
9: 9.982
10: 9.975
random_below(10) x 100000: 9.54 ms
0: 9.864
1: 9.951
2: 9.878
3: 10.079
4: 9.972
5: 9.986
6: 10.067
7: 10.066
8: 10.122
9: 10.015
d(6) x 100000: 9.88 ms
1: 16.737
2: 16.79
3: 16.475
4: 16.694
5: 16.722
6: 16.582
dice(2, 6) x 100000: 12.53 ms
2: 2.841
3: 5.591
4: 8.314
5: 11.179
6: 13.986
7: 16.471
8: 13.958
9: 11.066
10: 8.299
11: 5.46
12: 2.835
plus_or_minus(5) x 100000: 9.16 ms
-5: 9.165
-4: 9.124
-3: 8.957
-2: 8.97
-1: 9.25
0: 9.101
1: 9.187
2: 9.054
3: 9.003
4: 9.089
5: 9.1
plus_or_minus_linear(5) x 100000: 12.64 ms
-5: 2.783
-4: 5.46
-3: 8.375
-2: 11.075
-1: 13.714
0: 16.724
1: 14.067
2: 10.978
3: 8.479
4: 5.491
5: 2.854
plus_or_minus_curve(5) x 100000: 14.32 ms
-5: 0.198
-4: 1.197
-3: 4.433
-2: 11.542
-1: 20.379
0: 24.575
1: 20.321
2: 11.592
3: 4.397
4: 1.139
5: 0.227
percent_true(25) x 100000: 11.46 ms
False: 75.028
True: 24.972
Base Case:
random.choice(some_list) x 100000: 108.96 ms
Alpha: 14.029
Beta: 14.186
Delta: 14.25
Eta: 14.494
Gamma: 14.19
Kappa: 14.359
Zeta: 14.492
random_value(some_list) x 100000: 15.55 ms
Alpha: 14.161
Beta: 14.385
Delta: 14.214
Eta: 14.343
Gamma: 14.358
Kappa: 14.348
Zeta: 14.191
Mostly.mostly_flat() x 100000: 20.94 ms
Alpha: 14.355
Beta: 14.331
Delta: 14.495
Eta: 14.152
Gamma: 14.204
Kappa: 14.255
Zeta: 14.208
Mostly.mostly_front() x 100000: 32.99 ms
Alpha: 25.077
Beta: 21.46
Delta: 17.884
Eta: 14.269
Gamma: 10.751
Kappa: 7.083
Zeta: 3.476
Mostly.mostly_middle() x 100000: 32.9 ms
Alpha: 6.222
Beta: 12.307
Delta: 18.803
Eta: 25.018
Gamma: 18.788
Kappa: 12.627
Zeta: 6.235
Mostly.mostly_back() x 100000: 37.67 ms
Alpha: 3.562
Beta: 7.377
Delta: 10.579
Eta: 14.287
Gamma: 17.957
Kappa: 21.275
Zeta: 24.963
Mostly.mostly_first() x 100000: 35.53 ms
Alpha: 34.146
Beta: 30.006
Delta: 20.007
Eta: 10.337
Gamma: 4.052
Kappa: 1.189
Zeta: 0.263
Mostly.mostly_center() x 100000: 27.08 ms
Alpha: 0.43
Beta: 5.447
Delta: 24.246
Eta: 39.813
Gamma: 24.292
Kappa: 5.317
Zeta: 0.455
Mostly.mostly_last() x 100000: 43.31 ms
Alpha: 0.274
Beta: 1.187
Delta: 4.044
Eta: 10.149
Gamma: 20.2
Kappa: 30.061
Zeta: 34.085
Mostly() x 100000: 87.88 ms
Alpha: 10.738
Beta: 13.039
Delta: 16.126
Eta: 19.891
Gamma: 16.554
Kappa: 12.91
Zeta: 10.742
RandomCycle() x 100000: 73.87 ms
Alpha: 14.332
Beta: 14.188
Delta: 14.242
Eta: 14.262
Gamma: 14.3
Kappa: 14.292
Zeta: 14.384
RelativeWeightedChoice() x 100000: 37.82 ms
Apple: 23.205
Banana: 13.374
Cherry: 6.524
Grape: 33.497
Lime: 10.079
Orange: 13.321
CumulativeWeightedChoice() x 100000: 43.9 ms
Apple: 23.28
Banana: 13.174
Cherry: 6.658
Grape: 33.475
Lime: 10.023
Orange: 13.39
Total Test Time: 1.26 sec
</pre>