Last active
June 27, 2018 22:53
-
-
Save gerryjenkinslb/c9c0b5184cc076af3f7cb9ee0ba3f1b1 to your computer and use it in GitHub Desktop.
Python code to create random events where the events have different probabilites specified by a frequence table.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
""" Create random events where event probabilities are | |
set by a frequency list | |
This shows two functional approaches, the second being more terse | |
and then a object oriented approach""" | |
# functional approach using a closure | |
# setup: x = event_gen(freq_table) | |
# get sample: x() -> sample index | |
def event_gen(freq_table): | |
n = len(freq_table) | |
cumulative_frequency = [] | |
total = 0.0 | |
for p in freq_table: # build cumlative frequencies | |
total += p | |
cumulative_frequency.append(total) | |
def event_f(): | |
r = random.uniform(0, total) # between 0.0 and total | |
for i in range(n): | |
if cumulative_frequency[i] >= r: # find interval | |
return i | |
return event_f | |
# functional approach using a closure | |
# and generators/list comprehensions | |
# setup: x = event_gen2(freq_table) | |
# get sample: x() -> sample index | |
def event_gen2(freq_table): | |
cumulative_frequency = [sum(freq_table[:i+1]) | |
for i in range(len(freq_table))] # terse but O(n squared) | |
def event_i(): | |
r = random.uniform(0, cumulative_frequency[-1]) | |
return next((i for i, t | |
in enumerate(cumulative_frequency) if t >= r)) | |
# return function that returns random sample index of event | |
return event_i | |
# Object Oriented approach | |
# setup: x = Event_gen(freq_table) # create object | |
# get sample: x() -> sample index | |
class EventGen(object): | |
def __init__(self, freq_table): | |
n = len(freq_table) | |
self.cumulative_frequency = [] | |
total = 0.0 | |
for p in freq_table: | |
total += p | |
self.cumulative_frequency.append(total) | |
def __call__(self): # allows us to 'call' object | |
r = random.uniform(0, self.cumulative_frequency[-1]) | |
i = next((i for i, t in | |
enumerate(self.cumulative_frequency) if t >= r)) | |
return i | |
def test(): | |
# function/class test label | |
tests = ( (event_gen, 'event_gen functional case'), | |
(event_gen2,'event_gen2 functional case'), | |
(EventGen, 'EventGen OOP case'), | |
) | |
# simulate events for a game at sea | |
freq_table = (20, 3, 1, 5, 2, 1) | |
events = ('calm day', 'storm', 'alien', | |
'pirate', 'land', 'sea monster') | |
n = 1000000 # run n samples | |
for gen, label in tests: # run each case | |
sample = gen(freq_table) # get sampler function function | |
hist = {e: 0 for e in events} # histogram will be built | |
for _ in range(n): | |
hist[events[sample()]] += 1 # add 1 to historgram bucket | |
total = sum(freq_table) | |
print(label) | |
for event in hist.keys(): | |
print(f"{event:11s} {hist[event]:6d} " | |
f"{hist[event]*total/n:5.2f}") | |
print() | |
if __name__ == '__main__': | |
test() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment