# Random Function Bias Tester

I wanted to create a short little piece of code so that I could see the biases in the random function.

import random
import statistics
import time

num = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
average = []

while True:
randomNum = random.randint(0, 9)
num[randomNum] += 1
average.append(randomNum)
print(f"\nThe average for all the numbers is {round(statistics.mean(average), round(sum(num) / 20))}")  # The second part of the round function is to keep as few digits on screen while still showing changes with every number. 20 is the arbitrary number I found worked best.
print(f"The most common number is {num.index(max(num))}\n")
time.sleep(0.5)



Since I am new-ish to python I wanted to know if, even though its only 15 lines, that I am following good coding practices. That way when my code gets more complicated, I don't have to worry about that as much

num = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


The shorthand to initialize this is [0] * 10; it can be used for strings too (bonus fact.)

round(sum(num) / 20))


This should be written as round(sum(num), 20) as the division operator adds in error, and is present through many iterations. Changing it to the latter reduced the bias.

time.sleep(0.5)


Usually the user doesn't want to wait if they don't have to; if this is run as a console program and if the user wants to inspect the output (before it disappears), it can be managed in the IDE. If the user must press a key, calling input() waits.

I would also avoid hardcoding the start and end values for random, and the array size and instead use variables.

The program will eventually run out of memory because the average list is being appended to every iteration and not being cleared. This can be prevented by instead summing the numbers (keeping a single numeric counter) and dividing by the total number of iterations. This has been an exercise left for the author.

The final code becomes:

import random
import statistics

start = 0
end = 9

num = [0] * abs(end - start + 1)
average = []

while True:
randomNum = random.randint(start, end)
num[randomNum] += 1
average.append(randomNum)
print(
f"\nThe average for all the numbers is {round(statistics.mean(average), round(sum(num), 20))}"
)
print(f"The most common number is {num.index(max(num))}\n")



Hard coding your data can be lengthy and it can restrict flexibility when carrying out further analysis. You can shorten your code with list comprehension.

import random

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

def main(val):
ls = [i for i in random.choices(n, k=val)]
ns = sum(ls)/len(ls)
print('Length of Data : {}\nSum of Data    : {}\nAverage of Data: {}\nRandom Gen Data: {}'.format(len(ls), sum(ls), ns, ls))

main(100) #Enter amount of data you want to pass through the function


This returns:

Length of Data : 100
Sum of Data    : 399
Average of Data: 3.99
Random Gen Data: [7, 4, 0, 8, 8, 0, 6, 7, 6, 4, 7, 0, 0, 0, 9, 4, 3, 0, 0, 7, 0, 4, 6, 5, 3, 0, 6, 0, 7, 0, 0, 4, 3, 7, 7, 9, 5, 3, 5, 2, 0, 4, 1, 1, 5, 8, 9, 0, 3, 2, 7, 5, 3, 3, 3, 3, 7, 5, 7, 5, 7, 1, 6, 2, 7, 7, 5, 1,6, 6, 4, 7, 8, 2, 0, 1, 5, 3, 5, 4, 9, 7, 7, 2, 0, 3, 4, 6, 3, 4, 3, 5, 6, 3, 0, 1, 3, 8, 0, 1]


It isn't necessary to use a while loop in this instance because you will still need to break your loop which is the same as giving a start and end value to a random range reference.

As noted in the answer by @alexyorke, you keep on adding to the data list. This means that your program can run out of memory. One way to reduce this risk greatly is to not save every data point, but use the data of how often each number appears.

Here are two different ways to do it, one uses a list as an array, like your code:

def mean(numbers):
return sum(i * n for i, n in enumerate(numbers)) / sum(numbers)

def most_common(numbers):
return max((n, i) for i, n in enumerate(numbers))[1]

if __name__ == "__main__":
high = 10
counter = [0] * high
for _ in range(1000):
counter[random.randrange(high)] += 1
average = round(mean(counter), round(sum(counter) / 20))
print(f"\nThe average for all the numbers is {average}")
print(f"The most common number is {most_common(counter)}\n")


This has the disadvantage that it only works for integers (because only integers can be indices for a list). Instead you could use a collections.Counter to keep the counts:

from collections import Counter

def mean(counts):
return sum(i * n for n, i in counts.items()) / sum(counts.values())

def most_common(counts):
return counts.most_common(1)[0][0]

if __name__ == "__main__":
high = 10
counter = Counter()
for _ in range(1000):
counter[random.randrange(high)] += 1
average = round(mean(counter), round(sum(counter.values()) / 20))
print(f"\nThe average for all the numbers is {average}")
print(f"The most common number is {most_common(counter)}\n")


Note that in both cases I added a if __name__ == "__main__": guard to allow importing from this script from other scripts, which is a good practice to use.

As a bonus, making a rudimentary histogram is also quite easy with the counter and some format string magic:

width = 60
norm = sum(counter.values())
for i, n in sorted(counter.items()):
print(f"{i:<2} | {'#' * int((n * width / norm)):<{width}}| {n / norm:>7.2%}")

0  | #####                                                       |   9.80%
1  | ######                                                      |  10.40%
2  | ######                                                      |  10.20%
3  | #####                                                       |   8.40%
4  | ######                                                      |  10.70%
5  | #####                                                       |   9.30%
6  | ######                                                      |  11.00%
7  | #####                                                       |   9.40%
8  | ######                                                      |  10.80%
9  | ######                                                      |  10.00%