0

So I am simply playing around with trying to make a "dice roller" using random.getrandbits() and the "wasteful" methodology stated here: How to generate an un-biased random number within an arbitrary range using the fewest bits

My code seems to be working fine, however when I roll D6's the Max\Min ratio is in the 1.004... range but with D100's it's in the 1.05... range. Considering my dataset is only about a million rolls, is this ok or is the pRNG nature of random affecting the results? Or am I just being an idiot and overthinking it and it's due to D100s simply having a larger range of values than a D6?

Edit: Max/Min ratio is the frequency of the most common result divided by the frequency of the least common result. For a perfectly fair dice this should be 1.

from math import ceil, log2
from random import getrandbits

def wasteful_die(dice_size: int):
    #Generate minumum binary number greater than or equal to dice_size number of random bits
    bits = getrandbits(ceil(log2(dice_size)))

    #If bits is a valid number (i.e. its not greater than dice_size), yeild
    if bits < dice_size:
        yield 1 + bits


def generate_rolls(dice_size: int, number_of_rolls: int) -> list:
    #Store the results
    list_of_numbers = []

    #Command line activity indicator
    print('Rolling '+f'{number_of_rolls:,}'+' D'+str(dice_size)+'s',end='',flush=True)
    activityIndicator = 0

    #As this is a wasteful algorithm, keep rolling until you have the desired number of valid rolls.
    while len(list_of_numbers) < number_of_rolls:
        #Print a period every 1000 attempts
        if activityIndicator % 1000 == 0:
            print('.',end='',flush=True)

        #Build up the list of rolls with valid rolls.
        for value in wasteful_die(dice_size):
            list_of_numbers.append(value)
        activityIndicator+=1
    print(' ',flush=True)

    #Use list slice just in case something wrong.
    return list_of_numbers[0:number_of_rolls]

#Rolls one million, fourty eight thousand, five hundred and seventy six D6s
print(generate_rolls(6, 1048576), file=open("RollsD6.txt", "w"))

#Rolls one million, fourty eight thousand, five hundred and seventy six D100
print(generate_rolls(100, 1048576), file=open("RollsD100.txt", "w"))
  • What is this "Max\Min" ratio? Your code produces no such output, and your question doesn't explain it. Explain what this metric is supposed to represent, including the expected ranges and dependencies. – Prune Mar 11 '20 at 16:26
  • Max min ratio is the frequency of the most common result divided by the frequency of the least common result. For a perfectly fair dice this should be 1. – Gregory Rosenberg Mar 11 '20 at 16:33

1 Answers1

0

Your final statement is incorrect: for a perfectly fair douse (never say die :-) ), the ratio should tend to 1.0, but should rarely land directly on that value for large numbers of rolls. To hit 1.0 regularly requires the die to know the history of previous rolls, which violates the fairness principles.

A variation of 0.4% for a D6 is reasonable over 10^6 rolls, as is 0.5% for a D100. As you surmised, this is because the D100 has many more "buckets" (different values).

The D6 will average 10^6/6, or nearly 170K expected instances per "bucket". A D100 has only 10K expected instances per bucket: somewhat less room for the Law of Central Tendency to influence the numbers. Having a 50:4 difference in a single test run is well within expectations.

I suggest that you try running a chi-squared test, rather than a simple max/min metric.

Prune
  • 76,765
  • 14
  • 60
  • 81