Seven-sided dice from five-sided dice
Given an equal-probability generator of one of the integers 1 to 5 as dice5; create dice7 that generates a pseudo-random integer from 1 to 7 in equal probability using only dice5 as a source of random numbers, and check the distribution for at least 1000000 calls using the function created in Simple Random Distribution Checker.
You are encouraged to solve this task according to the task description, using any language you may know.
dice7 might call dice5 twice, re-call if four of the 25 combinations are given, otherwise split the other 21 combinations into 7 groups of three, and return the group index from the rolls.
(Task adapted from an answer here)
C
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <math.h>
void distcheck(int (*)(), int, double);
int dice5() {
return 1 + (int)(5.0*rand() / (RAND_MAX + 1.0));
}
int dice7() {
int d55; do { d55 = 5*dice5() + dice5() - 6; } while(d55 >= 21); return d55 % 7 + 1;
}
int main() {
distcheck(dice5, 1000000, 1); distcheck(dice7, 1000000, 1); return 0;
}</lang>
OCaml
<lang ocaml>let dice5() = 1 + Random.int 5 ;;
let dice7 =
let rolls2answer = Hashtbl.create 25 in let n = ref 0 in for roll1 = 1 to 5 do for roll2 = 1 to 5 do Hashtbl.add rolls2answer (roll1,roll2) (!n / 3 +1); incr n done; done; let rec aux() = let trial = Hashtbl.find rolls2answer (dice5(),dice5()) in if trial <= 7 then trial else aux() in aux
- </lang>
Python
Follows the method suggested in the task description for creating dice7, and uses a function creator for dice7, to calculate the binning of the two calls to dice5. <lang python>import re, random
onetofive = (1,2,3,4,5)
def dice5():
return random.choice(onetofive)
def dice7generator():
rolls2answer = {} n=0 for roll1 in onetofive: for roll2 in onetofive: rolls2answer[(roll1,roll2)] = (n // 3) + 1 n += 1 def dice7(): 'Generates 1 to 7 randomly, with equal prob. from dice5' trial = rolls2answer[(dice5(), dice5())] return trial if trial <=7 else dice7() return dice7
dice7 = dice7generator()</lang> Distribution check using Simple Random Distribution Checker:
>>> distcheck(dice5, 1000000, 1) {1: 200244, 2: 199831, 3: 199548, 4: 199853, 5: 200524} >>> distcheck(dice7, 1000000, 1) {1: 142853, 2: 142576, 3: 143067, 4: 142149, 5: 143189, 6: 143285, 7: 142881}
Ruby
Uses distcheck
from here.
<lang ruby>require './distcheck.rb'
def d5
1 + rand(5)
end
def d7
loop do d55 = 5*d5() + d5() - 6 return (d55 % 7 + 1) if d55 < 21 end
end
distcheck(1_000_000) {d5} distcheck(1_000_000) {d7}</lang>
output
1 200478 2 199986 3 199582 4 199560 5 200394 1 142371 2 142577 3 143328 4 143630 5 142553 6 142692 7 142849
Tcl
Any old D&D hand will know these as a D5 and a D7... <lang tcl>proc D5 {} {expr {1 + int(5 * rand())}}
proc D7 {} {
while 1 { set d55 [expr {5 * [D5] + [D5] - 6}] if {$d55 < 21} { return [expr {$d55 % 7 + 1}] } }
}</lang> Checking:
% distcheck D5 1000000 1 199893 2 200162 3 200075 4 199630 5 200240 % distcheck D7 1000000 1 143121 2 142383 3 143353 4 142811 5 142172 6 143291 7 142869