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)

// version 1.1.3
 
import java.util.Random
 
val r = Random()
 
fun dice5() = 1 + r.nextInt(5)
 
fun dice7(): Int {
    while (true) {
       val t = (dice5() - 1) * 5 + dice5() - 1
       if (t >= 21) continue
       return 1 + t / 3
    }
}
 
fun checkDist(gen: () -> Int, nRepeats: Int, tolerance: Double = 0.5) {
    val occurs = mutableMapOf<Int, Int>()
    for (i in 1..nRepeats) {
        val d = gen()
        if (occurs.containsKey(d))
            occurs[d] = occurs[d]!! + 1
        else
            occurs.put(d, 1)
    }
    val expected = (nRepeats.toDouble()/ occurs.size).toInt()
    val maxError = (expected * tolerance / 100.0).toInt()
    println("Repetitions = $nRepeats, Expected = $expected")
    println("Tolerance = $tolerance%, Max Error = $maxError\n")
    println("Integer   Occurrences   Error  Acceptable")
    val f = "  %d        %5d      %5d     %s" 
    var allAcceptable = true
    for ((k,v) in occurs.toSortedMap()) {
        val error = Math.abs(v - expected)
        val acceptable = if (error <= maxError) "Yes" else "No"
        if (acceptable == "No") allAcceptable = false
        println(f.format(k, v, error, acceptable))
    }  
    println("\nAcceptable overall: ${if (allAcceptable) "Yes" else "No"}")
}
 
fun main(args: Array<String>) {
    checkDist(::dice7, 1_400_000)
}