Seven-sided dice from five-sided dice: Difference between revisions
(→{{header|OCaml}}: Added PureBasic) |
|||
(120 intermediate revisions by 59 users not shown) | |||
Line 1: | Line 1: | ||
{{task|Probability and statistics}} |
{{task|Probability and statistics}} |
||
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]]. |
|||
;Task: |
|||
dice7 might call dice5 twice, re-call if four of the 25 |
|||
(Given an equal-probability generator of one of the integers 1 to 5 |
|||
as <code>dice5</code>), create <code>dice7</code> that generates a pseudo-random integer from |
|||
1 to 7 in equal probability using only <code>dice5</code> as a source of random |
|||
numbers, and check the distribution for at least one million calls using the function created in [[Verify distribution uniformity/Naive|Simple Random Distribution Checker]]. |
|||
'''Implementation suggestion:''' |
|||
<code>dice7</code> might call <code>dice5</code> twice, re-call if four of the 25 |
|||
combinations are given, otherwise split the other 21 combinations |
combinations are given, otherwise split the other 21 combinations |
||
into 7 groups of three, and return the group index from the rolls. |
into 7 groups of three, and return the group index from the rolls. |
||
<small>(Task adapted from an answer [http://stackoverflow.com/questions/90715/what-are-the-best-programming-puzzles-you-came-across here])</small> |
<small>(Task adapted from an answer [http://stackoverflow.com/questions/90715/what-are-the-best-programming-puzzles-you-came-across here])</small> |
||
<br><br> |
|||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">F dice5() |
|||
R random:(1..5) |
|||
F dice7() -> Int |
|||
V r = dice5() + dice5() * 5 - 6 |
|||
R I r < 21 {(r % 7) + 1} E dice7() |
|||
F distcheck(func, repeats, delta) |
|||
V bin = DefaultDict[Int, Int]() |
|||
L 1..repeats |
|||
bin[func()]++ |
|||
V target = repeats I/ bin.len |
|||
V deltacount = Int(delta / 100.0 * target) |
|||
assert(all(bin.values().map(count -> abs(@target - count) < @deltacount)), ‘Bin distribution skewed from #. +/- #.: #.’.format(target, deltacount, sorted(bin.items()).map((key, count) -> (key, @target - count)))) |
|||
print(bin) |
|||
distcheck(dice5, 1000000, 1) |
|||
distcheck(dice7, 1000000, 1)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
DefaultDict([1 = 199586, 2 = 200094, 3 = 198933, 4 = 200824, 5 = 200563]) |
|||
DefaultDict([1 = 142478, 2 = 142846, 3 = 143056, 4 = 142894, 5 = 143052, 6 = 143147, 7 = 142527]) |
|||
</pre> |
|||
=={{header|Ada}}== |
|||
The specification of a package Random_57: |
|||
<syntaxhighlight lang="ada">package Random_57 is |
|||
type Mod_7 is mod 7; |
|||
function Random7 return Mod_7; |
|||
-- a "fast" implementation, minimazing the calls to the Random5 generator |
|||
function Simple_Random7 return Mod_7; |
|||
-- a simple implementation |
|||
end Random_57;</syntaxhighlight> |
|||
Implementation of Random_57: |
|||
<syntaxhighlight lang="ada"> with Ada.Numerics.Discrete_Random; |
|||
package body Random_57 is |
|||
type M5 is mod 5; |
|||
package Rand_5 is new Ada.Numerics.Discrete_Random(M5); |
|||
Gen: Rand_5.Generator; |
|||
function Random7 return Mod_7 is |
|||
N: Natural; |
|||
begin |
|||
loop |
|||
N := Integer(Rand_5.Random(Gen))* 5 + Integer(Rand_5.Random(Gen)); |
|||
-- N is uniformly distributed in 0 .. 24 |
|||
if N < 21 then |
|||
return Mod_7(N/3); |
|||
else -- (N-21) is in 0 .. 3 |
|||
N := (N-21) * 5 + Integer(Rand_5.Random(Gen)); -- N is in 0 .. 19 |
|||
if N < 14 then |
|||
return Mod_7(N / 2); |
|||
else -- (N-14) is in 0 .. 5 |
|||
N := (N-14) * 5 + Integer(Rand_5.Random(Gen)); -- N is in 0 .. 29 |
|||
if N < 28 then |
|||
return Mod_7(N/4); |
|||
else -- (N-28) is in 0 .. 1 |
|||
N := (N-28) * 5 + Integer(Rand_5.Random(Gen)); -- 0 .. 9 |
|||
if N < 7 then |
|||
return Mod_7(N); |
|||
else -- (N-7) is in 0, 1, 2 |
|||
N := (N-7)* 5 + Integer(Rand_5.Random(Gen)); -- 0 .. 14 |
|||
if N < 14 then |
|||
return Mod_7(N/2); |
|||
else -- (N-14) is 0. This is not useful for us! |
|||
null; |
|||
end if; |
|||
end if; |
|||
end if; |
|||
end if; |
|||
end if; |
|||
end loop; |
|||
end Random7; |
|||
function Simple_Random7 return Mod_7 is |
|||
N: Natural := |
|||
Integer(Rand_5.Random(Gen))* 5 + Integer(Rand_5.Random(Gen)); |
|||
-- N is uniformly distributed in 0 .. 24 |
|||
begin |
|||
while N > 20 loop |
|||
N := Integer(Rand_5.Random(Gen))* 5 + Integer(Rand_5.Random(Gen)); |
|||
end loop; -- Now I <= 20 |
|||
return Mod_7(N / 3); |
|||
end Simple_Random7; |
|||
begin |
|||
Rand_5.Reset(Gen); |
|||
end Random_57;</syntaxhighlight> |
|||
A main program, using the Random_57 package: |
|||
<syntaxhighlight lang="ada">with Ada.Text_IO, Random_57; |
|||
procedure R57 is |
|||
use Random_57; |
|||
type Fun is access function return Mod_7; |
|||
function Rand return Mod_7 renames Random_57.Random7; |
|||
-- change this to "... renames Random_57.Simple_Random;" if you like |
|||
procedure Test(Sample_Size: Positive; Rand: Fun; Precision: Float := 0.3) is |
|||
Counter: array(Mod_7) of Natural := (others => 0); |
|||
Expected: Natural := Sample_Size/7; |
|||
Small: Mod_7 := Mod_7'First; |
|||
Large: Mod_7 := Mod_7'First; |
|||
Result: Mod_7; |
|||
begin |
|||
Ada.Text_IO.New_Line; |
|||
Ada.Text_IO.Put_Line("Sample Size: " & Integer'Image(Sample_Size)); |
|||
Ada.Text_IO.Put( " Bins:"); |
|||
for I in 1 .. Sample_Size loop |
|||
Result := Rand.all; |
|||
Counter(Result) := Counter(Result) + 1; |
|||
end loop; |
|||
for J in Mod_7 loop |
|||
Ada.Text_IO.Put(Integer'Image(Counter(J))); |
|||
if Counter(J) < Counter(Small) then Small := J; end if; |
|||
if Counter(J) > Counter(Large) then Large := J; end if; |
|||
end loop; |
|||
Ada.Text_IO.New_Line; |
|||
Ada.Text_IO.Put_Line(" Small Bin:" & Integer'Image(Counter(Small))); |
|||
Ada.Text_IO.Put_Line(" Large Bin: " & Integer'Image(Counter(Large))); |
|||
if Float(Counter(Small)*7) * (1.0+Precision) < Float(Sample_Size) then |
|||
Ada.Text_IO.Put_Line("Failed! Small too small!"); |
|||
elsif Float(Counter(Large)*7) * (1.0-Precision) > Float(Sample_Size) then |
|||
Ada.Text_IO.Put_Line("Failed! Large too large!"); |
|||
else |
|||
Ada.Text_IO.Put_Line("Passed"); |
|||
end if; |
|||
end Test; |
|||
begin |
|||
Test( 10_000, Rand'Access, 0.08); |
|||
Test( 100_000, Rand'Access, 0.04); |
|||
Test( 1_000_000, Rand'Access, 0.02); |
|||
Test(10_000_000, Rand'Access, 0.01); |
|||
end R57;</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Sample Size: 10000 |
|||
Bins: 1368 1404 1435 1491 1483 1440 1379 |
|||
Small Bin: 1368 |
|||
Large Bin: 1491 |
|||
Passed |
|||
Sample Size: 100000 |
|||
Bins: 14385 14110 14362 14404 14362 14206 14171 |
|||
Small Bin: 14110 |
|||
Large Bin: 14404 |
|||
Passed |
|||
Sample Size: 1000000 |
|||
Bins: 143765 142384 142958 142684 142799 142956 142454 |
|||
Small Bin: 142384 |
|||
Large Bin: 143765 |
|||
Passed |
|||
Sample Size: 10000000 |
|||
Bins: 1429266 1428214 1428753 1427032 1428418 1428699 1429618 |
|||
Small Bin: 1427032 |
|||
Large Bin: 1429618 |
|||
Passed</pre> |
|||
=={{header|ALGOL 68}}== |
|||
{{trans|C}} - note: This specimen retains the original [[Seven-sided dice from five-sided dice#C|C]] coding style. |
|||
{{works with|ALGOL 68|Revision 1 - no extensions to language used}} |
|||
{{works with|ALGOL 68G|Any - tested with release [http://sourceforge.net/projects/algol68/files/algol68g/algol68g-1.18.0/algol68g-1.18.0-9h.tiny.el5.centos.fc11.i386.rpm/download 1.18.0-9h.tiny]}} |
|||
{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release [http://sourceforge.net/projects/algol68/files/algol68toc/algol68toc-1.8.8d/algol68toc-1.8-8d.fc9.i386.rpm/download 1.8-8d]}} |
|||
C's version using no multiplications, divisions, or mod operators: |
|||
<syntaxhighlight lang="algol68">PROC dice5 = INT: |
|||
1 + ENTIER (5*random); |
|||
PROC mulby5 = (INT n)INT: |
|||
ABS (BIN n SHL 2) + n; |
|||
PROC dice7 = INT: ( |
|||
INT d55 := 0; |
|||
INT m := 1; |
|||
WHILE |
|||
m := ABS ((2r1 AND BIN m) SHL 2) + ABS (BIN m SHR 1); # repeats 4 - 2 - 1 # |
|||
d55 := mulby5(mulby5(d55)) + mulby5(dice5) + dice5 - 6; |
|||
# WHILE # d55 < m DO SKIP OD; |
|||
m := 1; |
|||
WHILE d55>0 DO |
|||
d55 +:= m; |
|||
m := ABS (BIN d55 AND 2r111); # modulas by 8 # |
|||
d55 := ABS (BIN d55 SHR 3) # divide by 8 # |
|||
OD; |
|||
m |
|||
); |
|||
PROC distcheck = (PROC INT dice, INT count, upb)VOID: ( |
|||
[upb]INT sum; FOR i TO UPB sum DO sum[i] := 0 OD; |
|||
FOR i TO count DO sum[dice]+:=1 OD; |
|||
FOR i TO UPB sum WHILE print(whole(sum[i],0)); i /= UPB sum DO print(", ") OD; |
|||
print(new line) |
|||
); |
|||
main: |
|||
( |
|||
distcheck(dice5, 1000000, 5); |
|||
distcheck(dice7, 1000000, 7) |
|||
)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
200598, 199852, 199939, 200602, 199009 |
|||
143529, 142688, 142816, 142747, 142958, 142802, 142460 |
|||
</pre> |
|||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
< |
<syntaxhighlight lang="autohotkey">dice5() |
||
{ Random, v, 1, 5 |
{ Random, v, 1, 5 |
||
Return, v |
Return, v |
||
Line 22: | Line 250: | ||
IfLess v, 21, Return, (v // 3) + 1 |
IfLess v, 21, Return, (v // 3) + 1 |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
<pre>Distribution check: |
<pre>Distribution check: |
||
Line 37: | Line 265: | ||
Bucket 7 contains 1444 elements.</pre> |
Bucket 7 contains 1444 elements.</pre> |
||
=={{header| |
=={{header|BBC BASIC}}== |
||
{{works with|BBC BASIC for Windows}} |
|||
A version using no multiplications, divisions, or mod operators. |
|||
<syntaxhighlight lang="bbcbasic"> MAXRND = 7 |
|||
<lang c>#include <stdio.h> |
|||
FOR r% = 2 TO 5 |
|||
#include <stdlib.h> |
|||
check% = FNdistcheck(FNdice7, 10^r%, 0.1) |
|||
#include <math.h> |
|||
PRINT "Over "; 10^r% " runs dice7 "; |
|||
IF check% THEN |
|||
void distcheck(int (*)(), int, double); |
|||
PRINT "failed distribution check with "; check% " bin(s) out of range" |
|||
ELSE |
|||
PRINT "passed distribution check" |
|||
ENDIF |
|||
NEXT |
|||
END |
|||
DEF FNdice7 |
|||
LOCAL x% : x% = FNdice5 + 5*FNdice5 |
|||
IF x%>26 THEN = FNdice7 ELSE = (x%+1) MOD 7 + 1 |
|||
DEF FNdice5 = RND(5) |
|||
DEF FNdistcheck(RETURN func%, repet%, delta) |
|||
LOCAL i%, m%, r%, s%, bins%() |
|||
DIM bins%(MAXRND) |
|||
FOR i% = 1 TO repet% |
|||
r% = FN(^func%) |
|||
bins%(r%) += 1 |
|||
IF r%>m% m% = r% |
|||
NEXT |
|||
FOR i% = 1 TO m% |
|||
IF bins%(i%)/(repet%/m%) > 1+delta s% += 1 |
|||
IF bins%(i%)/(repet%/m%) < 1-delta s% += 1 |
|||
NEXT |
|||
= s%</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Over 100 runs dice7 failed distribution check with 4 bin(s) out of range |
|||
Over 1000 runs dice7 failed distribution check with 2 bin(s) out of range |
|||
Over 10000 runs dice7 passed distribution check |
|||
Over 100000 runs dice7 passed distribution check |
|||
</pre> |
|||
=={{header|C}}== |
|||
int dice5() |
|||
<syntaxhighlight lang="c">int rand5() |
|||
{ |
{ |
||
int r, rand_max = RAND_MAX - (RAND_MAX % 5); |
|||
while ((r = rand()) >= rand_max); |
|||
return r / (rand_max / 5) + 1; |
|||
} |
} |
||
int mulby5(n) |
|||
int rand5_7() |
|||
{ |
{ |
||
int r; |
|||
return (n<<2) + n; |
|||
while ((r = rand5() * 5 + rand5()) >= 27); |
|||
return r / 3 - 1; |
|||
} |
} |
||
int |
int main() |
||
{ |
{ |
||
printf(check(rand5, 5, 1000000, .05) ? "flat\n" : "not flat\n"); |
|||
int d55 = 0; |
|||
printf(check(rand7, 7, 1000000, .05) ? "flat\n" : "not flat\n"); |
|||
int m = 1; |
|||
return 0; |
|||
do { |
|||
}</syntaxhighlight> |
|||
m = ((1&m)<<2) + (m>>1); // repeats 4 - 2 - 1 |
|||
{{out}} |
|||
d55 = mulby5(mulby5(d55)) + mulby5(dice5()) + dice5() - 6; |
|||
<pre> |
|||
} while (d55 < m); |
|||
flat |
|||
m = 1; |
|||
flat |
|||
while (d55>0) { |
|||
</pre> |
|||
d55 += m; |
|||
m = d55 & 7; |
|||
d55 >>= 3; |
|||
} |
|||
return m; |
|||
} |
|||
=={{header|C sharp}}== |
|||
int main() |
|||
{{trans|Java}} |
|||
<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
public class SevenSidedDice |
|||
{ |
{ |
||
Random random = new Random(); |
|||
distcheck(dice5, 1000000, 1); |
|||
distcheck(dice7, 1000000, 1); |
|||
static void Main(string[] args) |
|||
return 0; |
|||
{ |
|||
}</lang> |
|||
SevenSidedDice sevenDice = new SevenSidedDice(); |
|||
Console.WriteLine("Random number from 1 to 7: "+ sevenDice.seven()); |
|||
Console.Read(); |
|||
} |
|||
int seven() |
|||
{ |
|||
int v=21; |
|||
while(v>20) |
|||
v=five()+five()*5-6; |
|||
return 1+v%7; |
|||
} |
|||
int five() |
|||
{ |
|||
return 1 + random.Next(5); |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|C++}}== |
=={{header|C++}}== |
||
This solution tries to minimize calls to the underlying d5 by reusing information from earlier calls. |
This solution tries to minimize calls to the underlying d5 by reusing information from earlier calls. |
||
<syntaxhighlight lang="cpp">template<typename F> class fivetoseven |
|||
<lang cpp>template<typename F> class fivetoseven |
|||
{ |
{ |
||
public: |
public: |
||
Line 129: | Line 413: | ||
test_distribution(d5, 1000000, 0.001); |
test_distribution(d5, 1000000, 0.001); |
||
test_distribution(d7, 1000000, 0.001); |
test_distribution(d7, 1000000, 0.001); |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Clojure}}== |
|||
Uses the verify function defined in [[Verify distribution uniformity/Naive#Clojure]] |
|||
<syntaxhighlight lang="clojure">(def dice5 #(rand-int 5)) |
|||
(defn dice7 [] |
|||
(quot (->> dice5 ; do the following to dice5 |
|||
(repeatedly 2) ; call it twice |
|||
(apply #(+ %1 (* 5 %2))) ; d1 + 5*d2 => 0..24 |
|||
#() ; wrap that up in a function |
|||
repeatedly ; make infinite sequence of the above |
|||
(drop-while #(> % 20)) ; throw away anything > 20 |
|||
first) ; grab first acceptable element |
|||
3)) ; divide by three rounding down |
|||
(doseq [n [100 1000 10000] [num count okay?] (verify dice7 n)] |
|||
(println "Saw" num count "times:" |
|||
(if okay? "that's" " not") "acceptable"))</syntaxhighlight> |
|||
<pre>Saw 0 10 times: not acceptable |
|||
Saw 1 19 times: not acceptable |
|||
Saw 2 12 times: not acceptable |
|||
Saw 3 15 times: that's acceptable |
|||
Saw 4 11 times: not acceptable |
|||
Saw 5 11 times: not acceptable |
|||
Saw 6 22 times: not acceptable |
|||
Saw 0 142 times: that's acceptable |
|||
Saw 1 158 times: not acceptable |
|||
Saw 2 151 times: that's acceptable |
|||
Saw 3 153 times: that's acceptable |
|||
Saw 4 118 times: not acceptable |
|||
Saw 5 139 times: that's acceptable |
|||
Saw 6 139 times: that's acceptable |
|||
Saw 0 1498 times: that's acceptable |
|||
Saw 1 1411 times: that's acceptable |
|||
Saw 2 1436 times: that's acceptable |
|||
Saw 3 1434 times: that's acceptable |
|||
Saw 4 1414 times: that's acceptable |
|||
Saw 5 1408 times: that's acceptable |
|||
Saw 6 1399 times: that's acceptable</pre> |
|||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
{{trans|C}} |
{{trans|C}} |
||
< |
<syntaxhighlight lang="lisp">(defun d5 () |
||
(1+ (random 5))) |
(1+ (random 5))) |
||
Line 139: | Line 463: | ||
(loop for d55 = (+ (* 5 (d5)) (d5) -6) |
(loop for d55 = (+ (* 5 (d5)) (d5) -6) |
||
until (< d55 21) |
until (< d55 21) |
||
finally (return (1+ (mod d55 7)))))</ |
finally (return (1+ (mod d55 7)))))</syntaxhighlight> |
||
<pre>> (check-distribution 'd7 1000) |
<pre>> (check-distribution 'd7 1000) |
||
Line 155: | Line 479: | ||
=={{header|D}}== |
=={{header|D}}== |
||
{{trans|C++}} |
{{trans|C++}} |
||
<lang |
<syntaxhighlight lang="d">import std.random; |
||
import |
import verify_distribution_uniformity_naive: distCheck; |
||
/// |
/// Generates a random number in [1, 5]. |
||
int dice5() { |
int dice5() /*pure nothrow*/ @safe { |
||
return 1 |
return uniform(1, 6); |
||
} |
} |
||
/// Naive, generates a random number in [1, 7] using dice5. |
|||
int fiveToSevenNaive() /*pure nothrow*/ @safe { |
|||
/// Naive version, generates a random number in [1, 7] using dice5 |
|||
immutable int r = dice5() + dice5() * 5 - 6; |
|||
int dice7() { |
|||
return (r < 21) ? (r % 7) + 1 : fiveToSevenNaive(); |
|||
return (r < 21) ? (r % 7) + 1 : dice7(); |
|||
} |
} |
||
/** |
/** |
||
Generates a random number in [1, 7] using dice5, |
Generates a random number in [1, 7] using dice5, |
||
minimizing calls to |
minimizing calls to dice5. |
||
*/ |
*/ |
||
int fiveToSevenSmart() @safe { |
|||
struct FiveToSeven(alias d5) { |
|||
int |
static int rem = 0, max = 1; |
||
while (rem / 7 == max / 7) { |
|||
while (max < 7) { |
|||
int rand5 = d5() - 1; |
|||
max *= 5; |
|||
rem = 5 * rem + rand5; |
|||
} |
|||
while (rem / 7 == max / 7) { |
|||
while (max < 7) { |
|||
immutable int rand5 = dice5() - 1; |
|||
max *= 5; |
|||
rem = 5 * rem + rand5; |
|||
} |
} |
||
int |
immutable int groups = max / 7; |
||
rem |
if (rem >= 7 * groups) { |
||
rem -= 7 * groups; |
|||
max -= 7 * groups; |
|||
} |
|||
} |
} |
||
immutable int result = rem % 7; |
|||
private: |
|||
rem /= 7; |
|||
max /= 7; |
|||
return result + 1; |
|||
} |
} |
||
void main() /*@safe*/ { |
|||
enum int N = 400_000; |
|||
void main() { |
|||
const int N = 1_000_000; |
|||
distCheck(&dice5, N, 1); |
distCheck(&dice5, N, 1); |
||
distCheck(& |
distCheck(&fiveToSevenNaive, N, 1); |
||
distCheck(&fiveToSevenSmart, N, 1); |
|||
FiveToSeven!(dice5) dice7b; |
|||
}</syntaxhighlight> |
|||
distCheck(&dice7b.opCall, N, 1); |
|||
{{out}} |
|||
}</lang> |
|||
<pre>1 80365 |
|||
2 79941 |
|||
3 80065 |
|||
4 79784 |
|||
5 79845 |
|||
1 57186 |
|||
<pre> |
|||
2 57201 |
|||
Output: |
|||
3 57180 |
|||
[1:200416,2:199418,3:199471,4:200016,5:200679] |
|||
4 57231 |
|||
[1:142443,2:143605,3:142878,4:143038,5:142595,6:142205,7:143236] |
|||
5 57124 |
|||
[1:143354,2:143240,3:142882,4:142641,5:142715,6:142779,7:142389] |
|||
6 56832 |
|||
</pre> |
|||
7 57246 |
|||
1 57367 |
|||
=={{header|E}}== |
|||
2 56869 |
|||
3 57644 |
|||
4 57111 |
|||
5 57157 |
|||
6 56809 |
|||
7 57043</pre> |
|||
=={{header|E}}== |
|||
{{trans|Common Lisp}} |
{{trans|Common Lisp}} |
||
{{improve|E|Write dice7 in a prettier fashion and use the distribution checker once it's been written.}} |
{{improve|E|Write dice7 in a prettier fashion and use the distribution checker once it's been written.}} |
||
<syntaxhighlight lang="e">def dice5() { |
|||
<lang e>def dice5() { |
|||
return entropy.nextInt(5) + 1 |
return entropy.nextInt(5) + 1 |
||
} |
} |
||
Line 231: | Line 560: | ||
while ((d55 := 5 * dice5() + dice5() - 6) >= 21) {} |
while ((d55 := 5 * dice5() + dice5() - 6) >= 21) {} |
||
return d55 %% 7 + 1 |
return d55 %% 7 + 1 |
||
}</ |
}</syntaxhighlight> |
||
<syntaxhighlight lang="e">def bins := ([0] * 7).diverge() |
|||
<lang e>def bins := ([0] * 7).diverge() |
|||
for x in 1..1000 { |
for x in 1..1000 { |
||
bins[dice7() - 1] += 1 |
bins[dice7() - 1] += 1 |
||
} |
} |
||
println(bins.snapshot())</ |
println(bins.snapshot())</syntaxhighlight> |
||
=={{header|EasyLang}}== |
|||
<syntaxhighlight> |
|||
func dice5 . |
|||
return randint 5 |
|||
. |
|||
func dice25 . |
|||
return (dice5 - 1) * 5 + dice5 |
|||
. |
|||
func dice7a . |
|||
return dice25 mod1 7 |
|||
. |
|||
func dice7b . |
|||
repeat |
|||
h = dice25 |
|||
until h <= 21 |
|||
. |
|||
return h mod1 7 |
|||
. |
|||
numfmt 3 0 |
|||
n = 1000000 |
|||
len dist[] 7 |
|||
# |
|||
proc checkdist . . |
|||
for i to len dist[] |
|||
h = dist[i] / n * 7 |
|||
if abs (h - 1) > 0.01 |
|||
bad = 1 |
|||
. |
|||
dist[i] = 0 |
|||
print h |
|||
. |
|||
if bad = 1 |
|||
print "-> not uniform" |
|||
else |
|||
print "-> uniform" |
|||
. |
|||
. |
|||
# |
|||
for i to n |
|||
dist[dice7a] += 1 |
|||
. |
|||
checkdist |
|||
# |
|||
print "" |
|||
for i to n |
|||
dist[dice7b] += 1 |
|||
. |
|||
checkdist |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
1.122 |
|||
1.118 |
|||
1.121 |
|||
1.117 |
|||
0.840 |
|||
0.842 |
|||
0.840 |
|||
-> not uniform |
|||
0.996 |
|||
1.003 |
|||
1.001 |
|||
0.997 |
|||
1.004 |
|||
0.998 |
|||
1.001 |
|||
-> uniform |
|||
</pre> |
|||
=={{header|Elixir}}== |
|||
<syntaxhighlight lang="elixir">defmodule Dice do |
|||
def dice5, do: :rand.uniform( 5 ) |
|||
def dice7 do |
|||
dice7_from_dice5 |
|||
end |
|||
defp dice7_from_dice5 do |
|||
d55 = 5*dice5 + dice5 - 6 # 0..24 |
|||
if d55 < 21, do: rem( d55, 7 ) + 1, |
|||
else: dice7_from_dice5 |
|||
end |
|||
end |
|||
fun5 = fn -> Dice.dice5 end |
|||
IO.inspect VerifyDistribution.naive( fun5, 1000000, 3 ) |
|||
fun7 = fn -> Dice.dice7 end |
|||
IO.inspect VerifyDistribution.naive( fun7, 1000000, 3 )</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
:ok |
|||
:ok |
|||
</pre> |
|||
=={{header|Erlang}}== |
|||
<syntaxhighlight lang="erlang"> |
|||
-module( dice ). |
|||
-export( [dice5/0, dice7/0, task/0] ). |
|||
dice5() -> random:uniform( 5 ). |
|||
dice7() -> |
|||
dice7_small_enough( dice5() * 5 + dice5() - 6 ). % 0 - 24 |
|||
task() -> |
|||
verify_distribution_uniformity:naive( fun dice7/0, 1000000, 1 ). |
|||
dice7_small_enough( N ) when N < 21 -> N div 3 + 1; |
|||
dice7_small_enough( _N ) -> dice7(). |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
76> dice:task(). |
|||
ok |
|||
</pre> |
|||
=={{header|Factor}}== |
|||
<syntaxhighlight lang="factor">USING: kernel random sequences assocs locals sorting prettyprint |
|||
math math.functions math.statistics math.vectors math.ranges ; |
|||
IN: rosetta-code.dice7 |
|||
! Output a random integer 1..5. |
|||
: dice5 ( -- x ) |
|||
5 [1,b] random |
|||
; |
|||
! Output a random integer 1..7 using dice5 as randomness source. |
|||
: dice7 ( -- x ) |
|||
0 [ dup 21 < ] [ drop dice5 5 * dice5 + 6 - ] do until |
|||
7 rem 1 + |
|||
; |
|||
! Roll the die by calling the quotation the given number of times and return |
|||
! an array with roll results. |
|||
! Sample call: 1000 [ dice7 ] roll |
|||
: roll ( times quot: ( -- x ) -- array ) |
|||
[ call( -- x ) ] curry replicate |
|||
; |
|||
! Input array contains outcomes of a number of die throws. Each die result is |
|||
! an integer in the range 1..X. Calculate and return the number of each |
|||
! of the results in the array so that in the first position of the result |
|||
! there is the number of ones in the input array, in the second position |
|||
! of the result there is the number of twos in the input array, etc. |
|||
: count-dice-outcomes ( X array -- array ) |
|||
histogram |
|||
swap [1,b] [ over [ 0 or ] change-at ] each |
|||
sort-keys values |
|||
; |
|||
! Verify distribution uniformity/Naive. Delta is the acceptable deviation |
|||
! from the ideal number of items in each bucket, expressed as a fraction of |
|||
! the total count. Sides is the number of die sides. Die-func is a word that |
|||
! produces a random number on stack in the range [1..sides], times is the |
|||
! number of times to call it. |
|||
! Sample call: 0.02 7 [ dice7 ] 100000 verify |
|||
:: verify ( delta sides die-func: ( -- random ) times -- ) |
|||
sides |
|||
times die-func roll |
|||
count-dice-outcomes |
|||
dup . |
|||
times sides / :> ideal-count |
|||
ideal-count v-n vabs |
|||
times v/n |
|||
delta [ < ] curry all? |
|||
[ "Random enough" . ] [ "Not random enough" . ] if |
|||
; |
|||
! Call verify with 1, 10, 100, ... 1000000 rolls of 7-sided die. |
|||
: verify-all ( -- ) |
|||
{ 1 10 100 1000 10000 100000 1000000 } |
|||
[| times | 0.02 7 [ dice7 ] times verify ] each |
|||
;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>USE: rosetta-code.dice7 verify-all |
|||
{ 0 0 0 1 0 0 0 } |
|||
"Not random enough" |
|||
{ 0 2 3 1 1 1 2 } |
|||
"Not random enough" |
|||
{ 17 12 15 11 13 13 19 } |
|||
"Not random enough" |
|||
{ 140 130 141 148 143 155 143 } |
|||
"Random enough" |
|||
{ 1457 1373 1427 1433 1443 1382 1485 } |
|||
"Random enough" |
|||
{ 14225 14320 14216 14326 14415 14084 14414 } |
|||
"Random enough" |
|||
{ 142599 141910 142524 143029 143353 142696 143889 } |
|||
"Random enough"</pre> |
|||
=={{header|Forth}}== |
|||
{{works with|GNU Forth}} |
|||
<syntaxhighlight lang="forth">require random.fs |
|||
: d5 5 random 1+ ; |
|||
: discard? 5 = swap 1 > and ; |
|||
: d7 |
|||
begin d5 d5 2dup discard? while 2drop repeat |
|||
1- 5 * + 1- 7 mod 1+ ;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>cr ' d7 1000000 7 1 check-distribution . |
|||
lower bound = 141429 upper bound = 144285 |
|||
1 : 143241 ok |
|||
2 : 142397 ok |
|||
3 : 143522 ok |
|||
4 : 142909 ok |
|||
5 : 142001 ok |
|||
6 : 142844 ok |
|||
7 : 143086 ok |
|||
-1</pre> |
|||
=={{header|Fortran}}== |
|||
{{works with|Fortran|95 and later}} |
|||
<syntaxhighlight lang="fortran">module rand_mod |
|||
implicit none |
|||
contains |
|||
function rand5() |
|||
integer :: rand5 |
|||
real :: r |
|||
call random_number(r) |
|||
rand5 = 5*r + 1 |
|||
end function |
|||
function rand7() |
|||
integer :: rand7 |
|||
do |
|||
rand7 = 5*rand5() + rand5() - 6 |
|||
if (rand7 < 21) then |
|||
rand7 = rand7 / 3 + 1 |
|||
return |
|||
end if |
|||
end do |
|||
end function |
|||
end module |
|||
program Randtest |
|||
use rand_mod |
|||
implicit none |
|||
integer, parameter :: samples = 1000000 |
|||
call distcheck(rand7, samples, 0.005) |
|||
write(*,*) |
|||
call distcheck(rand7, samples, 0.001) |
|||
end program</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Distribution Uniform |
|||
Distribution potentially skewed for bucket 1 Expected: 142857 Actual: 143142 |
|||
Distribution potentially skewed for bucket 2 Expected: 142857 Actual: 143454 |
|||
Distribution potentially skewed for bucket 3 Expected: 142857 Actual: 143540 |
|||
Distribution potentially skewed for bucket 4 Expected: 142857 Actual: 142677 |
|||
Distribution potentially skewed for bucket 5 Expected: 142857 Actual: 142511 |
|||
Distribution potentially skewed for bucket 6 Expected: 142857 Actual: 142163 |
|||
Distribution potentially skewed for bucket 7 Expected: 142857 Actual: 142513</pre> |
|||
=={{header|FreeBASIC}}== |
|||
{{trans|Liberty BASIC}} |
|||
<syntaxhighlight lang="freebasic"> |
|||
Function dice5() As Integer |
|||
Return Int(Rnd * 5) + 1 |
|||
End Function |
|||
Function dice7() As Integer |
|||
Dim As Integer temp |
|||
Do |
|||
temp = dice5() * 5 + dice5() -6 |
|||
Loop Until temp < 21 |
|||
Return (temp Mod 7) +1 |
|||
End Function |
|||
Dim Shared As Ulongint n = 1000000 |
|||
Print "Testing "; n; " times" |
|||
If Not(distCheck(n, 0.05)) Then Print "Test failed" Else Print "Test passed" |
|||
Sleep |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Igual que la entrada de Liberty BASIC. |
|||
</pre> |
|||
=={{header|Go}}== |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
"fmt" |
|||
"math" |
|||
"math/rand" |
|||
"time" |
|||
) |
|||
// "given" |
|||
func dice5() int { |
|||
return rand.Intn(5) + 1 |
|||
} |
|||
// function specified by task "Seven-sided dice from five-sided dice" |
|||
func dice7() (i int) { |
|||
for { |
|||
i = 5*dice5() + dice5() |
|||
if i < 27 { |
|||
break |
|||
} |
|||
} |
|||
return (i / 3) - 1 |
|||
} |
|||
// function specified by task "Verify distribution uniformity/Naive" |
|||
// |
|||
// Parameter "f" is expected to return a random integer in the range 1..n. |
|||
// (Values out of range will cause an unceremonious crash.) |
|||
// "Max" is returned as an "indication of distribution achieved." |
|||
// It is the maximum delta observed from the count representing a perfectly |
|||
// uniform distribution. |
|||
// Also returned is a boolean, true if "max" is less than threshold |
|||
// parameter "delta." |
|||
func distCheck(f func() int, n int, |
|||
repeats int, delta float64) (max float64, flatEnough bool) { |
|||
count := make([]int, n) |
|||
for i := 0; i < repeats; i++ { |
|||
count[f()-1]++ |
|||
} |
|||
expected := float64(repeats) / float64(n) |
|||
for _, c := range count { |
|||
max = math.Max(max, math.Abs(float64(c)-expected)) |
|||
} |
|||
return max, max < delta |
|||
} |
|||
// Driver, produces output satisfying both tasks. |
|||
func main() { |
|||
rand.Seed(time.Now().UnixNano()) |
|||
const calls = 1000000 |
|||
max, flatEnough := distCheck(dice7, 7, calls, 500) |
|||
fmt.Println("Max delta:", max, "Flat enough:", flatEnough) |
|||
max, flatEnough = distCheck(dice7, 7, calls, 500) |
|||
fmt.Println("Max delta:", max, "Flat enough:", flatEnough) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Max delta: 356.1428571428696 Flat enough: true |
|||
Max delta: 787.8571428571304 Flat enough: false |
|||
</pre> |
|||
=={{header|Groovy}}== |
|||
<syntaxhighlight lang="groovy">random = new Random() |
|||
int rand5() { |
|||
random.nextInt(5) + 1 |
|||
} |
|||
int rand7From5() { |
|||
def raw = 25 |
|||
while (raw > 21) { |
|||
raw = 5*(rand5() - 1) + rand5() |
|||
} |
|||
(raw % 7) + 1 |
|||
}</syntaxhighlight> |
|||
Test: |
|||
<syntaxhighlight lang="groovy">def test = { |
|||
(1..6). each { |
|||
def counts = [0g, 0g, 0g, 0g, 0g, 0g, 0g] |
|||
def target = 10g**it |
|||
def popSize = 7*target |
|||
(0..<(popSize)).each { |
|||
def i = rand7From5() - 1 |
|||
counts[i] = counts[i] + 1g |
|||
} |
|||
BigDecimal stdDev = (counts.collect { it - target}.collect { it * it }.sum() / popSize) ** 0.5g |
|||
def countMap = (0..<counts.size()).inject([:]) { map, index -> map + [(index+1):counts[index]] } |
|||
println """\ |
|||
counts: ${countMap} |
|||
population size: ${popSize} |
|||
std dev: ${stdDev.round(new java.math.MathContext(3))} |
|||
""" |
|||
} |
|||
} |
|||
4.times { |
|||
println """ |
|||
TRIAL #${it+1} |
|||
==============""" |
|||
test(it) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll;">TRIAL #1 |
|||
============== |
|||
counts: [1:16, 2:10, 3:9, 4:7, 5:12, 6:8, 7:8] |
|||
population size: 70 |
|||
std dev: 0.910 |
|||
counts: [1:85, 2:97, 3:108, 4:110, 5:95, 6:105, 7:100] |
|||
population size: 700 |
|||
std dev: 0.800 |
|||
counts: [1:990, 2:1008, 3:992, 4:1060, 5:1008, 6:997, 7:945] |
|||
population size: 7000 |
|||
std dev: 0.995 |
|||
counts: [1:9976, 2:10007, 3:10009, 4:9858, 5:10109, 6:9988, 7:10053] |
|||
population size: 70000 |
|||
std dev: 0.714 |
|||
counts: [1:100310, 2:99783, 3:99843, 4:100353, 5:99804, 6:99553, 7:100354] |
|||
population size: 700000 |
|||
std dev: 0.968 |
|||
counts: [1:999320, 2:1000942, 3:1000201, 4:1000878, 5:999181, 6:999632, 7:999846] |
|||
population size: 7000000 |
|||
std dev: 0.654 |
|||
TRIAL #2 |
|||
============== |
|||
counts: [1:10, 2:8, 3:9, 4:9, 5:14, 6:7, 7:13] |
|||
population size: 70 |
|||
std dev: 0.756 |
|||
counts: [1:104, 2:101, 3:97, 4:108, 5:100, 6:87, 7:103] |
|||
population size: 700 |
|||
std dev: 0.619 |
|||
counts: [1:995, 2:970, 3:1001, 4:953, 5:1006, 6:1081, 7:994] |
|||
population size: 7000 |
|||
std dev: 1.18 |
|||
counts: [1:10013, 2:10063, 3:9843, 4:9984, 5:9986, 6:10059, 7:10052] |
|||
population size: 70000 |
|||
std dev: 0.711 |
|||
counts: [1:100048, 2:99647, 3:100240, 4:100683, 5:99813, 6:100320, 7:99249] |
|||
population size: 700000 |
|||
std dev: 1.39 |
|||
counts: [1:1000579, 2:1000541, 3:999497, 4:1000805, 5:999708, 6:999161, 7:999709] |
|||
population size: 7000000 |
|||
std dev: 0.586 |
|||
TRIAL #3 |
|||
============== |
|||
counts: [1:9, 2:8, 3:11, 4:14, 5:10, 6:11, 7:7] |
|||
population size: 70 |
|||
std dev: 0.676 |
|||
counts: [1:100, 2:92, 3:105, 4:107, 5:111, 6:91, 7:94] |
|||
population size: 700 |
|||
std dev: 0.733 |
|||
counts: [1:1010, 2:1053, 3:967, 4:981, 5:1027, 6:959, 7:1003] |
|||
population size: 7000 |
|||
std dev: 0.984 |
|||
counts: [1:9857, 2:10037, 3:9992, 4:10231, 5:9828, 6:10140, 7:9915] |
|||
population size: 70000 |
|||
std dev: 1.37 |
|||
counts: [1:99650, 2:99580, 3:99848, 4:100507, 5:99916, 6:100212, 7:100287] |
|||
population size: 700000 |
|||
std dev: 1.01 |
|||
counts: [1:1001710, 2:999667, 3:1000685, 4:1000411, 5:999369, 6:998469, 7:999689] |
|||
population size: 7000000 |
|||
std dev: 0.965 |
|||
TRIAL #4 |
|||
============== |
|||
counts: [1:12, 2:7, 3:11, 4:12, 5:7, 6:9, 7:12] |
|||
population size: 70 |
|||
std dev: 0.676 |
|||
counts: [1:97, 2:96, 3:101, 4:93, 5:96, 6:124, 7:93] |
|||
population size: 700 |
|||
std dev: 1.01 |
|||
counts: [1:985, 2:1023, 3:1018, 4:1023, 5:995, 6:973, 7:983] |
|||
population size: 7000 |
|||
std dev: 0.615 |
|||
counts: [1:9948, 2:9968, 3:10131, 4:10050, 5:9990, 6:10039, 7:9874] |
|||
population size: 70000 |
|||
std dev: 0.764 |
|||
counts: [1:100125, 2:99616, 3:99912, 4:100286, 5:99674, 6:100190, 7:100197] |
|||
population size: 700000 |
|||
std dev: 0.787 |
|||
counts: [1:1001267, 2:999911, 3:1000602, 4:999483, 5:1000549, 6:998725, 7:999463] |
|||
population size: 7000000 |
|||
std dev: 0.798</pre> |
|||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import System.Random |
||
import Data.List |
import Data.List |
||
Line 248: | Line 1,084: | ||
let d7 = 5*d51+d52-6 |
let d7 = 5*d51+d52-6 |
||
if d7 > 20 then sevenFrom5Dice |
if d7 > 20 then sevenFrom5Dice |
||
else return $ 1 + d7 `mod` 7</ |
else return $ 1 + d7 `mod` 7</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
< |
<syntaxhighlight lang="haskell">*Main> replicateM 10 sevenFrom5Dice |
||
[2,3,1,1,6,2,5,6,5,3]</ |
[2,3,1,1,6,2,5,6,5,3]</syntaxhighlight> |
||
Test: |
Test: |
||
< |
<syntaxhighlight lang="haskell">*Main> mapM_ print .sort =<< distribCheck sevenFrom5Dice 1000000 3 |
||
(1,(142759,True)) |
(1,(142759,True)) |
||
(2,(143078,True)) |
(2,(143078,True)) |
||
Line 260: | Line 1,096: | ||
(5,(142896,True)) |
(5,(142896,True)) |
||
(6,(143028,True)) |
(6,(143028,True)) |
||
(7,(143130,True))</ |
(7,(143130,True))</syntaxhighlight> |
||
=={{header|Icon}} and {{header|Unicon}}== |
|||
{{trans|Ruby}} |
|||
Uses <code>verify_uniform</code> from [[Simple_Random_Distribution_Checker#Icon_and_Unicon|here]]. |
|||
<syntaxhighlight lang="icon"> |
|||
$include "distribution-checker.icn" |
|||
# return a uniformly distributed number from 1 to 7, |
|||
# but only using a random number in range 1 to 5. |
|||
procedure die_7 () |
|||
while rnd := 5*?5 + ?5 - 6 do { |
|||
if rnd < 21 then suspend rnd % 7 + 1 |
|||
} |
|||
end |
|||
procedure main () |
|||
if verify_uniform (create (|die_7()), 1000000, 0.01) |
|||
then write ("uniform") |
|||
else write ("skewed") |
|||
end |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
5 142870 |
|||
2 142812 |
|||
7 142901 |
|||
4 142960 |
|||
1 143113 |
|||
6 142706 |
|||
3 142638 |
|||
uniform |
|||
</pre> |
|||
=={{header|J}}== |
=={{header|J}}== |
||
The first step is to create 7-sided dice rolls from 5-sided dice rolls (<code>rollD5</code>): |
The first step is to create 7-sided dice rolls from 5-sided dice rolls (<code>rollD5</code>): |
||
< |
<syntaxhighlight lang="j">rollD5=: [: >: ] ?@$ 5: NB. makes a y shape array of 5s, "rolls" the array and increments. |
||
roll2xD5=: [: rollD5 2 ,~ */ NB. rolls D5 twice for each desired D7 roll (y rows, 2 cols) |
roll2xD5=: [: rollD5 2 ,~ */ NB. rolls D5 twice for each desired D7 roll (y rows, 2 cols) |
||
toBase10=: 5 #. <: NB. decrements and converts rows from base 5 to 10 |
toBase10=: 5 #. <: NB. decrements and converts rows from base 5 to 10 |
||
Line 270: | Line 1,139: | ||
groupin3s=: [: >. >: % 3: NB. increments, divides by 3 and takes ceiling |
groupin3s=: [: >. >: % 3: NB. increments, divides by 3 and takes ceiling |
||
getD7=: groupin3s@keepGood@toBase10@roll2xD5</ |
getD7=: groupin3s@keepGood@toBase10@roll2xD5</syntaxhighlight> |
||
Here are a couple of variations on the theme that achieve the same result: |
Here are a couple of variations on the theme that achieve the same result: |
||
< |
<syntaxhighlight lang="j">getD7b=: 0 8 -.~ 3 >.@%~ 5 #. [: <:@rollD5 2 ,~ ] |
||
getD7c=: [: (#~ 7&>:) 3 >.@%~ [: 5&#.&.:<:@rollD5 ] , 2:</ |
getD7c=: [: (#~ 7&>:) 3 >.@%~ [: 5&#.&.:<:@rollD5 ] , 2:</syntaxhighlight> |
||
The trouble is that we probably don't have enough D7 rolls yet because we compressed out any double D5 rolls that evaluated to 21 or more. So we need to accumulate some more D7 rolls until we have enough. J has two types of verb definition - tacit (arguments not referenced) and explicit (more conventional function definitions) illustrated below: |
The trouble is that we probably don't have enough D7 rolls yet because we compressed out any double D5 rolls that evaluated to 21 or more. So we need to accumulate some more D7 rolls until we have enough. J has two types of verb definition - tacit (arguments not referenced) and explicit (more conventional function definitions) illustrated below: |
||
Here's an explicit definition that accumulates rolls from <code>getD7</code>: |
Here's an explicit definition that accumulates rolls from <code>getD7</code>: |
||
< |
<syntaxhighlight lang="j">rollD7x=: monad define |
||
n=. */y NB. product of vector y is total number of D7 rolls required |
n=. */y NB. product of vector y is total number of D7 rolls required |
||
rolls=. '' NB. initialize empty noun rolls |
rolls=. '' NB. initialize empty noun rolls |
||
while. n > # |
while. n > #rolls do. NB. checks if if enough D7 rolls accumulated |
||
rolls=. rolls, getD7 >. 0.75 * n NB. calcs 3/4 of required rolls and accumulates getD7 rolls |
rolls=. rolls, getD7 >. 0.75 * n NB. calcs 3/4 of required rolls and accumulates getD7 rolls |
||
end. |
end. |
||
y $ rolls NB. shape the result according to the vector y |
y $ rolls NB. shape the result according to the vector y |
||
)</ |
)</syntaxhighlight> |
||
Here's a tacit definition that does the same thing: |
Here's a tacit definition that does the same thing: |
||
< |
<syntaxhighlight lang="j">getNumRolls=: [: >. 0.75 * */@[ NB. calc approx 3/4 of the required rolls |
||
accumD7Rolls=: ] , getD7@getNumRolls NB. accumulates getD7 rolls |
accumD7Rolls=: ] , getD7@getNumRolls NB. accumulates getD7 rolls |
||
isNotEnough=: */@[ > #@] NB. checks if enough D7 rolls accumulated |
isNotEnough=: */@[ > #@] NB. checks if enough D7 rolls accumulated |
||
rollD7t=: ] $ (accumD7Rolls ^: isNotEnough ^:_)&''</ |
rollD7t=: ] $ (accumD7Rolls ^: isNotEnough ^:_)&''</syntaxhighlight> |
||
The <code>verb1 ^: verb2 ^:_</code> construct repeats <code>x verb1 y</code> while <code>x verb2 y</code> is true. It is like saying "Repeat accumD7Rolls while isNotEnough". |
The <code>verb1 ^: verb2 ^:_</code> construct repeats <code>x verb1 y</code> while <code>x verb2 y</code> is true. It is like saying "Repeat accumD7Rolls while isNotEnough". |
||
Example usage: |
Example usage: |
||
< |
<syntaxhighlight lang="j"> rollD7t 10 NB. 10 rolls of D7 |
||
6 4 5 1 4 2 4 5 2 5 |
6 4 5 1 4 2 4 5 2 5 |
||
rollD7t 2 5 NB. 2 by 5 array of D7 rolls |
rollD7t 2 5 NB. 2 by 5 array of D7 rolls |
||
Line 315: | Line 1,181: | ||
1 |
1 |
||
($@rollD7x -: $@rollD7t) 2 3 5 |
($@rollD7x -: $@rollD7t) 2 3 5 |
||
1</ |
1</syntaxhighlight> |
||
=={{header|Java}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="java">import java.util.Random; |
|||
public class SevenSidedDice |
|||
{ |
|||
private static final Random rnd = new Random(); |
|||
public static void main(String[] args) |
|||
{ |
|||
SevenSidedDice now=new SevenSidedDice(); |
|||
System.out.println("Random number from 1 to 7: "+now.seven()); |
|||
} |
|||
int seven() |
|||
{ |
|||
int v=21; |
|||
while(v>20) |
|||
v=five()+five()*5-6; |
|||
return 1+v%7; |
|||
} |
|||
int five() |
|||
{ |
|||
return 1+rnd.nextInt(5); |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
{{trans|Ruby}} |
{{trans|Ruby}} |
||
<lang |
<syntaxhighlight lang="javascript">function dice5() |
||
{ |
|||
return 1 + Math.floor(5 * Math.random()) |
|||
return 1 + Math.floor(5 * Math.random()); |
|||
} |
} |
||
function dice7() |
function dice7() |
||
{ |
|||
while (true) { |
|||
while (true) |
|||
var dice55 = 5 * dice5() + dice5() - 6 |
|||
{ |
|||
if (dice55 < 21) |
|||
var dice55 = 5 * dice5() + dice5() - 6; |
|||
return dice55 % 7 + 1; |
|||
if (dice55 < 21) |
|||
return dice55 % 7 + 1; |
|||
} |
|||
} |
} |
||
distcheck(dice5, 1000000); |
distcheck(dice5, 1000000); |
||
print(); |
print(); |
||
distcheck(dice7, 1000000);</ |
distcheck(dice7, 1000000);</syntaxhighlight> |
||
{{out}} |
|||
output |
|||
<pre>1 199792 |
<pre>1 199792 |
||
2 200425 |
2 200425 |
||
Line 349: | Line 1,242: | ||
7 142619 </pre> |
7 142619 </pre> |
||
=={{header| |
=={{header|jq}}== |
||
{{works with|jq}} |
|||
'''Also works with gojq, the Go implementation of jq.''' |
|||
In this entry, the results for both a low-entropy and a |
|||
<lang lua>dice5 = function() return math.random(5) end |
|||
high-entropy 5-sided die are shown. The former uses the computer's |
|||
clock as a not-very-good PRNG, and the latter uses /dev/random in accordance |
|||
with the following invocation: |
|||
<syntaxhighlight lang=sh> |
|||
#!/bin/bash |
|||
< /dev/random tr -cd '0-9' | fold -w 1 | jq -Mcnr -f dice.jq |
|||
</syntaxhighlight> |
|||
The results employ a two-tailed χ2-test at the 95% confidence level |
|||
according to which we are entitled to reject the null hypothesis of |
|||
uniform randomness if the χ2 statistic is less than 1.69 or greater |
|||
than 16.013, assuming the number of trials is large enough. |
|||
See https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm |
|||
'''dice.jq''' |
|||
<syntaxhighlight lang=jq> |
|||
# Output: a PRN in range(0;$n) where $n is . |
|||
def prn: |
|||
if . == 1 then 0 |
|||
else . as $n |
|||
| (($n-1)|tostring|length) as $w |
|||
| [limit($w; inputs)] | join("") | tonumber |
|||
| if . < $n then . else ($n | prn) end |
|||
end; |
|||
# Emit a stream of [value, frequency] pairs |
|||
def histogram(stream): |
|||
reduce stream as $s ({}; |
|||
($s|type) as $t |
|||
| (if $t == "string" then $s else ($s|tojson) end) as $y |
|||
| .[$t][$y][0] = $s |
|||
| .[$t][$y][1] += 1 ) |
|||
| .[][] ; |
|||
# sum of squares |
|||
def ss(s): reduce s as $x (0; . + ($x * $x)); |
|||
def chiSquared($expected): |
|||
debug # show the actual frequencies |
|||
| ss( .[] - $expected ) / $expected; |
|||
# The high-entropy 5-sided die |
|||
def dice5: 1 + (5|prn); |
|||
# The low-entropy 5-sided die |
|||
def pseudo_dice5: |
|||
def r: (now * 100000 | floor) % 10; |
|||
null | until(. and (. < 5); r) | 1 + . ; |
|||
# The formal argument dice5 should behave like a 5-sided dice: |
|||
def dice7(dice5): |
|||
1 + ([limit(7; repeat(dice5))]|add % 7) ; |
|||
# Issue a report on the results of a sequence of $n trials using the specified dice |
|||
def report(dice; $n): |
|||
1.69 as $lower |
|||
| 16.013 as $upper |
|||
| [histogram( limit($n; repeat(dice)) ) | last] |
|||
| chiSquared($n/7) as $x2 |
|||
| "The χ2 statistic for a trial of \($n) virtual tosses is \($x2).", |
|||
"Using a two-sided χ2-test with seven degrees of freedom (\($lower), \($upper)), it is reasonable to conclude that", |
|||
(if $x2 < $lower then "this is lower than would be expected for a fair die." |
|||
elif $x2 > $upper then "this is higher than would be expected for a fair die." |
|||
else "this is consistent with the die being fair." |
|||
end) ; |
|||
def report($n): |
|||
"Low-entropy die results:", |
|||
report(dice7(pseudo_dice5); $n), |
|||
"", |
|||
"High-entropy die results:", |
|||
report(dice7(dice5); $n) ; |
|||
report(70) |
|||
</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
Low-entropy die results: |
|||
["DEBUG:",[19,14,6,18,7,5,1]] |
|||
The χ2 statistic for a trial of 70 virtual tosses is 29.2. |
|||
Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that |
|||
this is higher than would be expected for a fair die. |
|||
High-entropy die results: |
|||
["DEBUG:",[9,11,9,10,15,11,5]] |
|||
The χ2 statistic for a trial of 70 virtual tosses is 5.4. |
|||
Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that |
|||
this is consistent with the die being fair. |
|||
</pre> |
|||
'''Results for 1,000,000 trials:''' |
|||
<pre> |
|||
Low-entropy die results: |
|||
["DEBUG:",[41440,57949,15946,44821,117168,339337,383339]] |
|||
The χ2 statistic for a trial of 1000000 virtual tosses is 982157.0011039999. |
|||
Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that |
|||
this is higher than would be expected for a fair die. |
|||
High-entropy die results: |
|||
["DEBUG:",[142860,143087,142213,142065,143359,143494,142922]] |
|||
The χ2 statistic for a trial of 1000000 virtual tosses is 12.298347999999999. |
|||
Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that |
|||
this is consistent with the die being fair. |
|||
</pre> |
|||
=={{header|Julia}}== |
|||
<syntaxhighlight lang="julia"> |
|||
using Random: seed! |
|||
seed!(1234) # for reproducibility |
|||
dice5() = rand(1:5) |
|||
function dice7() |
|||
while true |
|||
a = dice5() |
|||
b = dice5() |
|||
c = a + 5(b - 1) |
|||
if c <= 21 |
|||
return mod1(c, 7) |
|||
end |
|||
end |
|||
end |
|||
rolls = (dice7() for i in 1:100000) |
|||
roll_counts = Dict{Int,Int}() |
|||
for roll in rolls |
|||
roll_counts[roll] = get(roll_counts, roll, 0) + 1 |
|||
end |
|||
foreach(println, sort(roll_counts)) |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
1 => 14530 |
|||
2 => 13872 |
|||
3 => 14422 |
|||
4 => 14425 |
|||
5 => 14323 |
|||
6 => 14315 |
|||
7 => 14113 |
|||
</pre> |
|||
=={{header|Kotlin}}== |
|||
<syntaxhighlight lang="scala">// 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) |
|||
}</syntaxhighlight> |
|||
Sample output: |
|||
<pre> |
|||
Repetitions = 1400000, Expected = 200000 |
|||
Tolerance = 0.5%, Max Error = 1000 |
|||
Integer Occurrences Error Acceptable |
|||
1 199285 715 Yes |
|||
2 200247 247 Yes |
|||
3 199709 291 Yes |
|||
4 199983 17 Yes |
|||
5 199990 10 Yes |
|||
6 200664 664 Yes |
|||
7 200122 122 Yes |
|||
Acceptable overall: Yes |
|||
</pre> |
|||
=={{header|Liberty BASIC}}== |
|||
<syntaxhighlight lang="lb"> |
|||
n=1000000 '1000000 would take several minutes |
|||
print "Testing ";n;" times" |
|||
if not(check(n, 0.05)) then print "Test failed" else print "Test passed" |
|||
end |
|||
'function check(n, delta) is defined at |
|||
'http://rosettacode.org/wiki/Verify_distribution_uniformity/Naive#Liberty_BASIC |
|||
function GENERATOR() |
|||
'GENERATOR = int(rnd(0)*10) '0..9 |
|||
'GENERATOR = 1+int(rnd(0)*5) '1..5: dice5 |
|||
'dice7() |
|||
do |
|||
temp =dice5() *5 +dice5() -6 |
|||
loop until temp <21 |
|||
GENERATOR =( temp mod 7) +1 |
|||
end function |
|||
function dice5() |
|||
dice5=1+int(rnd(0)*5) '1..5: dice5 |
|||
end function |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
Testing 1000000 times |
|||
minVal Expected maxVal |
|||
135714 142857 150000 |
|||
Bucket Counter pass/fail |
|||
1 143310 |
|||
2 143500 |
|||
3 143040 |
|||
4 145185 |
|||
5 140998 |
|||
6 142610 |
|||
7 141357 |
|||
Test passed |
|||
</pre> |
|||
=={{header|Lua}}== |
|||
<syntaxhighlight lang="lua">dice5 = function() return math.random(5) end |
|||
function dice7() |
function dice7() |
||
Line 357: | Line 1,501: | ||
if x > 20 then return dice7() end |
if x > 20 then return dice7() end |
||
return x%7 + 1 |
return x%7 + 1 |
||
end</ |
end</syntaxhighlight> |
||
=={{header|M2000 Interpreter}}== |
|||
We make a stack object (is reference type) and pass it as a closure to dice7 lambda function. For each dice7 we pop the top value of stack, and we add a fresh dice5 (random(1,5)) as last value of stack, so stack used as FIFO. Each time z has the sum of 7 random values. |
|||
We check for uniform numbers using +-5% from expected value. |
|||
<syntaxhighlight lang="m2000 interpreter"> |
|||
Module CheckIt { |
|||
Def long i, calls, max, min |
|||
s=stack:=random(1,5),random(1,5), random(1,5), random(1,5), random(1,5), random(1,5), random(1,5) |
|||
z=0: for i=1 to 7 { z+=stackitem(s, i)} |
|||
dice7=lambda z, s -> { |
|||
=((z-1) mod 7)+1 : stack s {z-=Number : data random(1,5): z+=Stackitem(7)} |
|||
} |
|||
Dim count(1 to 7)=0& ' long type |
|||
calls=700000 |
|||
p=0.05 |
|||
IsUniform=lambda max=calls/7*(1+p), min=calls/7*(1-p) (a)->{ |
|||
if len(a)=0 then =false : exit |
|||
=false |
|||
m=each(a) |
|||
while m |
|||
if array(m)<min or array(m)>max then break |
|||
end while |
|||
=true |
|||
} |
|||
For i=1 to calls {count(dice7())++} |
|||
max=count()#max() |
|||
expected=calls div 7 |
|||
min=count()#min() |
|||
for i=1 to 7 |
|||
document doc$=format$("{0}{1::-7}",i,count(i))+{ |
|||
} |
|||
Next i |
|||
doc$=format$("min={0} expected={1} max={2}", min, expected, max)+{ |
|||
}+format$("Verify Uniform:{0}", if$(IsUniform(count())->"uniform", "skewed"))+{ |
|||
} |
|||
Print |
|||
report doc$ |
|||
clipboard doc$ |
|||
} |
|||
CheckIt |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll"> |
|||
1 9865 |
|||
2 10109 |
|||
3 9868 |
|||
4 9961 |
|||
5 9936 |
|||
6 9922 |
|||
7 10339 |
|||
min=9865 expected=10000 max=10339 |
|||
Verify Uniform:uniform |
|||
1 100214 |
|||
2 100336 |
|||
3 100049 |
|||
4 99505 |
|||
5 99951 |
|||
6 99729 |
|||
7 100216 |
|||
min=99505 expected=100000 max=100336 |
|||
Verify Uniform:uniform |
|||
</pre > |
|||
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
|||
<syntaxhighlight lang="mathematica">sevenFrom5Dice := (tmp$ = 5*RandomInteger[{1, 5}] + RandomInteger[{1, 5}] - 6; |
|||
If [tmp$ < 21, 1 + Mod[tmp$ , 7], sevenFrom5Dice])</syntaxhighlight> |
|||
<pre>CheckDistribution[sevenFrom5Dice, 1000000, 5] |
|||
->Expected: 142857., Generated :{142206,142590,142650,142693,142730,143475,143656} |
|||
->"Flat"</pre> |
|||
=={{header|Nim}}== |
|||
We use the distribution checker from task [[Simple Random Distribution Checker#Nim|Simple Random Distribution Checker]]. |
|||
<syntaxhighlight lang="nim">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)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Checking 1000000 values with a tolerance of 0.5%. |
|||
Random generator passed the uniformity test. |
|||
Max delta encountered = 552 Allowed delta = 714</pre> |
|||
=={{header|OCaml}}== |
=={{header|OCaml}}== |
||
< |
<syntaxhighlight lang="ocaml">let dice5() = 1 + Random.int 5 ;; |
||
let dice7 = |
let dice7 = |
||
Line 376: | Line 1,637: | ||
in |
in |
||
aux |
aux |
||
;;</ |
;;</syntaxhighlight> |
||
=={{header|PARI/GP}}== |
|||
<syntaxhighlight lang="parigp">dice5()=random(5)+1; |
|||
dice7()={ |
|||
my(t); |
|||
while((t=dice5()*5+dice5()) > 21,); |
|||
(t+2)\3 |
|||
};</syntaxhighlight> |
|||
=={{header|Pascal}}== |
|||
A console application in Free Pascal, created with the Lazarus IDE. |
|||
The algorithm suggested in the task description requires on average 50/21 (about 2.38) calls to Dice5 for each call to Dice7. See the link in the VBA solution for a discussion on how to reduce this ratio. It cannot be made less than log_5(7) = 1.209062. The algorithm below is based on Rex Kerr's solution, and requires about 1.2185 calls to Dice5 per call to Dice7. Runtime is about 60% of that for the suggested simple algorithm. |
|||
A chi-squared test can be carried out with the help of statistical tables, and is preferred here to an arbitrary "naive" test. |
|||
<syntaxhighlight lang="pascal"> |
|||
unit UConverter; |
|||
(* |
|||
Defines a converter object to output uniformly distributed random integers 1..7, |
|||
given a source of uniformly distributed random integers 1..5. |
|||
*) |
|||
interface |
|||
type |
|||
TFace5 = 1..5; |
|||
TFace7 = 1..7; |
|||
TDice5 = function() : TFace5; |
|||
type TConverter = class( TObject) |
|||
private |
|||
fDigitBuf: array [0..19] of integer; // holds digits in base 7 |
|||
fBufCount, fBufPtr : integer; |
|||
fDice5 : TDice5; // passed-in generator for integers 1..5 |
|||
fNrDice5 : int64; // diagnostics, counts calls to fDice5 |
|||
public |
|||
constructor Create( aDice5 : TDice5); |
|||
procedure Reset(); |
|||
function Dice7() : TFace7; |
|||
property NrDice5 : int64 read fNrDice5; |
|||
end; |
|||
implementation |
|||
constructor TConverter.Create( aDice5 : TDice5); |
|||
begin |
|||
inherited Create(); |
|||
fDice5 := aDice5; |
|||
self.Reset(); |
|||
end; |
|||
procedure TConverter.Reset(); |
|||
begin |
|||
fBufCount := 0; |
|||
fBufPtr := 0; |
|||
fNrDice5 := 0; |
|||
end; |
|||
function TConverter.Dice7() : TFace7; |
|||
var |
|||
digit_holder, temp : int64; |
|||
j : integer; |
|||
begin |
|||
if fBufPtr = fBufCount then begin // if no more in buffer |
|||
fBufCount := 0; |
|||
fBufPtr := 0; |
|||
repeat // first time through will usually be enough |
|||
// Use supplied fDice5 to generate random 23-digit integer in base 5. |
|||
digit_holder := 0; |
|||
for j := 0 to 22 do begin |
|||
digit_holder := 5*digit_holder + fDice5() - 1; |
|||
inc( fNrDice5); |
|||
end; |
|||
// Convert to 20-digit number in base 7. (A simultaneous DivMod |
|||
// procedure would be neater, but isn't available for int64.) |
|||
for j := 0 to 19 do begin |
|||
temp := digit_holder div 7; |
|||
fDigitBuf[j] := digit_holder - 7*temp; |
|||
digit_holder := temp; |
|||
end; |
|||
// Maximum possible is 5^23 - 1, which is 10214646460315315132 in base 7. |
|||
// If leading digit in base 7 is 0 then low 19 digits are random. |
|||
// Else number begins with 100, 101, or 102; and if with |
|||
// 100 or 101 then low 17 digits are random. And so on. |
|||
if fDigitBuf[19] = 0 then fBufCount := 19 |
|||
else if fDigitBuf[17] < 2 then fBufCount := 17 |
|||
else if fDigitBuf[16] = 0 then fBufCount := 16; |
|||
// We could go on but that will do. |
|||
until fBufCount > 0; |
|||
end; // if no more in buffer |
|||
result := fDigitBuf[fBufPtr] + 1; |
|||
inc( fBufPtr); |
|||
end; |
|||
end. |
|||
program Dice_SevenFromFive; |
|||
(* |
|||
Demonstrates use of the UConverter unit. |
|||
*) |
|||
{$mode objfpc}{$H+} |
|||
uses |
|||
SysUtils, UConverter; |
|||
function Dice5() : UConverter.TFace5; |
|||
begin |
|||
result := Random(5) + 1; // Random(5) returns 0..4 |
|||
end; |
|||
// Percentage points of the chi-squared distribution, 6 degrees of freedom. |
|||
// From New Cambridge Statistical Tables, 2nd edn, pp. 40-41. |
|||
const |
|||
CHI_SQ_6df_95pc = 1.635; |
|||
CHI_SQ_6df_05pc = 12.59; |
|||
// Main routine |
|||
var |
|||
nrThrows, j, k : integer; |
|||
nrFaces : array [1..7] of integer; |
|||
X2, expected, diff : double; |
|||
conv : UConverter.TConverter; |
|||
begin |
|||
conv := UConverter.TConverter.Create( @Dice5); |
|||
WriteLn( 'Enter 0 throws to quit'); |
|||
repeat |
|||
WriteLn(''); Write( 'Number of throws (0 to quit): '); |
|||
ReadLn( nrThrows); |
|||
if nrThrows = 0 then begin |
|||
conv.Free(); |
|||
exit; |
|||
end; |
|||
conv.Reset(); // clears count of calls to Dice5 |
|||
for k := 1 to 7 do nrFaces[k] := 0; |
|||
for j := 1 to nrThrows do begin |
|||
k := conv.Dice7(); |
|||
inc( nrFaces[k]); |
|||
end; |
|||
WriteLn(''); |
|||
WriteLn( SysUtils.Format( 'Number of throws = %10d', [nrThrows])); |
|||
WriteLn( SysUtils.Format( 'Calls to Dice5 = %10d', [conv.NrDice5])); |
|||
for k := 1 to 7 do |
|||
WriteLn( SysUtils.Format( ' Number of %d''s = %10d', [k, nrFaces[k]])); |
|||
// Calculation of chi-squared |
|||
expected := nrThrows/7.0; |
|||
X2 := 0.0; |
|||
for k := 1 to 7 do begin |
|||
diff := nrFaces[k] - expected; |
|||
X2 := X2 + diff*diff/expected; |
|||
end; |
|||
WriteLn( SysUtils.Format( 'X^2 = %0.3f on 6 degrees of freedom', [X2])); |
|||
if X2 < CHI_SQ_6df_95pc then WriteLn( 'Too regular at 5% level') |
|||
else if X2 > CHI_SQ_6df_05pc then WriteLn( 'Too irregular at 5% level') |
|||
else WriteLn( 'Satisfactory at 5% level') |
|||
until false; |
|||
end. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Number of throws = 100000000 |
|||
Calls to Dice5 = 121846341 |
|||
Number of 1's = 14282807 |
|||
Number of 2's = 14282277 |
|||
Number of 3's = 14288393 |
|||
Number of 4's = 14285486 |
|||
Number of 5's = 14289379 |
|||
Number of 6's = 14291053 |
|||
Number of 7's = 14280605 |
|||
X^2 = 6.687 on 6 degrees of freedom |
|||
Satisfactory at 5% level |
|||
</pre> |
|||
=={{header|Perl}}== |
|||
Using dice5 twice to generate numbers in the range 0 to 24. If we consider these modulo 8 and re-call if we get zero, we have eliminated 4 cases and created the necessary number in the range from 1 to 7. |
|||
<syntaxhighlight lang="perl">sub dice5 { 1+int rand(5) } |
|||
sub dice7 { |
|||
while(1) { |
|||
my $d7 = (5*dice5()+dice5()-6) % 8; |
|||
return $d7 if $d7; |
|||
} |
|||
} |
|||
my %count7; |
|||
my $n = 1000000; |
|||
$count7{dice7()}++ for 1..$n; |
|||
printf "%s: %5.2f%%\n", $_, 100*($count7{$_}/$n*7-1) for sort keys %count7; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
1: 0.05% |
|||
2: 0.16% |
|||
3: -0.43% |
|||
4: 0.11% |
|||
5: 0.01% |
|||
6: -0.15% |
|||
7: 0.24% |
|||
</pre> |
|||
=={{header|Phix}}== |
|||
replace rand7() in [[Verify_distribution_uniformity/Naive#Phix]] with: |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">dice5</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">5</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">dice7</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">r</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dice5</span><span style="color: #0000FF;">()*</span><span style="color: #000000;">5</span><span style="color: #0000FF;">+</span><span style="color: #000000;">dice5</span><span style="color: #0000FF;">()-</span><span style="color: #000000;">3</span> <span style="color: #000080;font-style:italic;">-- ( ie 3..27, but )</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">r</span><span style="color: #0000FF;"><</span><span style="color: #000000;">24</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">r</span><span style="color: #0000FF;">/</span><span style="color: #000000;">3</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (only 3..23 useful)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre> |
|||
1000000 iterations: flat |
|||
</pre> |
|||
=={{header|PicoLisp}}== |
|||
<syntaxhighlight lang="picolisp">(de dice5 () |
|||
(rand 1 5) ) |
|||
(de dice7 () |
|||
(use R |
|||
(until (> 21 (setq R (+ (* 5 (dice5)) (dice5) -6)))) |
|||
(inc (% R 7)) ) )</syntaxhighlight> |
|||
{{out}} |
|||
<pre>: (let R NIL |
|||
(do 1000000 (accu 'R (dice7) 1)) |
|||
(sort R) ) |
|||
-> ((1 . 142295) (2 . 142491) (3 . 143448) (4 . 143129) (5 . 142701) (6 . 143142) (7 . 142794))</pre> |
|||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
{{trans|Lua}} |
{{trans|Lua}} |
||
< |
<syntaxhighlight lang="purebasic">Procedure dice5() |
||
ProcedureReturn Random(4) + 1 |
ProcedureReturn Random(4) + 1 |
||
EndProcedure |
EndProcedure |
||
Line 392: | Line 1,884: | ||
ProcedureReturn x % 7 + 1 |
ProcedureReturn x % 7 + 1 |
||
EndProcedure</ |
EndProcedure</syntaxhighlight> |
||
=={{header|Python}}== |
=={{header|Python}}== |
||
< |
<syntaxhighlight lang="python">from random import randint |
||
def dice5(): |
def dice5(): |
||
Line 402: | Line 1,894: | ||
def dice7(): |
def dice7(): |
||
r = dice5() + dice5() * 5 - 6 |
r = dice5() + dice5() * 5 - 6 |
||
return (r % 7) + 1 if r < 21 else dice7()</ |
return (r % 7) + 1 if r < 21 else dice7()</syntaxhighlight> |
||
Distribution check using [[Simple Random Distribution Checker#Python|Simple Random Distribution Checker]]: |
Distribution check using [[Simple Random Distribution Checker#Python|Simple Random Distribution Checker]]: |
||
<pre>>>> distcheck(dice5, 1000000, 1) |
<pre>>>> distcheck(dice5, 1000000, 1) |
||
Line 409: | Line 1,901: | ||
{1: 142853, 2: 142576, 3: 143067, 4: 142149, 5: 143189, 6: 143285, 7: 142881} |
{1: 142853, 2: 142576, 3: 143067, 4: 142149, 5: 143189, 6: 143285, 7: 142881} |
||
</pre> |
</pre> |
||
=={{header|Quackery}}== |
|||
<syntaxhighlight lang="quackery"> [ 5 random 1+ ] is dice5 ( --> n ) |
|||
[ dice5 5 * |
|||
dice5 + 6 - |
|||
[ table |
|||
0 0 0 0 1 |
|||
1 1 2 2 2 |
|||
3 3 3 4 4 |
|||
4 5 5 5 6 |
|||
6 6 7 7 7 ] |
|||
dup 0 = iff |
|||
drop again ] is dice7 ( --> n )</syntaxhighlight> |
|||
{{out}} |
|||
<code>distribution</code> is defined at [[Verify distribution uniformity/Naive#Quackery]]. |
|||
<pre>/O> ' dice7 1000000 666 distribution |
|||
... |
|||
[ 143196 142815 143451 142716 142964 142300 142558 ] |
|||
Stack empty.</pre> |
|||
=={{header|R}}== |
=={{header|R}}== |
||
5-sided die. |
5-sided die. |
||
< |
<syntaxhighlight lang="r">dice5 <- function(n=1) sample(5, n, replace=TRUE)</syntaxhighlight> |
||
Simple but slow 7-sided die, using a while loop. |
Simple but slow 7-sided die, using a while loop. |
||
< |
<syntaxhighlight lang="r">dice7.while <- function(n=1) |
||
{ |
{ |
||
score <- numeric() |
score <- numeric() |
||
Line 424: | Line 1,940: | ||
score |
score |
||
} |
} |
||
system.time(dice7.while(1e6)) # longer than 4 minutes</ |
system.time(dice7.while(1e6)) # longer than 4 minutes</syntaxhighlight> |
||
More complex, but much faster vectorised version. |
More complex, but much faster vectorised version. |
||
< |
<syntaxhighlight lang="r">dice7.vec <- function(n=1, checkLength=TRUE) |
||
{ |
{ |
||
morethan2n <- 3 * n + 10 + (n %% 2) #need more than 2*n samples, because some are discarded |
morethan2n <- 3 * n + 10 + (n %% 2) #need more than 2*n samples, because some are discarded |
||
Line 443: | Line 1,959: | ||
} else score |
} else score |
||
} |
} |
||
system.time(dice7.vec(1e6)) # ~1 sec</ |
system.time(dice7.vec(1e6)) # ~1 sec</syntaxhighlight> |
||
=={{header|Racket}}== |
|||
<syntaxhighlight lang="racket"> |
|||
#lang racket |
|||
(define (dice5) (add1 (random 5))) |
|||
(define (dice7) |
|||
(define res (+ (* 5 (dice5)) (dice5) -6)) |
|||
(if (< res 21) (+ 1 (modulo res 7)) (dice7))) |
|||
</syntaxhighlight> |
|||
Checking the uniformity using math library: |
|||
<syntaxhighlight lang="racket"> |
|||
-> (require math/statistics) |
|||
-> (samples->hash (for/list ([i 700000]) (dice7))) |
|||
'#hash((7 . 100392) |
|||
(6 . 100285) |
|||
(5 . 99774) |
|||
(4 . 100000) |
|||
(3 . 100000) |
|||
(2 . 99927) |
|||
(1 . 99622)) |
|||
</syntaxhighlight> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2018.03}} |
|||
<syntaxhighlight lang="raku" line>my $d5 = 1..5; |
|||
sub d5() { $d5.roll; } # 1d5 |
|||
sub d7() { |
|||
my $flat = 21; |
|||
$flat = 5 * d5() - d5() until $flat < 21; |
|||
$flat % 7 + 1; |
|||
} |
|||
# Testing |
|||
my @dist; |
|||
my $n = 1_000_000; |
|||
my $expect = $n / 7; |
|||
loop ($_ = $n; $n; --$n) { @dist[d7()]++; } |
|||
say "Expect\t",$expect.fmt("%.3f"); |
|||
for @dist.kv -> $i, $v { |
|||
say "$i\t$v\t" ~ (($v - $expect)/$expect*100).fmt("%+.2f%%") if $v; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Expect 142857.143 |
|||
1 143088 +0.16% |
|||
2 143598 +0.52% |
|||
3 141741 -0.78% |
|||
4 142832 -0.02% |
|||
5 143040 +0.13% |
|||
6 142988 +0.09% |
|||
7 142713 -0.10% |
|||
</pre> |
|||
=={{header|REXX}}== |
|||
<syntaxhighlight lang="rexx">/*REXX program simulates a 7─sided die based on a 5─sided throw for a number of trials. */ |
|||
parse arg trials sample seed . /*obtain optional arguments from the CL*/ |
|||
if trials=='' | trials="," then trials= 1 /*Not specified? Then use the default.*/ |
|||
if sample=='' | sample="," then sample= 1000000 /* " " " " " " */ |
|||
if datatype(seed, 'W') then call random ,,seed /*Integer? Then use it as a RAND seed.*/ |
|||
L= length(trials) /* [↑] one million samples to be used.*/ |
|||
do #=1 for trials; die.= 0 /*performs the number of desired trials*/ |
|||
k= 0 |
|||
do until k==sample; r= 5 * random(1, 5) + random(1, 5) - 6 |
|||
if r>20 then iterate |
|||
k= k + 1; r= r // 7 + 1; die.r= die.r + 1 |
|||
end /*until*/ |
|||
say |
|||
expect= sample % 7 |
|||
say center('trial:' right(#, L) " " sample 'samples, expect' expect, 80, "─") |
|||
do j=1 for 7 |
|||
say ' side' j "had " die.j ' occurrences', |
|||
' difference from expected:'right(die.j - expect, length(sample) ) |
|||
end /*j*/ |
|||
end /*#*/ /*stick a fork in it, we're all done. */</syntaxhighlight> |
|||
{{out|output|text= when using the input of: <tt> 11 </tt>}} |
|||
<br>(Shown at five-sixth size.) |
|||
<pre style="font-size:84%;height:71ex"> |
|||
──────────────────trial: 1 1000000 samples, expect 142857────────────────── |
|||
side 1 had 142076 occurrences difference from expected: -781 |
|||
side 2 had 143053 occurrences difference from expected: 196 |
|||
side 3 had 142342 occurrences difference from expected: -515 |
|||
side 4 had 142633 occurrences difference from expected: -224 |
|||
side 5 had 143024 occurrences difference from expected: 167 |
|||
side 6 had 143827 occurrences difference from expected: 970 |
|||
side 7 had 143045 occurrences difference from expected: 188 |
|||
──────────────────trial: 2 1000000 samples, expect 142857────────────────── |
|||
side 1 had 143470 occurrences difference from expected: 613 |
|||
side 2 had 142998 occurrences difference from expected: 141 |
|||
side 3 had 142654 occurrences difference from expected: -203 |
|||
side 4 had 142545 occurrences difference from expected: -312 |
|||
side 5 had 142452 occurrences difference from expected: -405 |
|||
side 6 had 143144 occurrences difference from expected: 287 |
|||
side 7 had 142737 occurrences difference from expected: -120 |
|||
──────────────────trial: 3 1000000 samples, expect 142857────────────────── |
|||
side 1 had 142773 occurrences difference from expected: -84 |
|||
side 2 had 143198 occurrences difference from expected: 341 |
|||
side 3 had 142296 occurrences difference from expected: -561 |
|||
side 4 had 142804 occurrences difference from expected: -53 |
|||
side 5 had 142897 occurrences difference from expected: 40 |
|||
side 6 had 142382 occurrences difference from expected: -475 |
|||
side 7 had 143650 occurrences difference from expected: 793 |
|||
──────────────────trial: 4 1000000 samples, expect 142857────────────────── |
|||
side 1 had 143150 occurrences difference from expected: 293 |
|||
side 2 had 142635 occurrences difference from expected: -222 |
|||
side 3 had 142763 occurrences difference from expected: -94 |
|||
side 4 had 142853 occurrences difference from expected: -4 |
|||
side 5 had 143132 occurrences difference from expected: 275 |
|||
side 6 had 142403 occurrences difference from expected: -454 |
|||
side 7 had 143064 occurrences difference from expected: 207 |
|||
──────────────────trial: 5 1000000 samples, expect 142857────────────────── |
|||
side 1 had 143041 occurrences difference from expected: 184 |
|||
side 2 had 142701 occurrences difference from expected: -156 |
|||
side 3 had 143416 occurrences difference from expected: 559 |
|||
side 4 had 142097 occurrences difference from expected: -760 |
|||
side 5 had 142451 occurrences difference from expected: -406 |
|||
side 6 had 143332 occurrences difference from expected: 475 |
|||
side 7 had 142962 occurrences difference from expected: 105 |
|||
──────────────────trial: 6 1000000 samples, expect 142857────────────────── |
|||
side 1 had 142502 occurrences difference from expected: -355 |
|||
side 2 had 142429 occurrences difference from expected: -428 |
|||
side 3 had 143146 occurrences difference from expected: 289 |
|||
side 4 had 142791 occurrences difference from expected: -66 |
|||
side 5 had 143271 occurrences difference from expected: 414 |
|||
side 6 had 143415 occurrences difference from expected: 558 |
|||
side 7 had 142446 occurrences difference from expected: -411 |
|||
──────────────────trial: 7 1000000 samples, expect 142857────────────────── |
|||
side 1 had 142700 occurrences difference from expected: -157 |
|||
side 2 had 142691 occurrences difference from expected: -166 |
|||
side 3 had 143067 occurrences difference from expected: 210 |
|||
side 4 had 141562 occurrences difference from expected: -1295 |
|||
side 5 had 143316 occurrences difference from expected: 459 |
|||
side 6 had 143150 occurrences difference from expected: 293 |
|||
side 7 had 143514 occurrences difference from expected: 657 |
|||
──────────────────trial: 8 1000000 samples, expect 142857────────────────── |
|||
side 1 had 142362 occurrences difference from expected: -495 |
|||
side 2 had 143298 occurrences difference from expected: 441 |
|||
side 3 had 142639 occurrences difference from expected: -218 |
|||
side 4 had 142811 occurrences difference from expected: -46 |
|||
side 5 had 143275 occurrences difference from expected: 418 |
|||
side 6 had 142765 occurrences difference from expected: -92 |
|||
side 7 had 142850 occurrences difference from expected: -7 |
|||
──────────────────trial: 9 1000000 samples, expect 142857────────────────── |
|||
side 1 had 143508 occurrences difference from expected: 651 |
|||
side 2 had 142650 occurrences difference from expected: -207 |
|||
side 3 had 142614 occurrences difference from expected: -243 |
|||
side 4 had 142916 occurrences difference from expected: 59 |
|||
side 5 had 142944 occurrences difference from expected: 87 |
|||
side 6 had 143129 occurrences difference from expected: 272 |
|||
side 7 had 142239 occurrences difference from expected: -618 |
|||
──────────────────trial: 10 1000000 samples, expect 142857────────────────── |
|||
side 1 had 142455 occurrences difference from expected: -402 |
|||
side 2 had 143112 occurrences difference from expected: 255 |
|||
side 3 had 143435 occurrences difference from expected: 578 |
|||
side 4 had 142704 occurrences difference from expected: -153 |
|||
side 5 had 142376 occurrences difference from expected: -481 |
|||
side 6 had 142721 occurrences difference from expected: -136 |
|||
side 7 had 143197 occurrences difference from expected: 340 |
|||
──────────────────trial: 11 1000000 samples, expect 142857────────────────── |
|||
side 1 had 142967 occurrences difference from expected: 110 |
|||
side 2 had 142204 occurrences difference from expected: -653 |
|||
side 3 had 142993 occurrences difference from expected: 136 |
|||
side 4 had 142797 occurrences difference from expected: -60 |
|||
side 5 had 143081 occurrences difference from expected: 224 |
|||
side 6 had 142711 occurrences difference from expected: -146 |
|||
side 7 had 143247 occurrences difference from expected: 390 |
|||
</pre> |
|||
=={{header|Ring}}== |
|||
<syntaxhighlight lang="ring"> |
|||
# Project : Seven-sided dice from five-sided dice |
|||
for n = 1 to 20 |
|||
d = dice7() |
|||
see "" + d + " " |
|||
next |
|||
see nl |
|||
func dice7() |
|||
x = dice5() * 5 + dice5() - 6 |
|||
if x > 20 |
|||
return dice7() |
|||
ok |
|||
dc = x % 7 + 1 |
|||
return dc |
|||
func dice5() |
|||
rnd = random(4) + 1 |
|||
return rnd |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
7 6 3 5 2 2 7 1 2 7 3 7 4 4 4 2 3 2 6 1 |
|||
</pre> |
|||
=={{header|RPL}}== |
|||
<code>UNIF?</code> is defined at [[Verify distribution uniformity/Naive#RPL|Verify distribution uniformity/Naive]] |
|||
{{works with|Halcyon Calc|4.2.7}} |
|||
≪ ≪ RAND 5 * CEIL ≫ → dice5 |
|||
≪ '''WHILE''' |
|||
dice5 EVAL 5 * |
|||
dice5 EVAL 6 - + |
|||
DUP 21 ≥ |
|||
'''REPEAT''' DROP '''END''' |
|||
7 MOD 1 + |
|||
≫ ≫ '<span style="color:blue">DICE7</span>' STO |
|||
≪ <span style="color:blue">DICE7</span> ≫ 100000 .1 <span style="color:blue">UNIF?</span> |
|||
{{out}} |
|||
<pre> |
|||
1: [ 14557 14245 14255 14400 14224 14151 14168 ] |
|||
</pre> |
|||
Watchdog timer limits the loop to 100,000 items. |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
{{trans|Tcl}} |
{{trans|Tcl}} |
||
Uses <code>distcheck</code> from [[Simple_Random_Distribution_Checker#Ruby|here]]. |
Uses <code>distcheck</code> from [[Simple_Random_Distribution_Checker#Ruby|here]]. |
||
< |
<syntaxhighlight lang="ruby">require './distcheck.rb' |
||
def d5 |
def d5 |
||
Line 457: | Line 2,205: | ||
def d7 |
def d7 |
||
loop do |
loop do |
||
d55 = 5*d5 |
d55 = 5*d5 + d5 - 6 |
||
return (d55 % 7 + 1) if d55 < 21 |
return (d55 % 7 + 1) if d55 < 21 |
||
end |
end |
||
Line 463: | Line 2,211: | ||
distcheck(1_000_000) {d5} |
distcheck(1_000_000) {d5} |
||
distcheck(1_000_000) {d7}</ |
distcheck(1_000_000) {d7}</syntaxhighlight> |
||
{{out}} |
|||
output |
|||
<pre>1 200227 |
|||
<pre>1 200478 2 199986 3 199582 4 199560 5 200394 |
|||
2 200264 |
|||
1 142371 2 142577 3 143328 4 143630 5 142553 6 142692 7 142849 </pre> |
|||
3 199777 |
|||
4 199387 |
|||
5 200345 |
|||
1 143175 |
|||
2 143031 |
|||
3 142731 |
|||
4 142716 |
|||
5 142931 |
|||
6 142605 |
|||
7 142811</pre> |
|||
=={{header|Scala}}== |
|||
{{Out}}Best seen running in your browser either by [https://scalafiddle.io/sf/3RNtNEC/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/Y5qSeW52QiC40l5vJCUMRA Scastie (remote JVM)]. |
|||
<syntaxhighlight lang="scala">import scala.util.Random |
|||
object SevenSidedDice extends App { |
|||
private val rnd = new Random |
|||
private def seven = { |
|||
var v = 21 |
|||
def five = 1 + rnd.nextInt(5) |
|||
while (v > 20) v = five + five * 5 - 6 |
|||
1 + v % 7 |
|||
} |
|||
println("Random number from 1 to 7: " + seven) |
|||
}</syntaxhighlight> |
|||
=={{header|Sidef}}== |
|||
{{trans|Perl}} |
|||
<syntaxhighlight lang="ruby">func dice5 { 1 + 5.rand.int } |
|||
func dice7 { |
|||
loop { |
|||
var d7 = ((5*dice5() + dice5() - 6) % 8); |
|||
d7 && return d7; |
|||
} |
|||
} |
|||
var count7 = Hash.new; |
|||
var n = 1e6; |
|||
n.times { count7{dice7()} := 0 ++ } |
|||
count7.keys.sort.each { |k| |
|||
printf("%s: %5.2f%%\n", k, 100*(count7{k}/n * 7 - 1)); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>1: -0.00% |
|||
2: 0.02% |
|||
3: 0.23% |
|||
4: 0.42% |
|||
5: -0.23% |
|||
6: -0.54% |
|||
7: 0.10%</pre> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
Any old D&D hand will know these as a D5 and a D7... |
Any old D&D hand will know these as a D5 and a D7... |
||
< |
<syntaxhighlight lang="tcl">proc D5 {} {expr {1 + int(5 * rand())}} |
||
proc D7 {} { |
proc D7 {} { |
||
Line 480: | Line 2,285: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Checking: |
Checking: |
||
<span class="sy0">%</span> distcheck D5 <span class="nu0">1000000</span> |
<span class="sy0">%</span> distcheck D5 <span class="nu0">1000000</span> |
||
Line 486: | Line 2,291: | ||
<span class="sy0">%</span> distcheck D7 <span class="nu0">1000000</span> |
<span class="sy0">%</span> distcheck D7 <span class="nu0">1000000</span> |
||
1 143121 2 142383 3 143353 4 142811 5 142172 6 143291 7 142869 |
1 143121 2 142383 3 143353 4 142811 5 142172 6 143291 7 142869 |
||
=={{header|VBA}}== |
|||
The original StackOverflow page doesn't exist any longer. Luckily [https://web.archive.org/web/20100730055051/http://stackoverflow.com:80/questions/137783/given-a-function-which-produces-a-random-integer-in-the-range-1-to-5-write-a-fun archive.org] exists. |
|||
<syntaxhighlight lang="vb">Private Function Test4DiscreteUniformDistribution(ObservationFrequencies() As Variant, Significance As Single) As Boolean |
|||
'Returns true if the observed frequencies pass the Pearson Chi-squared test at the required significance level. |
|||
Dim Total As Long, Ei As Long, i As Integer |
|||
Dim ChiSquared As Double, DegreesOfFreedom As Integer, p_value As Double |
|||
Debug.Print "[1] ""Data set:"" "; |
|||
For i = LBound(ObservationFrequencies) To UBound(ObservationFrequencies) |
|||
Total = Total + ObservationFrequencies(i) |
|||
Debug.Print ObservationFrequencies(i); " "; |
|||
Next i |
|||
DegreesOfFreedom = UBound(ObservationFrequencies) - LBound(ObservationFrequencies) |
|||
'This is exactly the number of different categories minus 1 |
|||
Ei = Total / (DegreesOfFreedom + 1) |
|||
For i = LBound(ObservationFrequencies) To UBound(ObservationFrequencies) |
|||
ChiSquared = ChiSquared + (ObservationFrequencies(i) - Ei) ^ 2 / Ei |
|||
Next i |
|||
p_value = 1 - WorksheetFunction.ChiSq_Dist(ChiSquared, DegreesOfFreedom, True) |
|||
Debug.Print |
|||
Debug.Print "Chi-squared test for given frequencies" |
|||
Debug.Print "X-squared ="; Format(ChiSquared, "0.0000"); ", "; |
|||
Debug.Print "df ="; DegreesOfFreedom; ", "; |
|||
Debug.Print "p-value = "; Format(p_value, "0.0000") |
|||
Test4DiscreteUniformDistribution = p_value > Significance |
|||
End Function |
|||
Private Function Dice5() As Integer |
|||
Dice5 = Int(5 * Rnd + 1) |
|||
End Function |
|||
Private Function Dice7() As Integer |
|||
Dim i As Integer |
|||
Do |
|||
i = 5 * (Dice5 - 1) + Dice5 |
|||
Loop While i > 21 |
|||
Dice7 = i Mod 7 + 1 |
|||
End Function |
|||
Sub TestDice7() |
|||
Dim i As Long, roll As Integer |
|||
Dim Bins(1 To 7) As Variant |
|||
For i = 1 To 1000000 |
|||
roll = Dice7 |
|||
Bins(roll) = Bins(roll) + 1 |
|||
Next i |
|||
Debug.Print "[1] ""Uniform? "; Test4DiscreteUniformDistribution(Bins, 0.05); """" |
|||
End Sub</syntaxhighlight> |
|||
{{out}}<pre>[1] "Data set:" 142418 142898 142940 142573 143030 143139 143002 |
|||
Chi-squared test for given frequencies |
|||
X-squared =2.8870, df = 6 , p-value = 0.8229 |
|||
[1] "Uniform? True" |
|||
</pre> |
|||
=={{header|VBScript}}== |
|||
<syntaxhighlight lang="vb">Option Explicit |
|||
function dice5 |
|||
dice5 = int(rnd*5) + 1 |
|||
end function |
|||
function dice7 |
|||
dim j |
|||
do |
|||
j = 5 * dice5 + dice5 - 6 |
|||
loop until j < 21 |
|||
dice7 = j mod 7 + 1 |
|||
end function</syntaxhighlight> |
|||
=={{header|Verilog}}== |
|||
<syntaxhighlight lang="verilog"> |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
/// seven_sided_dice_tb : (testbench) /// |
|||
/// Check the distribution of the output of a seven sided dice circuit /// |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
module seven_sided_dice_tb; |
|||
reg [31:0] freq[0:6]; |
|||
reg clk; |
|||
wire [2:0] dice_face; |
|||
reg req; |
|||
wire valid_roll; |
|||
integer i; |
|||
initial begin |
|||
clk <= 0; |
|||
forever begin |
|||
#1; |
|||
clk <= ~clk; |
|||
end |
|||
end |
|||
initial begin |
|||
req <= 1'b1; |
|||
for(i = 0; i < 7; i = i + 1) begin |
|||
freq[i] <= 32'b0; |
|||
end |
|||
repeat(10) @(posedge clk); |
|||
repeat(7000000) begin |
|||
@(posedge clk); |
|||
while(~valid_roll) begin |
|||
@(posedge clk); |
|||
end |
|||
freq[dice_face] <= freq[dice_face] + 32'b1; |
|||
end |
|||
$display("********************************************"); |
|||
$display("*** Seven sided dice distribution: "); |
|||
$display(" Theoretical distribution is an uniform "); |
|||
$display(" distribution with (1/7)-probability "); |
|||
$display(" for each possible outcome, "); |
|||
$display(" The experimental distribution is: "); |
|||
for(i = 0; i < 7; i = i + 1) begin |
|||
if(freq[i] < 32'd1_000_000) begin |
|||
$display("%d with probability 1/7 - (%d ppm)", i, (32'd1_000_000 - freq[i])/7); |
|||
end |
|||
else begin |
|||
$display("%d with probability 1/7 + (%d ppm)", i, (freq[i] - 32'd1_000_000)/7); |
|||
end |
|||
end |
|||
$finish; |
|||
end |
|||
seven_sided_dice DUT( |
|||
.clk(clk), |
|||
.req(req), |
|||
.valid_roll(valid_roll), |
|||
.dice_face(dice_face) |
|||
); |
|||
endmodule |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
/// seven_sided_dice : /// |
|||
/// Synthsizeable module that using a 5 sided dice as a black box /// |
|||
/// is able to reproduce the outcomes if a 7-sided dice /// |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
module seven_sided_dice( |
|||
input wire clk, |
|||
input wire req, |
|||
output reg valid_roll, |
|||
output reg [2:0] dice_face |
|||
); |
|||
wire [2:0] face1; |
|||
wire [2:0] face2; |
|||
reg [4:0] combination; |
|||
reg req_p1; |
|||
reg req_p2; |
|||
reg req_p3; |
|||
always @(posedge clk) begin |
|||
req_p1 <= req; |
|||
req_p2 <= req_p1; |
|||
end |
|||
always @(posedge clk) begin |
|||
if(req_p1) begin |
|||
combination <= face1 + face2 + {face2, 2'b00}; |
|||
end |
|||
if(req_p2) begin |
|||
case(combination) |
|||
5'd0, 5'd1, 5'd2: {valid_roll, dice_face} <= {1'b1, 3'd0}; |
|||
5'd3, 5'd4, 5'd5: {valid_roll, dice_face} <= {1'b1, 3'd1}; |
|||
5'd6, 5'd7, 5'd8: {valid_roll, dice_face} <= {1'b1, 3'd2}; |
|||
5'd9, 5'd10, 5'd11: {valid_roll, dice_face} <= {1'b1, 3'd3}; |
|||
5'd12, 5'd13, 5'd14: {valid_roll, dice_face} <= {1'b1, 3'd4}; |
|||
5'd15, 5'd16, 5'd17: {valid_roll, dice_face} <= {1'b1, 3'd5}; |
|||
5'd18, 5'd19, 5'd20: {valid_roll, dice_face} <= {1'b1, 3'd6}; |
|||
default: valid_roll <= 1'b0; |
|||
endcase |
|||
end |
|||
end |
|||
five_sided_dice dice1( |
|||
.clk(clk), |
|||
.req(req), |
|||
.dice_face(face1) |
|||
); |
|||
five_sided_dice dice2( |
|||
.clk(clk), |
|||
.req(req), |
|||
.dice_face(face2) |
|||
); |
|||
endmodule |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
/// five_sided_dice : /// |
|||
/// A model of the five sided dice component /// |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
module five_sided_dice( |
|||
input wire clk, |
|||
input wire req, |
|||
output reg [2:0] dice_face |
|||
); |
|||
always @(posedge clk) begin |
|||
if(req) begin |
|||
dice_face <= $urandom % 5; |
|||
end |
|||
end |
|||
endmodule |
|||
</syntaxhighlight> |
|||
Compiling with Icarus Verilog |
|||
<pre> |
|||
> iverilog seven-sided-dice.v -o seven-sided-dice |
|||
</pre> |
|||
Running the test |
|||
<pre> |
|||
> vvp seven-sided-dice |
|||
******************************************** |
|||
*** Seven sided dice distribution: |
|||
Theoretical distribution is an uniform |
|||
distribution with (1/7)-probability |
|||
for each possible outcome, |
|||
The experimental distribution is: |
|||
0 with probability 1/7 + ( 67 ppm) |
|||
1 with probability 1/7 - ( 47 ppm) |
|||
2 with probability 1/7 + ( 92 ppm) |
|||
3 with probability 1/7 - ( 17 ppm) |
|||
4 with probability 1/7 - ( 36 ppm) |
|||
5 with probability 1/7 + ( 51 ppm) |
|||
6 with probability 1/7 - ( 109 ppm) |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
{{trans|Kotlin}} |
|||
{{libheader|Wren-sort}} |
|||
{{libheader|Wren-fmt}} |
|||
<syntaxhighlight lang="wren">import "random" for Random |
|||
import "./sort" for Sort |
|||
import "./fmt" for Fmt |
|||
var r = Random.new() |
|||
var dice5 = Fn.new { r.int(1, 6) } |
|||
var dice7 = Fn.new { |
|||
while (true) { |
|||
var t = (dice5.call() - 1) * 5 + dice5.call() - 1 |
|||
if (t < 21) return 1 + (t/3).floor |
|||
} |
|||
} |
|||
var checkDist = Fn.new { |gen, nRepeats, tolerance| |
|||
var occurs = {} |
|||
for (i in 1..nRepeats) { |
|||
var d = gen.call() |
|||
occurs[d] = occurs.containsKey(d) ? occurs[d] + 1 : 1 |
|||
} |
|||
var expected = (nRepeats/occurs.count).floor |
|||
var maxError = (expected * tolerance / 100).floor |
|||
System.print("Repetitions = %(nRepeats), Expected = %(expected)") |
|||
System.print("Tolerance = %(tolerance)\%, Max Error = %(maxError)\n") |
|||
System.print("Integer Occurrences Error Acceptable") |
|||
var f = " $d $5d $5d $s" |
|||
var allAcceptable = true |
|||
var cmp = Fn.new { |me1, me2| (me1.key - me2.key).sign } |
|||
occurs = occurs.toList |
|||
Sort.insertion(occurs, cmp) |
|||
for (me in occurs) { |
|||
var k = me.key |
|||
var v = me.value |
|||
var error = (v - expected).abs |
|||
var acceptable = (error <= maxError) ? "Yes" : "No" |
|||
if (acceptable == "No") allAcceptable = false |
|||
Fmt.print(f, k, v, error, acceptable) |
|||
} |
|||
System.print("\nAcceptable overall: %(allAcceptable ? "Yes" : "No")") |
|||
} |
|||
checkDist.call(dice7, 1400000, 0.5)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Repetitions = 1400000, Expected = 200000 |
|||
Tolerance = 0.5%, Max Error = 1000 |
|||
Integer Occurrences Error Acceptable |
|||
1 199744 256 Yes |
|||
2 199678 322 Yes |
|||
3 200254 254 Yes |
|||
4 199903 97 Yes |
|||
5 200080 80 Yes |
|||
6 200070 70 Yes |
|||
7 200271 271 Yes |
|||
Acceptable overall: Yes |
|||
</pre> |
|||
=={{header|zkl}}== |
|||
<syntaxhighlight lang="zkl">var die5=(1).random.fp(6); // [1..5] |
|||
fcn die7{ while((r:=5*die5() + die5())>=27){} r/3-1 } |
|||
fcn rtest(N){ //test spread over [0..9] |
|||
dist:=L(0,0,0,0,0,0,0,0,0,0); |
|||
do(N){ dist[die7()]+=1 } |
|||
sum:=dist.sum(); |
|||
dist=dist.apply('wrap(n){ "%.2f%%".fmt(n.toFloat()/sum*100) }).println(); |
|||
} |
|||
println("Looking for ",100.0/7,"%"); |
|||
rtest(0d1_000_000);</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Looking for 14.2857% |
|||
L("0.00%","14.28%","14.36%","14.22%","14.26%","14.34%","14.33%","14.21%","0.00%","0.00%") |
|||
</pre> |
Latest revision as of 22:56, 13 February 2024
You are encouraged to solve this task according to the task description, using any language you may know.
- 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)
11l
F dice5()
R random:(1..5)
F dice7() -> Int
V r = dice5() + dice5() * 5 - 6
R I r < 21 {(r % 7) + 1} E dice7()
F distcheck(func, repeats, delta)
V bin = DefaultDict[Int, Int]()
L 1..repeats
bin[func()]++
V target = repeats I/ bin.len
V deltacount = Int(delta / 100.0 * target)
assert(all(bin.values().map(count -> abs(@target - count) < @deltacount)), ‘Bin distribution skewed from #. +/- #.: #.’.format(target, deltacount, sorted(bin.items()).map((key, count) -> (key, @target - count))))
print(bin)
distcheck(dice5, 1000000, 1)
distcheck(dice7, 1000000, 1)
- Output:
DefaultDict([1 = 199586, 2 = 200094, 3 = 198933, 4 = 200824, 5 = 200563]) DefaultDict([1 = 142478, 2 = 142846, 3 = 143056, 4 = 142894, 5 = 143052, 6 = 143147, 7 = 142527])
Ada
The specification of a package Random_57:
package Random_57 is
type Mod_7 is mod 7;
function Random7 return Mod_7;
-- a "fast" implementation, minimazing the calls to the Random5 generator
function Simple_Random7 return Mod_7;
-- a simple implementation
end Random_57;
Implementation of Random_57:
with Ada.Numerics.Discrete_Random;
package body Random_57 is
type M5 is mod 5;
package Rand_5 is new Ada.Numerics.Discrete_Random(M5);
Gen: Rand_5.Generator;
function Random7 return Mod_7 is
N: Natural;
begin
loop
N := Integer(Rand_5.Random(Gen))* 5 + Integer(Rand_5.Random(Gen));
-- N is uniformly distributed in 0 .. 24
if N < 21 then
return Mod_7(N/3);
else -- (N-21) is in 0 .. 3
N := (N-21) * 5 + Integer(Rand_5.Random(Gen)); -- N is in 0 .. 19
if N < 14 then
return Mod_7(N / 2);
else -- (N-14) is in 0 .. 5
N := (N-14) * 5 + Integer(Rand_5.Random(Gen)); -- N is in 0 .. 29
if N < 28 then
return Mod_7(N/4);
else -- (N-28) is in 0 .. 1
N := (N-28) * 5 + Integer(Rand_5.Random(Gen)); -- 0 .. 9
if N < 7 then
return Mod_7(N);
else -- (N-7) is in 0, 1, 2
N := (N-7)* 5 + Integer(Rand_5.Random(Gen)); -- 0 .. 14
if N < 14 then
return Mod_7(N/2);
else -- (N-14) is 0. This is not useful for us!
null;
end if;
end if;
end if;
end if;
end if;
end loop;
end Random7;
function Simple_Random7 return Mod_7 is
N: Natural :=
Integer(Rand_5.Random(Gen))* 5 + Integer(Rand_5.Random(Gen));
-- N is uniformly distributed in 0 .. 24
begin
while N > 20 loop
N := Integer(Rand_5.Random(Gen))* 5 + Integer(Rand_5.Random(Gen));
end loop; -- Now I <= 20
return Mod_7(N / 3);
end Simple_Random7;
begin
Rand_5.Reset(Gen);
end Random_57;
A main program, using the Random_57 package:
with Ada.Text_IO, Random_57;
procedure R57 is
use Random_57;
type Fun is access function return Mod_7;
function Rand return Mod_7 renames Random_57.Random7;
-- change this to "... renames Random_57.Simple_Random;" if you like
procedure Test(Sample_Size: Positive; Rand: Fun; Precision: Float := 0.3) is
Counter: array(Mod_7) of Natural := (others => 0);
Expected: Natural := Sample_Size/7;
Small: Mod_7 := Mod_7'First;
Large: Mod_7 := Mod_7'First;
Result: Mod_7;
begin
Ada.Text_IO.New_Line;
Ada.Text_IO.Put_Line("Sample Size: " & Integer'Image(Sample_Size));
Ada.Text_IO.Put( " Bins:");
for I in 1 .. Sample_Size loop
Result := Rand.all;
Counter(Result) := Counter(Result) + 1;
end loop;
for J in Mod_7 loop
Ada.Text_IO.Put(Integer'Image(Counter(J)));
if Counter(J) < Counter(Small) then Small := J; end if;
if Counter(J) > Counter(Large) then Large := J; end if;
end loop;
Ada.Text_IO.New_Line;
Ada.Text_IO.Put_Line(" Small Bin:" & Integer'Image(Counter(Small)));
Ada.Text_IO.Put_Line(" Large Bin: " & Integer'Image(Counter(Large)));
if Float(Counter(Small)*7) * (1.0+Precision) < Float(Sample_Size) then
Ada.Text_IO.Put_Line("Failed! Small too small!");
elsif Float(Counter(Large)*7) * (1.0-Precision) > Float(Sample_Size) then
Ada.Text_IO.Put_Line("Failed! Large too large!");
else
Ada.Text_IO.Put_Line("Passed");
end if;
end Test;
begin
Test( 10_000, Rand'Access, 0.08);
Test( 100_000, Rand'Access, 0.04);
Test( 1_000_000, Rand'Access, 0.02);
Test(10_000_000, Rand'Access, 0.01);
end R57;
- Output:
Sample Size: 10000 Bins: 1368 1404 1435 1491 1483 1440 1379 Small Bin: 1368 Large Bin: 1491 Passed Sample Size: 100000 Bins: 14385 14110 14362 14404 14362 14206 14171 Small Bin: 14110 Large Bin: 14404 Passed Sample Size: 1000000 Bins: 143765 142384 142958 142684 142799 142956 142454 Small Bin: 142384 Large Bin: 143765 Passed Sample Size: 10000000 Bins: 1429266 1428214 1428753 1427032 1428418 1428699 1429618 Small Bin: 1427032 Large Bin: 1429618 Passed
ALGOL 68
- note: This specimen retains the original C coding style.
C's version using no multiplications, divisions, or mod operators:
PROC dice5 = INT:
1 + ENTIER (5*random);
PROC mulby5 = (INT n)INT:
ABS (BIN n SHL 2) + n;
PROC dice7 = INT: (
INT d55 := 0;
INT m := 1;
WHILE
m := ABS ((2r1 AND BIN m) SHL 2) + ABS (BIN m SHR 1); # repeats 4 - 2 - 1 #
d55 := mulby5(mulby5(d55)) + mulby5(dice5) + dice5 - 6;
# WHILE # d55 < m DO SKIP OD;
m := 1;
WHILE d55>0 DO
d55 +:= m;
m := ABS (BIN d55 AND 2r111); # modulas by 8 #
d55 := ABS (BIN d55 SHR 3) # divide by 8 #
OD;
m
);
PROC distcheck = (PROC INT dice, INT count, upb)VOID: (
[upb]INT sum; FOR i TO UPB sum DO sum[i] := 0 OD;
FOR i TO count DO sum[dice]+:=1 OD;
FOR i TO UPB sum WHILE print(whole(sum[i],0)); i /= UPB sum DO print(", ") OD;
print(new line)
);
main:
(
distcheck(dice5, 1000000, 5);
distcheck(dice7, 1000000, 7)
)
- Output:
200598, 199852, 199939, 200602, 199009 143529, 142688, 142816, 142747, 142958, 142802, 142460
AutoHotkey
dice5()
{ Random, v, 1, 5
Return, v
}
dice7()
{ Loop
{ v := 5 * dice5() + dice5() - 6
IfLess v, 21, Return, (v // 3) + 1
}
}
Distribution check: Total elements = 10000 Margin = 3% --> Lbound = 1386, Ubound = 1471 Bucket 1 contains 1450 elements. Bucket 2 contains 1374 elements. Skewed. Bucket 3 contains 1412 elements. Bucket 4 contains 1465 elements. Bucket 5 contains 1370 elements. Skewed. Bucket 6 contains 1485 elements. Skewed. Bucket 7 contains 1444 elements.
BBC BASIC
MAXRND = 7
FOR r% = 2 TO 5
check% = FNdistcheck(FNdice7, 10^r%, 0.1)
PRINT "Over "; 10^r% " runs dice7 ";
IF check% THEN
PRINT "failed distribution check with "; check% " bin(s) out of range"
ELSE
PRINT "passed distribution check"
ENDIF
NEXT
END
DEF FNdice7
LOCAL x% : x% = FNdice5 + 5*FNdice5
IF x%>26 THEN = FNdice7 ELSE = (x%+1) MOD 7 + 1
DEF FNdice5 = RND(5)
DEF FNdistcheck(RETURN func%, repet%, delta)
LOCAL i%, m%, r%, s%, bins%()
DIM bins%(MAXRND)
FOR i% = 1 TO repet%
r% = FN(^func%)
bins%(r%) += 1
IF r%>m% m% = r%
NEXT
FOR i% = 1 TO m%
IF bins%(i%)/(repet%/m%) > 1+delta s% += 1
IF bins%(i%)/(repet%/m%) < 1-delta s% += 1
NEXT
= s%
- Output:
Over 100 runs dice7 failed distribution check with 4 bin(s) out of range Over 1000 runs dice7 failed distribution check with 2 bin(s) out of range Over 10000 runs dice7 passed distribution check Over 100000 runs dice7 passed distribution check
C
int rand5()
{
int r, rand_max = RAND_MAX - (RAND_MAX % 5);
while ((r = rand()) >= rand_max);
return r / (rand_max / 5) + 1;
}
int rand5_7()
{
int r;
while ((r = rand5() * 5 + rand5()) >= 27);
return r / 3 - 1;
}
int main()
{
printf(check(rand5, 5, 1000000, .05) ? "flat\n" : "not flat\n");
printf(check(rand7, 7, 1000000, .05) ? "flat\n" : "not flat\n");
return 0;
}
- Output:
flat flat
C#
using System;
public class SevenSidedDice
{
Random random = new Random();
static void Main(string[] args)
{
SevenSidedDice sevenDice = new SevenSidedDice();
Console.WriteLine("Random number from 1 to 7: "+ sevenDice.seven());
Console.Read();
}
int seven()
{
int v=21;
while(v>20)
v=five()+five()*5-6;
return 1+v%7;
}
int five()
{
return 1 + random.Next(5);
}
}
C++
This solution tries to minimize calls to the underlying d5 by reusing information from earlier calls.
template<typename F> class fivetoseven
{
public:
fivetoseven(F f): d5(f), rem(0), max(1) {}
int operator()();
private:
F d5;
int rem, max;
};
template<typename F>
int fivetoseven<F>::operator()()
{
while (rem/7 == max/7)
{
while (max < 7)
{
int rand5 = d5()-1;
max *= 5;
rem = 5*rem + rand5;
}
int groups = max / 7;
if (rem >= 7*groups)
{
rem -= 7*groups;
max -= 7*groups;
}
}
int result = rem % 7;
rem /= 7;
max /= 7;
return result+1;
}
int d5()
{
return 5.0*std::rand()/(RAND_MAX + 1.0) + 1;
}
fivetoseven<int(*)()> d7(d5);
int main()
{
srand(time(0));
test_distribution(d5, 1000000, 0.001);
test_distribution(d7, 1000000, 0.001);
}
Clojure
Uses the verify function defined in Verify distribution uniformity/Naive#Clojure
(def dice5 #(rand-int 5))
(defn dice7 []
(quot (->> dice5 ; do the following to dice5
(repeatedly 2) ; call it twice
(apply #(+ %1 (* 5 %2))) ; d1 + 5*d2 => 0..24
#() ; wrap that up in a function
repeatedly ; make infinite sequence of the above
(drop-while #(> % 20)) ; throw away anything > 20
first) ; grab first acceptable element
3)) ; divide by three rounding down
(doseq [n [100 1000 10000] [num count okay?] (verify dice7 n)]
(println "Saw" num count "times:"
(if okay? "that's" " not") "acceptable"))
Saw 0 10 times: not acceptable Saw 1 19 times: not acceptable Saw 2 12 times: not acceptable Saw 3 15 times: that's acceptable Saw 4 11 times: not acceptable Saw 5 11 times: not acceptable Saw 6 22 times: not acceptable Saw 0 142 times: that's acceptable Saw 1 158 times: not acceptable Saw 2 151 times: that's acceptable Saw 3 153 times: that's acceptable Saw 4 118 times: not acceptable Saw 5 139 times: that's acceptable Saw 6 139 times: that's acceptable Saw 0 1498 times: that's acceptable Saw 1 1411 times: that's acceptable Saw 2 1436 times: that's acceptable Saw 3 1434 times: that's acceptable Saw 4 1414 times: that's acceptable Saw 5 1408 times: that's acceptable Saw 6 1399 times: that's acceptable
Common Lisp
(defun d5 ()
(1+ (random 5)))
(defun d7 ()
(loop for d55 = (+ (* 5 (d5)) (d5) -6)
until (< d55 21)
finally (return (1+ (mod d55 7)))))
> (check-distribution 'd7 1000) Distribution potentially skewed for 1: expected around 1000/7 got 153. Distribution potentially skewed for 2: expected around 1000/7 got 119. Distribution potentially skewed for 3: expected around 1000/7 got 125. Distribution potentially skewed for 7: expected around 1000/7 got 156. T #<EQL Hash Table{7} 200B5A53> > (check-distribution 'd7 10000) NIL #<EQL Hash Table{7} 200CB5BB>
D
import std.random;
import verify_distribution_uniformity_naive: distCheck;
/// Generates a random number in [1, 5].
int dice5() /*pure nothrow*/ @safe {
return uniform(1, 6);
}
/// Naive, generates a random number in [1, 7] using dice5.
int fiveToSevenNaive() /*pure nothrow*/ @safe {
immutable int r = dice5() + dice5() * 5 - 6;
return (r < 21) ? (r % 7) + 1 : fiveToSevenNaive();
}
/**
Generates a random number in [1, 7] using dice5,
minimizing calls to dice5.
*/
int fiveToSevenSmart() @safe {
static int rem = 0, max = 1;
while (rem / 7 == max / 7) {
while (max < 7) {
immutable int rand5 = dice5() - 1;
max *= 5;
rem = 5 * rem + rand5;
}
immutable int groups = max / 7;
if (rem >= 7 * groups) {
rem -= 7 * groups;
max -= 7 * groups;
}
}
immutable int result = rem % 7;
rem /= 7;
max /= 7;
return result + 1;
}
void main() /*@safe*/ {
enum int N = 400_000;
distCheck(&dice5, N, 1);
distCheck(&fiveToSevenNaive, N, 1);
distCheck(&fiveToSevenSmart, N, 1);
}
- Output:
1 80365 2 79941 3 80065 4 79784 5 79845 1 57186 2 57201 3 57180 4 57231 5 57124 6 56832 7 57246 1 57367 2 56869 3 57644 4 57111 5 57157 6 56809 7 57043
E
def dice5() {
return entropy.nextInt(5) + 1
}
def dice7() {
var d55 := null
while ((d55 := 5 * dice5() + dice5() - 6) >= 21) {}
return d55 %% 7 + 1
}
def bins := ([0] * 7).diverge()
for x in 1..1000 {
bins[dice7() - 1] += 1
}
println(bins.snapshot())
EasyLang
func dice5 .
return randint 5
.
func dice25 .
return (dice5 - 1) * 5 + dice5
.
func dice7a .
return dice25 mod1 7
.
func dice7b .
repeat
h = dice25
until h <= 21
.
return h mod1 7
.
numfmt 3 0
n = 1000000
len dist[] 7
#
proc checkdist . .
for i to len dist[]
h = dist[i] / n * 7
if abs (h - 1) > 0.01
bad = 1
.
dist[i] = 0
print h
.
if bad = 1
print "-> not uniform"
else
print "-> uniform"
.
.
#
for i to n
dist[dice7a] += 1
.
checkdist
#
print ""
for i to n
dist[dice7b] += 1
.
checkdist
- Output:
1.122 1.118 1.121 1.117 0.840 0.842 0.840 -> not uniform 0.996 1.003 1.001 0.997 1.004 0.998 1.001 -> uniform
Elixir
defmodule Dice do
def dice5, do: :rand.uniform( 5 )
def dice7 do
dice7_from_dice5
end
defp dice7_from_dice5 do
d55 = 5*dice5 + dice5 - 6 # 0..24
if d55 < 21, do: rem( d55, 7 ) + 1,
else: dice7_from_dice5
end
end
fun5 = fn -> Dice.dice5 end
IO.inspect VerifyDistribution.naive( fun5, 1000000, 3 )
fun7 = fn -> Dice.dice7 end
IO.inspect VerifyDistribution.naive( fun7, 1000000, 3 )
- Output:
:ok :ok
Erlang
-module( dice ).
-export( [dice5/0, dice7/0, task/0] ).
dice5() -> random:uniform( 5 ).
dice7() ->
dice7_small_enough( dice5() * 5 + dice5() - 6 ). % 0 - 24
task() ->
verify_distribution_uniformity:naive( fun dice7/0, 1000000, 1 ).
dice7_small_enough( N ) when N < 21 -> N div 3 + 1;
dice7_small_enough( _N ) -> dice7().
- Output:
76> dice:task(). ok
Factor
USING: kernel random sequences assocs locals sorting prettyprint
math math.functions math.statistics math.vectors math.ranges ;
IN: rosetta-code.dice7
! Output a random integer 1..5.
: dice5 ( -- x )
5 [1,b] random
;
! Output a random integer 1..7 using dice5 as randomness source.
: dice7 ( -- x )
0 [ dup 21 < ] [ drop dice5 5 * dice5 + 6 - ] do until
7 rem 1 +
;
! Roll the die by calling the quotation the given number of times and return
! an array with roll results.
! Sample call: 1000 [ dice7 ] roll
: roll ( times quot: ( -- x ) -- array )
[ call( -- x ) ] curry replicate
;
! Input array contains outcomes of a number of die throws. Each die result is
! an integer in the range 1..X. Calculate and return the number of each
! of the results in the array so that in the first position of the result
! there is the number of ones in the input array, in the second position
! of the result there is the number of twos in the input array, etc.
: count-dice-outcomes ( X array -- array )
histogram
swap [1,b] [ over [ 0 or ] change-at ] each
sort-keys values
;
! Verify distribution uniformity/Naive. Delta is the acceptable deviation
! from the ideal number of items in each bucket, expressed as a fraction of
! the total count. Sides is the number of die sides. Die-func is a word that
! produces a random number on stack in the range [1..sides], times is the
! number of times to call it.
! Sample call: 0.02 7 [ dice7 ] 100000 verify
:: verify ( delta sides die-func: ( -- random ) times -- )
sides
times die-func roll
count-dice-outcomes
dup .
times sides / :> ideal-count
ideal-count v-n vabs
times v/n
delta [ < ] curry all?
[ "Random enough" . ] [ "Not random enough" . ] if
;
! Call verify with 1, 10, 100, ... 1000000 rolls of 7-sided die.
: verify-all ( -- )
{ 1 10 100 1000 10000 100000 1000000 }
[| times | 0.02 7 [ dice7 ] times verify ] each
;
- Output:
USE: rosetta-code.dice7 verify-all { 0 0 0 1 0 0 0 } "Not random enough" { 0 2 3 1 1 1 2 } "Not random enough" { 17 12 15 11 13 13 19 } "Not random enough" { 140 130 141 148 143 155 143 } "Random enough" { 1457 1373 1427 1433 1443 1382 1485 } "Random enough" { 14225 14320 14216 14326 14415 14084 14414 } "Random enough" { 142599 141910 142524 143029 143353 142696 143889 } "Random enough"
Forth
require random.fs
: d5 5 random 1+ ;
: discard? 5 = swap 1 > and ;
: d7
begin d5 d5 2dup discard? while 2drop repeat
1- 5 * + 1- 7 mod 1+ ;
- Output:
cr ' d7 1000000 7 1 check-distribution . lower bound = 141429 upper bound = 144285 1 : 143241 ok 2 : 142397 ok 3 : 143522 ok 4 : 142909 ok 5 : 142001 ok 6 : 142844 ok 7 : 143086 ok -1
Fortran
module rand_mod
implicit none
contains
function rand5()
integer :: rand5
real :: r
call random_number(r)
rand5 = 5*r + 1
end function
function rand7()
integer :: rand7
do
rand7 = 5*rand5() + rand5() - 6
if (rand7 < 21) then
rand7 = rand7 / 3 + 1
return
end if
end do
end function
end module
program Randtest
use rand_mod
implicit none
integer, parameter :: samples = 1000000
call distcheck(rand7, samples, 0.005)
write(*,*)
call distcheck(rand7, samples, 0.001)
end program
- Output:
Distribution Uniform Distribution potentially skewed for bucket 1 Expected: 142857 Actual: 143142 Distribution potentially skewed for bucket 2 Expected: 142857 Actual: 143454 Distribution potentially skewed for bucket 3 Expected: 142857 Actual: 143540 Distribution potentially skewed for bucket 4 Expected: 142857 Actual: 142677 Distribution potentially skewed for bucket 5 Expected: 142857 Actual: 142511 Distribution potentially skewed for bucket 6 Expected: 142857 Actual: 142163 Distribution potentially skewed for bucket 7 Expected: 142857 Actual: 142513
FreeBASIC
Function dice5() As Integer
Return Int(Rnd * 5) + 1
End Function
Function dice7() As Integer
Dim As Integer temp
Do
temp = dice5() * 5 + dice5() -6
Loop Until temp < 21
Return (temp Mod 7) +1
End Function
Dim Shared As Ulongint n = 1000000
Print "Testing "; n; " times"
If Not(distCheck(n, 0.05)) Then Print "Test failed" Else Print "Test passed"
Sleep
- Output:
Igual que la entrada de Liberty BASIC.
Go
package main
import (
"fmt"
"math"
"math/rand"
"time"
)
// "given"
func dice5() int {
return rand.Intn(5) + 1
}
// function specified by task "Seven-sided dice from five-sided dice"
func dice7() (i int) {
for {
i = 5*dice5() + dice5()
if i < 27 {
break
}
}
return (i / 3) - 1
}
// function specified by task "Verify distribution uniformity/Naive"
//
// Parameter "f" is expected to return a random integer in the range 1..n.
// (Values out of range will cause an unceremonious crash.)
// "Max" is returned as an "indication of distribution achieved."
// It is the maximum delta observed from the count representing a perfectly
// uniform distribution.
// Also returned is a boolean, true if "max" is less than threshold
// parameter "delta."
func distCheck(f func() int, n int,
repeats int, delta float64) (max float64, flatEnough bool) {
count := make([]int, n)
for i := 0; i < repeats; i++ {
count[f()-1]++
}
expected := float64(repeats) / float64(n)
for _, c := range count {
max = math.Max(max, math.Abs(float64(c)-expected))
}
return max, max < delta
}
// Driver, produces output satisfying both tasks.
func main() {
rand.Seed(time.Now().UnixNano())
const calls = 1000000
max, flatEnough := distCheck(dice7, 7, calls, 500)
fmt.Println("Max delta:", max, "Flat enough:", flatEnough)
max, flatEnough = distCheck(dice7, 7, calls, 500)
fmt.Println("Max delta:", max, "Flat enough:", flatEnough)
}
- Output:
Max delta: 356.1428571428696 Flat enough: true Max delta: 787.8571428571304 Flat enough: false
Groovy
random = new Random()
int rand5() {
random.nextInt(5) + 1
}
int rand7From5() {
def raw = 25
while (raw > 21) {
raw = 5*(rand5() - 1) + rand5()
}
(raw % 7) + 1
}
Test:
def test = {
(1..6). each {
def counts = [0g, 0g, 0g, 0g, 0g, 0g, 0g]
def target = 10g**it
def popSize = 7*target
(0..<(popSize)).each {
def i = rand7From5() - 1
counts[i] = counts[i] + 1g
}
BigDecimal stdDev = (counts.collect { it - target}.collect { it * it }.sum() / popSize) ** 0.5g
def countMap = (0..<counts.size()).inject([:]) { map, index -> map + [(index+1):counts[index]] }
println """\
counts: ${countMap}
population size: ${popSize}
std dev: ${stdDev.round(new java.math.MathContext(3))}
"""
}
}
4.times {
println """
TRIAL #${it+1}
=============="""
test(it)
}
- Output:
TRIAL #1 ============== counts: [1:16, 2:10, 3:9, 4:7, 5:12, 6:8, 7:8] population size: 70 std dev: 0.910 counts: [1:85, 2:97, 3:108, 4:110, 5:95, 6:105, 7:100] population size: 700 std dev: 0.800 counts: [1:990, 2:1008, 3:992, 4:1060, 5:1008, 6:997, 7:945] population size: 7000 std dev: 0.995 counts: [1:9976, 2:10007, 3:10009, 4:9858, 5:10109, 6:9988, 7:10053] population size: 70000 std dev: 0.714 counts: [1:100310, 2:99783, 3:99843, 4:100353, 5:99804, 6:99553, 7:100354] population size: 700000 std dev: 0.968 counts: [1:999320, 2:1000942, 3:1000201, 4:1000878, 5:999181, 6:999632, 7:999846] population size: 7000000 std dev: 0.654 TRIAL #2 ============== counts: [1:10, 2:8, 3:9, 4:9, 5:14, 6:7, 7:13] population size: 70 std dev: 0.756 counts: [1:104, 2:101, 3:97, 4:108, 5:100, 6:87, 7:103] population size: 700 std dev: 0.619 counts: [1:995, 2:970, 3:1001, 4:953, 5:1006, 6:1081, 7:994] population size: 7000 std dev: 1.18 counts: [1:10013, 2:10063, 3:9843, 4:9984, 5:9986, 6:10059, 7:10052] population size: 70000 std dev: 0.711 counts: [1:100048, 2:99647, 3:100240, 4:100683, 5:99813, 6:100320, 7:99249] population size: 700000 std dev: 1.39 counts: [1:1000579, 2:1000541, 3:999497, 4:1000805, 5:999708, 6:999161, 7:999709] population size: 7000000 std dev: 0.586 TRIAL #3 ============== counts: [1:9, 2:8, 3:11, 4:14, 5:10, 6:11, 7:7] population size: 70 std dev: 0.676 counts: [1:100, 2:92, 3:105, 4:107, 5:111, 6:91, 7:94] population size: 700 std dev: 0.733 counts: [1:1010, 2:1053, 3:967, 4:981, 5:1027, 6:959, 7:1003] population size: 7000 std dev: 0.984 counts: [1:9857, 2:10037, 3:9992, 4:10231, 5:9828, 6:10140, 7:9915] population size: 70000 std dev: 1.37 counts: [1:99650, 2:99580, 3:99848, 4:100507, 5:99916, 6:100212, 7:100287] population size: 700000 std dev: 1.01 counts: [1:1001710, 2:999667, 3:1000685, 4:1000411, 5:999369, 6:998469, 7:999689] population size: 7000000 std dev: 0.965 TRIAL #4 ============== counts: [1:12, 2:7, 3:11, 4:12, 5:7, 6:9, 7:12] population size: 70 std dev: 0.676 counts: [1:97, 2:96, 3:101, 4:93, 5:96, 6:124, 7:93] population size: 700 std dev: 1.01 counts: [1:985, 2:1023, 3:1018, 4:1023, 5:995, 6:973, 7:983] population size: 7000 std dev: 0.615 counts: [1:9948, 2:9968, 3:10131, 4:10050, 5:9990, 6:10039, 7:9874] population size: 70000 std dev: 0.764 counts: [1:100125, 2:99616, 3:99912, 4:100286, 5:99674, 6:100190, 7:100197] population size: 700000 std dev: 0.787 counts: [1:1001267, 2:999911, 3:1000602, 4:999483, 5:1000549, 6:998725, 7:999463] population size: 7000000 std dev: 0.798
Haskell
import System.Random
import Data.List
sevenFrom5Dice = do
d51 <- randomRIO(1,5) :: IO Int
d52 <- randomRIO(1,5) :: IO Int
let d7 = 5*d51+d52-6
if d7 > 20 then sevenFrom5Dice
else return $ 1 + d7 `mod` 7
- Output:
*Main> replicateM 10 sevenFrom5Dice
[2,3,1,1,6,2,5,6,5,3]
Test:
*Main> mapM_ print .sort =<< distribCheck sevenFrom5Dice 1000000 3
(1,(142759,True))
(2,(143078,True))
(3,(142706,True))
(4,(142403,True))
(5,(142896,True))
(6,(143028,True))
(7,(143130,True))
Icon and Unicon
Uses verify_uniform
from here.
- Output:
5 142870 2 142812 7 142901 4 142960 1 143113 6 142706 3 142638 uniform
J
The first step is to create 7-sided dice rolls from 5-sided dice rolls (rollD5
):
rollD5=: [: >: ] ?@$ 5: NB. makes a y shape array of 5s, "rolls" the array and increments.
roll2xD5=: [: rollD5 2 ,~ */ NB. rolls D5 twice for each desired D7 roll (y rows, 2 cols)
toBase10=: 5 #. <: NB. decrements and converts rows from base 5 to 10
keepGood=: #~ 21&> NB. compress out values not less than 21
groupin3s=: [: >. >: % 3: NB. increments, divides by 3 and takes ceiling
getD7=: groupin3s@keepGood@toBase10@roll2xD5
Here are a couple of variations on the theme that achieve the same result:
getD7b=: 0 8 -.~ 3 >.@%~ 5 #. [: <:@rollD5 2 ,~ ]
getD7c=: [: (#~ 7&>:) 3 >.@%~ [: 5&#.&.:<:@rollD5 ] , 2:
The trouble is that we probably don't have enough D7 rolls yet because we compressed out any double D5 rolls that evaluated to 21 or more. So we need to accumulate some more D7 rolls until we have enough. J has two types of verb definition - tacit (arguments not referenced) and explicit (more conventional function definitions) illustrated below:
Here's an explicit definition that accumulates rolls from getD7
:
rollD7x=: monad define
n=. */y NB. product of vector y is total number of D7 rolls required
rolls=. '' NB. initialize empty noun rolls
while. n > #rolls do. NB. checks if if enough D7 rolls accumulated
rolls=. rolls, getD7 >. 0.75 * n NB. calcs 3/4 of required rolls and accumulates getD7 rolls
end.
y $ rolls NB. shape the result according to the vector y
)
Here's a tacit definition that does the same thing:
getNumRolls=: [: >. 0.75 * */@[ NB. calc approx 3/4 of the required rolls
accumD7Rolls=: ] , getD7@getNumRolls NB. accumulates getD7 rolls
isNotEnough=: */@[ > #@] NB. checks if enough D7 rolls accumulated
rollD7t=: ] $ (accumD7Rolls ^: isNotEnough ^:_)&''
The verb1 ^: verb2 ^:_
construct repeats x verb1 y
while x verb2 y
is true. It is like saying "Repeat accumD7Rolls while isNotEnough".
Example usage:
rollD7t 10 NB. 10 rolls of D7
6 4 5 1 4 2 4 5 2 5
rollD7t 2 5 NB. 2 by 5 array of D7 rolls
5 1 5 1 3
3 4 3 5 6
rollD7t 2 3 5 NB. 2 by 3 by 5 array of D7 rolls
4 7 7 5 7
3 7 1 4 5
5 4 5 7 6
1 1 7 6 3
4 4 1 4 4
1 1 1 6 5
NB. check results from rollD7x and rollD7t have same shape
($@rollD7x -: $@rollD7t) 10
1
($@rollD7x -: $@rollD7t) 2 3 5
1
Java
import java.util.Random;
public class SevenSidedDice
{
private static final Random rnd = new Random();
public static void main(String[] args)
{
SevenSidedDice now=new SevenSidedDice();
System.out.println("Random number from 1 to 7: "+now.seven());
}
int seven()
{
int v=21;
while(v>20)
v=five()+five()*5-6;
return 1+v%7;
}
int five()
{
return 1+rnd.nextInt(5);
}
}
JavaScript
function dice5()
{
return 1 + Math.floor(5 * Math.random());
}
function dice7()
{
while (true)
{
var dice55 = 5 * dice5() + dice5() - 6;
if (dice55 < 21)
return dice55 % 7 + 1;
}
}
distcheck(dice5, 1000000);
print();
distcheck(dice7, 1000000);
- Output:
1 199792 2 200425 3 199243 4 200407 5 200133 1 143617 2 142209 3 143023 4 142990 5 142894 6 142648 7 142619
jq
Also works with gojq, the Go implementation of jq.
In this entry, the results for both a low-entropy and a high-entropy 5-sided die are shown. The former uses the computer's clock as a not-very-good PRNG, and the latter uses /dev/random in accordance with the following invocation:
#!/bin/bash
< /dev/random tr -cd '0-9' | fold -w 1 | jq -Mcnr -f dice.jq
The results employ a two-tailed χ2-test at the 95% confidence level according to which we are entitled to reject the null hypothesis of uniform randomness if the χ2 statistic is less than 1.69 or greater than 16.013, assuming the number of trials is large enough. See https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
dice.jq
# Output: a PRN in range(0;$n) where $n is .
def prn:
if . == 1 then 0
else . as $n
| (($n-1)|tostring|length) as $w
| [limit($w; inputs)] | join("") | tonumber
| if . < $n then . else ($n | prn) end
end;
# Emit a stream of [value, frequency] pairs
def histogram(stream):
reduce stream as $s ({};
($s|type) as $t
| (if $t == "string" then $s else ($s|tojson) end) as $y
| .[$t][$y][0] = $s
| .[$t][$y][1] += 1 )
| .[][] ;
# sum of squares
def ss(s): reduce s as $x (0; . + ($x * $x));
def chiSquared($expected):
debug # show the actual frequencies
| ss( .[] - $expected ) / $expected;
# The high-entropy 5-sided die
def dice5: 1 + (5|prn);
# The low-entropy 5-sided die
def pseudo_dice5:
def r: (now * 100000 | floor) % 10;
null | until(. and (. < 5); r) | 1 + . ;
# The formal argument dice5 should behave like a 5-sided dice:
def dice7(dice5):
1 + ([limit(7; repeat(dice5))]|add % 7) ;
# Issue a report on the results of a sequence of $n trials using the specified dice
def report(dice; $n):
1.69 as $lower
| 16.013 as $upper
| [histogram( limit($n; repeat(dice)) ) | last]
| chiSquared($n/7) as $x2
| "The χ2 statistic for a trial of \($n) virtual tosses is \($x2).",
"Using a two-sided χ2-test with seven degrees of freedom (\($lower), \($upper)), it is reasonable to conclude that",
(if $x2 < $lower then "this is lower than would be expected for a fair die."
elif $x2 > $upper then "this is higher than would be expected for a fair die."
else "this is consistent with the die being fair."
end) ;
def report($n):
"Low-entropy die results:",
report(dice7(pseudo_dice5); $n),
"",
"High-entropy die results:",
report(dice7(dice5); $n) ;
report(70)
- Output:
Low-entropy die results: ["DEBUG:",[19,14,6,18,7,5,1]] The χ2 statistic for a trial of 70 virtual tosses is 29.2. Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that this is higher than would be expected for a fair die. High-entropy die results: ["DEBUG:",[9,11,9,10,15,11,5]] The χ2 statistic for a trial of 70 virtual tosses is 5.4. Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that this is consistent with the die being fair.
Results for 1,000,000 trials:
Low-entropy die results: ["DEBUG:",[41440,57949,15946,44821,117168,339337,383339]] The χ2 statistic for a trial of 1000000 virtual tosses is 982157.0011039999. Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that this is higher than would be expected for a fair die. High-entropy die results: ["DEBUG:",[142860,143087,142213,142065,143359,143494,142922]] The χ2 statistic for a trial of 1000000 virtual tosses is 12.298347999999999. Using a two-sided χ2-test with seven degrees of freedom (1.69, 16.013), it is reasonable to conclude that this is consistent with the die being fair.
Julia
using Random: seed!
seed!(1234) # for reproducibility
dice5() = rand(1:5)
function dice7()
while true
a = dice5()
b = dice5()
c = a + 5(b - 1)
if c <= 21
return mod1(c, 7)
end
end
end
rolls = (dice7() for i in 1:100000)
roll_counts = Dict{Int,Int}()
for roll in rolls
roll_counts[roll] = get(roll_counts, roll, 0) + 1
end
foreach(println, sort(roll_counts))
Output:
1 => 14530 2 => 13872 3 => 14422 4 => 14425 5 => 14323 6 => 14315 7 => 14113
Kotlin
// 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)
}
Sample output:
Repetitions = 1400000, Expected = 200000 Tolerance = 0.5%, Max Error = 1000 Integer Occurrences Error Acceptable 1 199285 715 Yes 2 200247 247 Yes 3 199709 291 Yes 4 199983 17 Yes 5 199990 10 Yes 6 200664 664 Yes 7 200122 122 Yes Acceptable overall: Yes
Liberty BASIC
n=1000000 '1000000 would take several minutes
print "Testing ";n;" times"
if not(check(n, 0.05)) then print "Test failed" else print "Test passed"
end
'function check(n, delta) is defined at
'http://rosettacode.org/wiki/Verify_distribution_uniformity/Naive#Liberty_BASIC
function GENERATOR()
'GENERATOR = int(rnd(0)*10) '0..9
'GENERATOR = 1+int(rnd(0)*5) '1..5: dice5
'dice7()
do
temp =dice5() *5 +dice5() -6
loop until temp <21
GENERATOR =( temp mod 7) +1
end function
function dice5()
dice5=1+int(rnd(0)*5) '1..5: dice5
end function
- Output:
Testing 1000000 times minVal Expected maxVal 135714 142857 150000 Bucket Counter pass/fail 1 143310 2 143500 3 143040 4 145185 5 140998 6 142610 7 141357 Test passed
Lua
dice5 = function() return math.random(5) end
function dice7()
x = dice5() * 5 + dice5() - 6
if x > 20 then return dice7() end
return x%7 + 1
end
M2000 Interpreter
We make a stack object (is reference type) and pass it as a closure to dice7 lambda function. For each dice7 we pop the top value of stack, and we add a fresh dice5 (random(1,5)) as last value of stack, so stack used as FIFO. Each time z has the sum of 7 random values.
We check for uniform numbers using +-5% from expected value.
Module CheckIt {
Def long i, calls, max, min
s=stack:=random(1,5),random(1,5), random(1,5), random(1,5), random(1,5), random(1,5), random(1,5)
z=0: for i=1 to 7 { z+=stackitem(s, i)}
dice7=lambda z, s -> {
=((z-1) mod 7)+1 : stack s {z-=Number : data random(1,5): z+=Stackitem(7)}
}
Dim count(1 to 7)=0& ' long type
calls=700000
p=0.05
IsUniform=lambda max=calls/7*(1+p), min=calls/7*(1-p) (a)->{
if len(a)=0 then =false : exit
=false
m=each(a)
while m
if array(m)<min or array(m)>max then break
end while
=true
}
For i=1 to calls {count(dice7())++}
max=count()#max()
expected=calls div 7
min=count()#min()
for i=1 to 7
document doc$=format$("{0}{1::-7}",i,count(i))+{
}
Next i
doc$=format$("min={0} expected={1} max={2}", min, expected, max)+{
}+format$("Verify Uniform:{0}", if$(IsUniform(count())->"uniform", "skewed"))+{
}
Print
report doc$
clipboard doc$
}
CheckIt
- Output:
1 9865 2 10109 3 9868 4 9961 5 9936 6 9922 7 10339 min=9865 expected=10000 max=10339 Verify Uniform:uniform 1 100214 2 100336 3 100049 4 99505 5 99951 6 99729 7 100216 min=99505 expected=100000 max=100336 Verify Uniform:uniform
Mathematica/Wolfram Language
sevenFrom5Dice := (tmp$ = 5*RandomInteger[{1, 5}] + RandomInteger[{1, 5}] - 6;
If [tmp$ < 21, 1 + Mod[tmp$ , 7], sevenFrom5Dice])
CheckDistribution[sevenFrom5Dice, 1000000, 5] ->Expected: 142857., Generated :{142206,142590,142650,142693,142730,143475,143656} ->"Flat"
Nim
We use the distribution checker from task Simple Random Distribution Checker.
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)
- Output:
Checking 1000000 values with a tolerance of 0.5%. Random generator passed the uniformity test. Max delta encountered = 552 Allowed delta = 714
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
;;
PARI/GP
dice5()=random(5)+1;
dice7()={
my(t);
while((t=dice5()*5+dice5()) > 21,);
(t+2)\3
};
Pascal
A console application in Free Pascal, created with the Lazarus IDE.
The algorithm suggested in the task description requires on average 50/21 (about 2.38) calls to Dice5 for each call to Dice7. See the link in the VBA solution for a discussion on how to reduce this ratio. It cannot be made less than log_5(7) = 1.209062. The algorithm below is based on Rex Kerr's solution, and requires about 1.2185 calls to Dice5 per call to Dice7. Runtime is about 60% of that for the suggested simple algorithm.
A chi-squared test can be carried out with the help of statistical tables, and is preferred here to an arbitrary "naive" test.
unit UConverter;
(*
Defines a converter object to output uniformly distributed random integers 1..7,
given a source of uniformly distributed random integers 1..5.
*)
interface
type
TFace5 = 1..5;
TFace7 = 1..7;
TDice5 = function() : TFace5;
type TConverter = class( TObject)
private
fDigitBuf: array [0..19] of integer; // holds digits in base 7
fBufCount, fBufPtr : integer;
fDice5 : TDice5; // passed-in generator for integers 1..5
fNrDice5 : int64; // diagnostics, counts calls to fDice5
public
constructor Create( aDice5 : TDice5);
procedure Reset();
function Dice7() : TFace7;
property NrDice5 : int64 read fNrDice5;
end;
implementation
constructor TConverter.Create( aDice5 : TDice5);
begin
inherited Create();
fDice5 := aDice5;
self.Reset();
end;
procedure TConverter.Reset();
begin
fBufCount := 0;
fBufPtr := 0;
fNrDice5 := 0;
end;
function TConverter.Dice7() : TFace7;
var
digit_holder, temp : int64;
j : integer;
begin
if fBufPtr = fBufCount then begin // if no more in buffer
fBufCount := 0;
fBufPtr := 0;
repeat // first time through will usually be enough
// Use supplied fDice5 to generate random 23-digit integer in base 5.
digit_holder := 0;
for j := 0 to 22 do begin
digit_holder := 5*digit_holder + fDice5() - 1;
inc( fNrDice5);
end;
// Convert to 20-digit number in base 7. (A simultaneous DivMod
// procedure would be neater, but isn't available for int64.)
for j := 0 to 19 do begin
temp := digit_holder div 7;
fDigitBuf[j] := digit_holder - 7*temp;
digit_holder := temp;
end;
// Maximum possible is 5^23 - 1, which is 10214646460315315132 in base 7.
// If leading digit in base 7 is 0 then low 19 digits are random.
// Else number begins with 100, 101, or 102; and if with
// 100 or 101 then low 17 digits are random. And so on.
if fDigitBuf[19] = 0 then fBufCount := 19
else if fDigitBuf[17] < 2 then fBufCount := 17
else if fDigitBuf[16] = 0 then fBufCount := 16;
// We could go on but that will do.
until fBufCount > 0;
end; // if no more in buffer
result := fDigitBuf[fBufPtr] + 1;
inc( fBufPtr);
end;
end.
program Dice_SevenFromFive;
(*
Demonstrates use of the UConverter unit.
*)
{$mode objfpc}{$H+}
uses
SysUtils, UConverter;
function Dice5() : UConverter.TFace5;
begin
result := Random(5) + 1; // Random(5) returns 0..4
end;
// Percentage points of the chi-squared distribution, 6 degrees of freedom.
// From New Cambridge Statistical Tables, 2nd edn, pp. 40-41.
const
CHI_SQ_6df_95pc = 1.635;
CHI_SQ_6df_05pc = 12.59;
// Main routine
var
nrThrows, j, k : integer;
nrFaces : array [1..7] of integer;
X2, expected, diff : double;
conv : UConverter.TConverter;
begin
conv := UConverter.TConverter.Create( @Dice5);
WriteLn( 'Enter 0 throws to quit');
repeat
WriteLn(''); Write( 'Number of throws (0 to quit): ');
ReadLn( nrThrows);
if nrThrows = 0 then begin
conv.Free();
exit;
end;
conv.Reset(); // clears count of calls to Dice5
for k := 1 to 7 do nrFaces[k] := 0;
for j := 1 to nrThrows do begin
k := conv.Dice7();
inc( nrFaces[k]);
end;
WriteLn('');
WriteLn( SysUtils.Format( 'Number of throws = %10d', [nrThrows]));
WriteLn( SysUtils.Format( 'Calls to Dice5 = %10d', [conv.NrDice5]));
for k := 1 to 7 do
WriteLn( SysUtils.Format( ' Number of %d''s = %10d', [k, nrFaces[k]]));
// Calculation of chi-squared
expected := nrThrows/7.0;
X2 := 0.0;
for k := 1 to 7 do begin
diff := nrFaces[k] - expected;
X2 := X2 + diff*diff/expected;
end;
WriteLn( SysUtils.Format( 'X^2 = %0.3f on 6 degrees of freedom', [X2]));
if X2 < CHI_SQ_6df_95pc then WriteLn( 'Too regular at 5% level')
else if X2 > CHI_SQ_6df_05pc then WriteLn( 'Too irregular at 5% level')
else WriteLn( 'Satisfactory at 5% level')
until false;
end.
- Output:
Number of throws = 100000000 Calls to Dice5 = 121846341 Number of 1's = 14282807 Number of 2's = 14282277 Number of 3's = 14288393 Number of 4's = 14285486 Number of 5's = 14289379 Number of 6's = 14291053 Number of 7's = 14280605 X^2 = 6.687 on 6 degrees of freedom Satisfactory at 5% level
Perl
Using dice5 twice to generate numbers in the range 0 to 24. If we consider these modulo 8 and re-call if we get zero, we have eliminated 4 cases and created the necessary number in the range from 1 to 7.
sub dice5 { 1+int rand(5) }
sub dice7 {
while(1) {
my $d7 = (5*dice5()+dice5()-6) % 8;
return $d7 if $d7;
}
}
my %count7;
my $n = 1000000;
$count7{dice7()}++ for 1..$n;
printf "%s: %5.2f%%\n", $_, 100*($count7{$_}/$n*7-1) for sort keys %count7;
- Output:
1: 0.05% 2: 0.16% 3: -0.43% 4: 0.11% 5: 0.01% 6: -0.15% 7: 0.24%
Phix
replace rand7() in Verify_distribution_uniformity/Naive#Phix with:
function dice5() return rand(5) end function function dice7() while true do integer r = dice5()*5+dice5()-3 -- ( ie 3..27, but ) if r<24 then return floor(r/3) end if -- (only 3..23 useful) end while end function
- Output:
1000000 iterations: flat
PicoLisp
(de dice5 ()
(rand 1 5) )
(de dice7 ()
(use R
(until (> 21 (setq R (+ (* 5 (dice5)) (dice5) -6))))
(inc (% R 7)) ) )
- Output:
: (let R NIL (do 1000000 (accu 'R (dice7) 1)) (sort R) ) -> ((1 . 142295) (2 . 142491) (3 . 143448) (4 . 143129) (5 . 142701) (6 . 143142) (7 . 142794))
PureBasic
Procedure dice5()
ProcedureReturn Random(4) + 1
EndProcedure
Procedure dice7()
Protected x
x = dice5() * 5 + dice5() - 6
If x > 20
ProcedureReturn dice7()
EndIf
ProcedureReturn x % 7 + 1
EndProcedure
Python
from random import randint
def dice5():
return randint(1, 5)
def dice7():
r = dice5() + dice5() * 5 - 6
return (r % 7) + 1 if r < 21 else dice7()
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}
Quackery
[ 5 random 1+ ] is dice5 ( --> n )
[ dice5 5 *
dice5 + 6 -
[ table
0 0 0 0 1
1 1 2 2 2
3 3 3 4 4
4 5 5 5 6
6 6 7 7 7 ]
dup 0 = iff
drop again ] is dice7 ( --> n )
- Output:
distribution
is defined at Verify distribution uniformity/Naive#Quackery.
/O> ' dice7 1000000 666 distribution ... [ 143196 142815 143451 142716 142964 142300 142558 ] Stack empty.
R
5-sided die.
dice5 <- function(n=1) sample(5, n, replace=TRUE)
Simple but slow 7-sided die, using a while loop.
dice7.while <- function(n=1)
{
score <- numeric()
while(length(score) < n)
{
total <- sum(c(5,1) * dice5(2)) - 3
if(total < 24) score <- c(score, total %/% 3)
}
score
}
system.time(dice7.while(1e6)) # longer than 4 minutes
More complex, but much faster vectorised version.
dice7.vec <- function(n=1, checkLength=TRUE)
{
morethan2n <- 3 * n + 10 + (n %% 2) #need more than 2*n samples, because some are discarded
twoDfive <- matrix(dice5(morethan2n), nrow=2)
total <- colSums(c(5, 1) * twoDfive) - 3
score <- ifelse(total < 24, total %/% 3, NA)
score <- score[!is.na(score)]
#If length is less than n (very unlikely), add some more samples
if(checkLength)
{
while(length(score) < n)
{
score <- c(score, dice7(n, FALSE))
}
score[1:n]
} else score
}
system.time(dice7.vec(1e6)) # ~1 sec
Racket
#lang racket
(define (dice5) (add1 (random 5)))
(define (dice7)
(define res (+ (* 5 (dice5)) (dice5) -6))
(if (< res 21) (+ 1 (modulo res 7)) (dice7)))
Checking the uniformity using math library:
-> (require math/statistics)
-> (samples->hash (for/list ([i 700000]) (dice7)))
'#hash((7 . 100392)
(6 . 100285)
(5 . 99774)
(4 . 100000)
(3 . 100000)
(2 . 99927)
(1 . 99622))
Raku
(formerly Perl 6)
my $d5 = 1..5;
sub d5() { $d5.roll; } # 1d5
sub d7() {
my $flat = 21;
$flat = 5 * d5() - d5() until $flat < 21;
$flat % 7 + 1;
}
# Testing
my @dist;
my $n = 1_000_000;
my $expect = $n / 7;
loop ($_ = $n; $n; --$n) { @dist[d7()]++; }
say "Expect\t",$expect.fmt("%.3f");
for @dist.kv -> $i, $v {
say "$i\t$v\t" ~ (($v - $expect)/$expect*100).fmt("%+.2f%%") if $v;
}
- Output:
Expect 142857.143 1 143088 +0.16% 2 143598 +0.52% 3 141741 -0.78% 4 142832 -0.02% 5 143040 +0.13% 6 142988 +0.09% 7 142713 -0.10%
REXX
/*REXX program simulates a 7─sided die based on a 5─sided throw for a number of trials. */
parse arg trials sample seed . /*obtain optional arguments from the CL*/
if trials=='' | trials="," then trials= 1 /*Not specified? Then use the default.*/
if sample=='' | sample="," then sample= 1000000 /* " " " " " " */
if datatype(seed, 'W') then call random ,,seed /*Integer? Then use it as a RAND seed.*/
L= length(trials) /* [↑] one million samples to be used.*/
do #=1 for trials; die.= 0 /*performs the number of desired trials*/
k= 0
do until k==sample; r= 5 * random(1, 5) + random(1, 5) - 6
if r>20 then iterate
k= k + 1; r= r // 7 + 1; die.r= die.r + 1
end /*until*/
say
expect= sample % 7
say center('trial:' right(#, L) " " sample 'samples, expect' expect, 80, "─")
do j=1 for 7
say ' side' j "had " die.j ' occurrences',
' difference from expected:'right(die.j - expect, length(sample) )
end /*j*/
end /*#*/ /*stick a fork in it, we're all done. */
- output when using the input of: 11
(Shown at five-sixth size.)
──────────────────trial: 1 1000000 samples, expect 142857────────────────── side 1 had 142076 occurrences difference from expected: -781 side 2 had 143053 occurrences difference from expected: 196 side 3 had 142342 occurrences difference from expected: -515 side 4 had 142633 occurrences difference from expected: -224 side 5 had 143024 occurrences difference from expected: 167 side 6 had 143827 occurrences difference from expected: 970 side 7 had 143045 occurrences difference from expected: 188 ──────────────────trial: 2 1000000 samples, expect 142857────────────────── side 1 had 143470 occurrences difference from expected: 613 side 2 had 142998 occurrences difference from expected: 141 side 3 had 142654 occurrences difference from expected: -203 side 4 had 142545 occurrences difference from expected: -312 side 5 had 142452 occurrences difference from expected: -405 side 6 had 143144 occurrences difference from expected: 287 side 7 had 142737 occurrences difference from expected: -120 ──────────────────trial: 3 1000000 samples, expect 142857────────────────── side 1 had 142773 occurrences difference from expected: -84 side 2 had 143198 occurrences difference from expected: 341 side 3 had 142296 occurrences difference from expected: -561 side 4 had 142804 occurrences difference from expected: -53 side 5 had 142897 occurrences difference from expected: 40 side 6 had 142382 occurrences difference from expected: -475 side 7 had 143650 occurrences difference from expected: 793 ──────────────────trial: 4 1000000 samples, expect 142857────────────────── side 1 had 143150 occurrences difference from expected: 293 side 2 had 142635 occurrences difference from expected: -222 side 3 had 142763 occurrences difference from expected: -94 side 4 had 142853 occurrences difference from expected: -4 side 5 had 143132 occurrences difference from expected: 275 side 6 had 142403 occurrences difference from expected: -454 side 7 had 143064 occurrences difference from expected: 207 ──────────────────trial: 5 1000000 samples, expect 142857────────────────── side 1 had 143041 occurrences difference from expected: 184 side 2 had 142701 occurrences difference from expected: -156 side 3 had 143416 occurrences difference from expected: 559 side 4 had 142097 occurrences difference from expected: -760 side 5 had 142451 occurrences difference from expected: -406 side 6 had 143332 occurrences difference from expected: 475 side 7 had 142962 occurrences difference from expected: 105 ──────────────────trial: 6 1000000 samples, expect 142857────────────────── side 1 had 142502 occurrences difference from expected: -355 side 2 had 142429 occurrences difference from expected: -428 side 3 had 143146 occurrences difference from expected: 289 side 4 had 142791 occurrences difference from expected: -66 side 5 had 143271 occurrences difference from expected: 414 side 6 had 143415 occurrences difference from expected: 558 side 7 had 142446 occurrences difference from expected: -411 ──────────────────trial: 7 1000000 samples, expect 142857────────────────── side 1 had 142700 occurrences difference from expected: -157 side 2 had 142691 occurrences difference from expected: -166 side 3 had 143067 occurrences difference from expected: 210 side 4 had 141562 occurrences difference from expected: -1295 side 5 had 143316 occurrences difference from expected: 459 side 6 had 143150 occurrences difference from expected: 293 side 7 had 143514 occurrences difference from expected: 657 ──────────────────trial: 8 1000000 samples, expect 142857────────────────── side 1 had 142362 occurrences difference from expected: -495 side 2 had 143298 occurrences difference from expected: 441 side 3 had 142639 occurrences difference from expected: -218 side 4 had 142811 occurrences difference from expected: -46 side 5 had 143275 occurrences difference from expected: 418 side 6 had 142765 occurrences difference from expected: -92 side 7 had 142850 occurrences difference from expected: -7 ──────────────────trial: 9 1000000 samples, expect 142857────────────────── side 1 had 143508 occurrences difference from expected: 651 side 2 had 142650 occurrences difference from expected: -207 side 3 had 142614 occurrences difference from expected: -243 side 4 had 142916 occurrences difference from expected: 59 side 5 had 142944 occurrences difference from expected: 87 side 6 had 143129 occurrences difference from expected: 272 side 7 had 142239 occurrences difference from expected: -618 ──────────────────trial: 10 1000000 samples, expect 142857────────────────── side 1 had 142455 occurrences difference from expected: -402 side 2 had 143112 occurrences difference from expected: 255 side 3 had 143435 occurrences difference from expected: 578 side 4 had 142704 occurrences difference from expected: -153 side 5 had 142376 occurrences difference from expected: -481 side 6 had 142721 occurrences difference from expected: -136 side 7 had 143197 occurrences difference from expected: 340 ──────────────────trial: 11 1000000 samples, expect 142857────────────────── side 1 had 142967 occurrences difference from expected: 110 side 2 had 142204 occurrences difference from expected: -653 side 3 had 142993 occurrences difference from expected: 136 side 4 had 142797 occurrences difference from expected: -60 side 5 had 143081 occurrences difference from expected: 224 side 6 had 142711 occurrences difference from expected: -146 side 7 had 143247 occurrences difference from expected: 390
Ring
# Project : Seven-sided dice from five-sided dice
for n = 1 to 20
d = dice7()
see "" + d + " "
next
see nl
func dice7()
x = dice5() * 5 + dice5() - 6
if x > 20
return dice7()
ok
dc = x % 7 + 1
return dc
func dice5()
rnd = random(4) + 1
return rnd
Output:
7 6 3 5 2 2 7 1 2 7 3 7 4 4 4 2 3 2 6 1
RPL
UNIF?
is defined at Verify distribution uniformity/Naive
≪ ≪ RAND 5 * CEIL ≫ → dice5
≪ WHILE
dice5 EVAL 5 *
dice5 EVAL 6 - +
DUP 21 ≥
REPEAT DROP END
7 MOD 1 +
≫ ≫ 'DICE7' STO
≪ DICE7 ≫ 100000 .1 UNIF?
- Output:
1: [ 14557 14245 14255 14400 14224 14151 14168 ]
Watchdog timer limits the loop to 100,000 items.
Ruby
Uses distcheck
from here.
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}
- Output:
1 200227 2 200264 3 199777 4 199387 5 200345 1 143175 2 143031 3 142731 4 142716 5 142931 6 142605 7 142811
Scala
- Output:
Best seen running in your browser either by ScalaFiddle (ES aka JavaScript, non JVM) or Scastie (remote JVM).
import scala.util.Random
object SevenSidedDice extends App {
private val rnd = new Random
private def seven = {
var v = 21
def five = 1 + rnd.nextInt(5)
while (v > 20) v = five + five * 5 - 6
1 + v % 7
}
println("Random number from 1 to 7: " + seven)
}
Sidef
func dice5 { 1 + 5.rand.int }
func dice7 {
loop {
var d7 = ((5*dice5() + dice5() - 6) % 8);
d7 && return d7;
}
}
var count7 = Hash.new;
var n = 1e6;
n.times { count7{dice7()} := 0 ++ }
count7.keys.sort.each { |k|
printf("%s: %5.2f%%\n", k, 100*(count7{k}/n * 7 - 1));
}
- Output:
1: -0.00% 2: 0.02% 3: 0.23% 4: 0.42% 5: -0.23% 6: -0.54% 7: 0.10%
Tcl
Any old D&D hand will know these as a D5 and a D7...
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}]
}
}
}
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
VBA
The original StackOverflow page doesn't exist any longer. Luckily archive.org exists.
Private Function Test4DiscreteUniformDistribution(ObservationFrequencies() As Variant, Significance As Single) As Boolean
'Returns true if the observed frequencies pass the Pearson Chi-squared test at the required significance level.
Dim Total As Long, Ei As Long, i As Integer
Dim ChiSquared As Double, DegreesOfFreedom As Integer, p_value As Double
Debug.Print "[1] ""Data set:"" ";
For i = LBound(ObservationFrequencies) To UBound(ObservationFrequencies)
Total = Total + ObservationFrequencies(i)
Debug.Print ObservationFrequencies(i); " ";
Next i
DegreesOfFreedom = UBound(ObservationFrequencies) - LBound(ObservationFrequencies)
'This is exactly the number of different categories minus 1
Ei = Total / (DegreesOfFreedom + 1)
For i = LBound(ObservationFrequencies) To UBound(ObservationFrequencies)
ChiSquared = ChiSquared + (ObservationFrequencies(i) - Ei) ^ 2 / Ei
Next i
p_value = 1 - WorksheetFunction.ChiSq_Dist(ChiSquared, DegreesOfFreedom, True)
Debug.Print
Debug.Print "Chi-squared test for given frequencies"
Debug.Print "X-squared ="; Format(ChiSquared, "0.0000"); ", ";
Debug.Print "df ="; DegreesOfFreedom; ", ";
Debug.Print "p-value = "; Format(p_value, "0.0000")
Test4DiscreteUniformDistribution = p_value > Significance
End Function
Private Function Dice5() As Integer
Dice5 = Int(5 * Rnd + 1)
End Function
Private Function Dice7() As Integer
Dim i As Integer
Do
i = 5 * (Dice5 - 1) + Dice5
Loop While i > 21
Dice7 = i Mod 7 + 1
End Function
Sub TestDice7()
Dim i As Long, roll As Integer
Dim Bins(1 To 7) As Variant
For i = 1 To 1000000
roll = Dice7
Bins(roll) = Bins(roll) + 1
Next i
Debug.Print "[1] ""Uniform? "; Test4DiscreteUniformDistribution(Bins, 0.05); """"
End Sub
- Output:
[1] "Data set:" 142418 142898 142940 142573 143030 143139 143002Chi-squared test for given frequencies X-squared =2.8870, df = 6 , p-value = 0.8229 [1] "Uniform? True"
VBScript
Option Explicit
function dice5
dice5 = int(rnd*5) + 1
end function
function dice7
dim j
do
j = 5 * dice5 + dice5 - 6
loop until j < 21
dice7 = j mod 7 + 1
end function
Verilog
///////////////////////////////////////////////////////////////////////////////
/// seven_sided_dice_tb : (testbench) ///
/// Check the distribution of the output of a seven sided dice circuit ///
///////////////////////////////////////////////////////////////////////////////
module seven_sided_dice_tb;
reg [31:0] freq[0:6];
reg clk;
wire [2:0] dice_face;
reg req;
wire valid_roll;
integer i;
initial begin
clk <= 0;
forever begin
#1;
clk <= ~clk;
end
end
initial begin
req <= 1'b1;
for(i = 0; i < 7; i = i + 1) begin
freq[i] <= 32'b0;
end
repeat(10) @(posedge clk);
repeat(7000000) begin
@(posedge clk);
while(~valid_roll) begin
@(posedge clk);
end
freq[dice_face] <= freq[dice_face] + 32'b1;
end
$display("********************************************");
$display("*** Seven sided dice distribution: ");
$display(" Theoretical distribution is an uniform ");
$display(" distribution with (1/7)-probability ");
$display(" for each possible outcome, ");
$display(" The experimental distribution is: ");
for(i = 0; i < 7; i = i + 1) begin
if(freq[i] < 32'd1_000_000) begin
$display("%d with probability 1/7 - (%d ppm)", i, (32'd1_000_000 - freq[i])/7);
end
else begin
$display("%d with probability 1/7 + (%d ppm)", i, (freq[i] - 32'd1_000_000)/7);
end
end
$finish;
end
seven_sided_dice DUT(
.clk(clk),
.req(req),
.valid_roll(valid_roll),
.dice_face(dice_face)
);
endmodule
///////////////////////////////////////////////////////////////////////////////
/// seven_sided_dice : ///
/// Synthsizeable module that using a 5 sided dice as a black box ///
/// is able to reproduce the outcomes if a 7-sided dice ///
///////////////////////////////////////////////////////////////////////////////
module seven_sided_dice(
input wire clk,
input wire req,
output reg valid_roll,
output reg [2:0] dice_face
);
wire [2:0] face1;
wire [2:0] face2;
reg [4:0] combination;
reg req_p1;
reg req_p2;
reg req_p3;
always @(posedge clk) begin
req_p1 <= req;
req_p2 <= req_p1;
end
always @(posedge clk) begin
if(req_p1) begin
combination <= face1 + face2 + {face2, 2'b00};
end
if(req_p2) begin
case(combination)
5'd0, 5'd1, 5'd2: {valid_roll, dice_face} <= {1'b1, 3'd0};
5'd3, 5'd4, 5'd5: {valid_roll, dice_face} <= {1'b1, 3'd1};
5'd6, 5'd7, 5'd8: {valid_roll, dice_face} <= {1'b1, 3'd2};
5'd9, 5'd10, 5'd11: {valid_roll, dice_face} <= {1'b1, 3'd3};
5'd12, 5'd13, 5'd14: {valid_roll, dice_face} <= {1'b1, 3'd4};
5'd15, 5'd16, 5'd17: {valid_roll, dice_face} <= {1'b1, 3'd5};
5'd18, 5'd19, 5'd20: {valid_roll, dice_face} <= {1'b1, 3'd6};
default: valid_roll <= 1'b0;
endcase
end
end
five_sided_dice dice1(
.clk(clk),
.req(req),
.dice_face(face1)
);
five_sided_dice dice2(
.clk(clk),
.req(req),
.dice_face(face2)
);
endmodule
///////////////////////////////////////////////////////////////////////////////
/// five_sided_dice : ///
/// A model of the five sided dice component ///
///////////////////////////////////////////////////////////////////////////////
module five_sided_dice(
input wire clk,
input wire req,
output reg [2:0] dice_face
);
always @(posedge clk) begin
if(req) begin
dice_face <= $urandom % 5;
end
end
endmodule
Compiling with Icarus Verilog
> iverilog seven-sided-dice.v -o seven-sided-dice
Running the test
> vvp seven-sided-dice ******************************************** *** Seven sided dice distribution: Theoretical distribution is an uniform distribution with (1/7)-probability for each possible outcome, The experimental distribution is: 0 with probability 1/7 + ( 67 ppm) 1 with probability 1/7 - ( 47 ppm) 2 with probability 1/7 + ( 92 ppm) 3 with probability 1/7 - ( 17 ppm) 4 with probability 1/7 - ( 36 ppm) 5 with probability 1/7 + ( 51 ppm) 6 with probability 1/7 - ( 109 ppm)
Wren
import "random" for Random
import "./sort" for Sort
import "./fmt" for Fmt
var r = Random.new()
var dice5 = Fn.new { r.int(1, 6) }
var dice7 = Fn.new {
while (true) {
var t = (dice5.call() - 1) * 5 + dice5.call() - 1
if (t < 21) return 1 + (t/3).floor
}
}
var checkDist = Fn.new { |gen, nRepeats, tolerance|
var occurs = {}
for (i in 1..nRepeats) {
var d = gen.call()
occurs[d] = occurs.containsKey(d) ? occurs[d] + 1 : 1
}
var expected = (nRepeats/occurs.count).floor
var maxError = (expected * tolerance / 100).floor
System.print("Repetitions = %(nRepeats), Expected = %(expected)")
System.print("Tolerance = %(tolerance)\%, Max Error = %(maxError)\n")
System.print("Integer Occurrences Error Acceptable")
var f = " $d $5d $5d $s"
var allAcceptable = true
var cmp = Fn.new { |me1, me2| (me1.key - me2.key).sign }
occurs = occurs.toList
Sort.insertion(occurs, cmp)
for (me in occurs) {
var k = me.key
var v = me.value
var error = (v - expected).abs
var acceptable = (error <= maxError) ? "Yes" : "No"
if (acceptable == "No") allAcceptable = false
Fmt.print(f, k, v, error, acceptable)
}
System.print("\nAcceptable overall: %(allAcceptable ? "Yes" : "No")")
}
checkDist.call(dice7, 1400000, 0.5)
- Output:
Repetitions = 1400000, Expected = 200000 Tolerance = 0.5%, Max Error = 1000 Integer Occurrences Error Acceptable 1 199744 256 Yes 2 199678 322 Yes 3 200254 254 Yes 4 199903 97 Yes 5 200080 80 Yes 6 200070 70 Yes 7 200271 271 Yes Acceptable overall: Yes
zkl
var die5=(1).random.fp(6); // [1..5]
fcn die7{ while((r:=5*die5() + die5())>=27){} r/3-1 }
fcn rtest(N){ //test spread over [0..9]
dist:=L(0,0,0,0,0,0,0,0,0,0);
do(N){ dist[die7()]+=1 }
sum:=dist.sum();
dist=dist.apply('wrap(n){ "%.2f%%".fmt(n.toFloat()/sum*100) }).println();
}
println("Looking for ",100.0/7,"%");
rtest(0d1_000_000);
- Output:
Looking for 14.2857% L("0.00%","14.28%","14.36%","14.22%","14.26%","14.34%","14.33%","14.21%","0.00%","0.00%")
- Programming Tasks
- Probability and statistics
- 11l
- Ada
- ALGOL 68
- AutoHotkey
- BBC BASIC
- C
- C sharp
- C++
- Clojure
- Common Lisp
- D
- E
- E examples needing attention
- Examples needing attention
- EasyLang
- Elixir
- Erlang
- Factor
- Forth
- Fortran
- FreeBASIC
- Go
- Groovy
- Haskell
- Icon
- Unicon
- J
- Java
- JavaScript
- Jq
- Julia
- Kotlin
- Liberty BASIC
- Lua
- M2000 Interpreter
- Mathematica
- Wolfram Language
- Nim
- OCaml
- PARI/GP
- Pascal
- Perl
- Phix
- PicoLisp
- PureBasic
- Python
- Quackery
- R
- Racket
- Raku
- REXX
- Ring
- RPL
- Ruby
- Scala
- Sidef
- Tcl
- VBA
- VBScript
- Verilog
- Wren
- Wren-sort
- Wren-fmt
- Zkl