Seven-sided dice from five-sided dice
You are encouraged to solve this task according to the task description, using any language you may know.
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.
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 + 4.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 = ref [] in let n = ref 0 in for roll1 = 1 to 5 do for roll2 = 1 to 5 do rolls2answer := ((roll1,roll2), (!n / 3) +1) :: !rolls2answer; incr n done; done; let rec aux() = let trial = List.assoc (dice5(),dice5()) !rolls2answer 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