Lee Inbar
Lee Inbar

Reputation: 21

Unexpected results of randint function in python

I'm trying to simulate dnd dice rolls to get evenly distributed numbers between 1 and (for this example) 14.

The code I have is as follows:

from random import randint

def r(n):
    return randint(1, n)
def d4():
    return r(4)
def d8():
    return r(8)
def d2():
    return d4()%2+1
def d7():
    res = d8()
    while res == 8:
        res = d8()
    return res
def d14():
    return d7()*d2()

# sim
from collections import Counter

res = Counter()
for i in range(100000):
    res[d14()] += 1


t = 0
for k in sorted(res.keys()):
    print(k, res[k])
    t += res[k]

print(t)

I checked the d2 and d7 separately, and they seem to function correctly.

The sim part is just to check that every result is equally likely, but the output seems off.

1 7185
2 14260
3 7220
4 14276
5 7239
6 14349
7 7171
8 6992
10 7078
12 7124
14 7106
100000

This is what it outputed this time. It seems that 2, 4 and 6 are twice as likely to come up as anything else. Sometimes it changes without me changing the code, but that is expected as it should be random, but there are always numbers that are twice as likely to come up, and they are always even. I've done the math, and hopefully correctly, and it obviously shouldn't happen.

Is my code wrong or is there something about the randint function itself I don't know?

Upvotes: 2

Views: 77

Answers (3)

Jordan Hyatt
Jordan Hyatt

Reputation: 433

the following will give you a list of 50 numbers drawn from a uniform distribution between 1 and 14.

import numpy as np

min_val = 1
max_val = 14
n = 50
dice_vals = np.random.randint(min_val, max_val+1, n)

Change min, max and n to fit your usecase

Upvotes: -1

Sam Mason
Sam Mason

Reputation: 16213

As pointed out by @TheThonnu d14 probably isn't doing what you think it does. I'm not sure why you're not just defining it in terms of r(14), but you could continue to define it in terms of d2 and d7 by doing one of the following:

def d14():
    return (d7() - 1) * 2 + d2()

def d14():
    return (d2() - 1) * 7 + d7()

def d14():
    return d2() * 7 - d7() + 1

Because you're only generating a few options it can be useful to exhaustively enumerate them, numpy makes this relatively easy:

import numpy as np
d2, d7 = np.meshgrid(
  np.arange(2) + 1,
  np.arange(7) + 1,
)
print((d7 - 1) * 2 + d2)

gives

[[ 1  2]
 [ 3  4]
 [ 5  6]
 [ 7  8]
 [ 9 10]
 [11 12]
 [13 14]]

showing that every value appears exactly once.

Upvotes: 3

The Thonnu
The Thonnu

Reputation: 3624

return d7()*d2(): You're multiplying two random numbers.

6 can be made in 2 ways:

  • 1 * 6
  • 2 * 3

5 can be made in 1 way:

  • 1 * 5

etc.

But 9, 11 and 13 cannot be made.

Therefore 2, 4 and 6 are twice as likely to be chosen as 1, 3, 5, 7, 10, 12 and 14, and 9, 11 and 13 cannot be chosen.

num ways to make
1 1*1
2 1*2; 2*1
3 1*3
4 1*4; 2*2
5 1*5
6 1*6; 2*3
7 1*7
8 2*4
9
10 2*5
11
12 2*6
13
14 2*7

Upvotes: 7

Related Questions