random_distribution.py

#!/usr/bin/python3
# ====================================================================
# Generate random numbers with a given (numerical) distribution
# from: https://stackoverflow.com/questions/4265988/
# generate-random-numbers-with-a-given-numerical-distribution
# ====================================================================
# The optional keyword-only argument k allows one to request more
# than one sample at once. This is valuable because there is some
# preparatory work that random.choices has to do every time it is
# called, prior to generating any samples; by generating many samples
# at once, we only have to do that preparatory work once. Here we
# generate a million samples, and use collections.Counter to check
# that the distribution we get roughly matches the weights we gave.
#
# from random import choices
# population = [1, 2, 3, 4, 5, 6]
# weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]
# million_samples = choices(population, weights, k=10**6)
# from collections import Counter
# Counter(million_samples)
# Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636,
#          3: 50219,  2: 50025})
# ====================================================================

from random import choices

population = [0,1,2,3,4,5,6,7,8,9]
weights    = [1,2,3,4,3,3,4,3,2,1]

wtotal = 0
for w in weights:
    wtotal += w
print(f'total-weights = {wtotal}')

# ---- collect a sample of the population based on weights distribution

total  = 0
counts = [0 for _ in range(len(population))]

for _ in range(10000):
    x = choices(population,weights)
    total        += 1
    counts[x[0]] += 1
    ## ---- give the user a warm fuzzy
    ##if total % 500 == 0:
    ##    print(f'x = {x}')
    ##    print(f'processing {total}')

# ---- display sample counts, etc.

print(f'total  = {total}')
print(f'counts = {counts}')

print()
print(' idx    weight  sample')
for i,c in enumerate(counts):
    print(f'[{i:2}] ({float(weights[i]/wtotal):<7.4})  {float(c/total):6.4}')