Task

(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 one million calls using the function created in   Simple Random Distribution Checker.


Implementation suggestion:
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)

import random, tables
 
 
proc dice5(): int = rand(1..5)
 
 
proc dice7(): int =
  while true:
    let val = 5 * dice5() + dice5() - 6
    if val < 21:
      return val div 3 + 1
 
 
proc checkDist(f: proc(): int; repeat: Positive; tolerance: float) =
 
  var counts: CountTable[int]
  for _ in 1..repeat:
    counts.inc f()
 
  let expected = (repeat / counts.len).toInt    # Rounded to nearest.
  let allowedDelta = (expected.toFloat * tolerance / 100).toInt
  var maxDelta = 0
  for val, count in counts.pairs:
    let d = abs(count - expected)
    if d > maxDelta: maxDelta = d
 
  let status = if maxDelta <= allowedDelta: "passed" else: "failed"
  echo "Checking ", repeat, " values with a tolerance of ", tolerance, "%."
  echo "Random generator ", status, " the uniformity test."
  echo "Max delta encountered = ", maxDelta, "   Allowed delta = ", allowedDelta
 
 
when isMainModule:
  import random
  randomize()
  checkDist(dice7, 1_000_000, 0.5)