# Good randomness in Python code for a lottery-like game

I'm trying to build a lottery-like game in Python 3.6 where a function selects a winner from a participant list where every participant has a winning chance weighted by the value of the tickets they bought. I'm using the SystemRandom class in order to avoid software-state-dependency. Is this a good practice regarding good enough randomness? How could this code be improved? Also, does the shuffling does add anything to the randomness?

import random

# Randomly selects an element from a choice list using weighted probabilities
def randomWeightedSelection(choices, weights):

# Build index list
indices = list(range(len(choices)))

# Shuffle indices
random.shuffle(indices)

# Shuffle weights accordingly
shuffled_weights = [weights[i] for i in indices]

# Create random generator
rng = random.SystemRandom()

# Select element
selected_index = rng.choices(indices, weights=shuffled_weights, k=1)[0]

return [choices[selected_index], selected_index]


According to the Python documentation random.SystemRandom() uses os.urandom() which

[...] returns random bytes from an OS-specific randomness source. The returned data should be unpredictable enough for cryptographic applications, though its exact quality depends on the OS implementation.

Given that your system does a good enough job, the output of random.SystemRandom() should be indistinguishable from true randomness, and thus be good enough for your purpose.

Also, if the randomness provided by your system is indistinguishable from true randomness, shuffling does not add anything to the randomness of your output (even if you had also used a cryprographically secure RNG for shuffling).

In this case your code could be reduced to

import random

def randomWeightedSelection(choices, weights):
rng = random.SystemRandom()

indices = list(range(len(choices)))

selected_index = rng.choices(indices, weights = weights, k = 1)[0]

return [choices[selected_index], selected_index]

• You can go as far as using list(enumerate(choices)) for the population to get the value and the index at once: (index, value), = rng.choices(list(enumerate(choices)), weights=weights, k=1); return [value, index]. – 301_Moved_Permanently Oct 3 '18 at 16:38