⚠️ Warning: This is a draft ⚠️
This means it might contain formatting issues,
incorrect code, conceptual problems, or other severe issues.
If you want to help to improve and eventually enable this page,
please fork
RosettaGit's repository
and open a merge request on GitHub.
{{task|Probability and statistics}}
Given a mapping between items and their required probability of occurrence, generate a million items ''randomly'' subject to the given probabilities and compare the target probability of occurrence versus the generated values.
The total of all the probabilities should equal one. (Because floating point arithmetic is involved, this is subject to rounding errors).
Use the following mapping to test your programs:
aleph 1/5.0
beth 1/6.0
gimel 1/7.0
daleth 1/8.0
he 1/9.0
waw 1/10.0
zayin 1/11.0
heth 1759/27720 # adjusted so that probabilities add to 1
;Related task:
- [[Random number generator (device)]]
Ada
with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random;
with Ada.Text_IO; use Ada.Text_IO;
procedure Random_Distribution is
Trials : constant := 1_000_000;
type Outcome is (Aleph, Beth, Gimel, Daleth, He, Waw, Zayin, Heth);
Pr : constant array (Outcome) of Uniformly_Distributed :=
(1.0/5.0, 1.0/6.0, 1.0/7.0, 1.0/8.0, 1.0/9.0, 1.0/10.0, 1.0/11.0, 1.0);
Samples : array (Outcome) of Natural := (others => 0);
Value : Uniformly_Distributed;
Dice : Generator;
begin
for Try in 1..Trials loop
Value := Random (Dice);
for I in Pr'Range loop
if Value <= Pr (I) then
Samples (I) := Samples (I) + 1;
exit;
else
Value := Value - Pr (I);
end if;
end loop;
end loop;
-- Printing the results
for I in Pr'Range loop
Put (Outcome'Image (I) & Character'Val (9));
Put (Float'Image (Float (Samples (I)) / Float (Trials)) & Character'Val (9));
if I = Heth then
Put_Line (" rest");
else
Put_Line (Uniformly_Distributed'Image (Pr (I)));
end if;
end loop;
end Random_Distribution;
Sample output:
ALEPH 2.00167E-01 2.00000E-01
BETH 1.67212E-01 1.66667E-01
GIMEL 1.42290E-01 1.42857E-01
DALETH 1.24186E-01 1.25000E-01
HE 1.11455E-01 1.11111E-01
WAW 1.00325E-01 1.00000E-01
ZAYIN 9.10220E-02 9.09091E-02
HETH 6.33430E-02 rest
ALGOL 68
{{trans|C}}
{{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]}}
{{wont work 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] - due to extensive use of FORMATted transput}}
INT trials = 1 000 000;
MODE LREAL = LONG REAL;
MODE ITEM = STRUCT(
STRING name,
INT prob count,
LREAL expect,
mapping
);
INT col width = 9;
FORMAT real repr = $g(-col width+1, 6)$,
item repr = $"Name: "g", Prob count: "g(0)", Expect: "f(real repr)", Mapping: ", f(real repr)l$;
[8]ITEM items := (
( "aleph", 0, ~, ~ ),
( "beth", 0, ~, ~ ),
( "gimel", 0, ~, ~ ),
( "daleth", 0, ~, ~ ),
( "he", 0, ~, ~ ),
( "waw", 0, ~, ~ ),
( "zayin", 0, ~, ~ ),
( "heth", 0, ~, ~ )
);
main:
(
LREAL offset = 5; # const #
# initialise items #
LREAL total sum := 0;
FOR i FROM LWB items TO UPB items - 1 DO
expect OF items[i] := 1/(i-1+offset);
total sum +:= expect OF items[i]
OD;
expect OF items[UPB items] := 1 - total sum;
mapping OF items[LWB items] := expect OF items[LWB items];
FOR i FROM LWB items + 1 TO UPB items DO
mapping OF items[i] := mapping OF items[i-1] + expect OF items[i]
OD;
# printf((item repr, items)) #
# perform the sampling #
PROC sample = (REF[]LREAL mapping)INT:(
INT out;
LREAL rand real = random;
FOR j FROM LWB items TO UPB items DO
IF rand real < mapping[j] THEN
out := j;
done
FI
OD;
done: out
);
FOR i TO trials DO
prob count OF items[sample(mapping OF items)] +:= 1
OD;
FORMAT indent = $17k$;
# print the results #
printf(($"Trials: "g(0)l$, trials));
printf(($"Items:"$,indent));
FOR i FROM LWB items TO UPB items DO printf(($gn(col width)k" "$, name OF items[i])) OD;
printf(($l"Target prob.:"$, indent, $f(real repr)" "$, expect OF items));
printf(($l"Attained prob.:"$, indent));
FOR i FROM LWB items TO UPB items DO printf(($f(real repr)" "$, prob count OF items[i]/trials)) OD;
printf($l$)
)
Sample output:
Trials: 1000000
Items: aleph beth gimel daleth he waw zayin heth
Target prob.: 0.200000 0.166667 0.142857 0.125000 0.111111 0.100000 0.090909 0.063456
Attained prob.: 0.199987 0.166917 0.142531 0.124203 0.111338 0.099702 0.091660 0.063662
AutoHotkey
contributed by Laszlo on the ahk [http://www.autohotkey.com/forum/post-276279.html#276279 forum]
s1 := "aleph", p1 := 1/5.0 ; Input
s2 := "beth", p2 := 1/6.0
s3 := "gimel", p3 := 1/7.0
s4 := "daleth", p4 := 1/8.0
s5 := "he", p5 := 1/9.0
s6 := "waw", p6 := 1/10.0
s7 := "zayin", p7 := 1/11.0
s8 := "heth", p8 := 1-p1-p2-p3-p4-p5-p6-p7
n := 8, r0 := 0, r%n% := 1 ; auxiliary data
Loop % n-1
i := A_Index-1, r%A_Index% := r%i% + p%A_Index% ; cummulative distribution
Loop 1000000 {
Random R, 0, 1.0
Loop %n% ; linear search
If (R < r%A_Index%) {
c%A_Index%++
Break
}
}
; Output
Loop %n%
t .= s%A_Index% "`t" p%A_Index% "`t" c%A_Index%*1.0e-6 "`n"
Msgbox %t%
/*
output:
---------------------------
aleph 0.200000 0.199960
beth 0.166667 0.166146
gimel 0.142857 0.142624
daleth 0.125000 0.124924
he 0.111111 0.111226
waw 0.100000 0.100434
zayin 0.090909 0.091344
heth 0.063456 0.063342
---------------------------
*/
AWK
#!/usr/bin/awk -f
BEGIN {
ITERATIONS = 1000000
delete symbMap
delete probMap
delete counts
initData();
for (i = 0; i < ITERATIONS; i++) {
distribute(rand())
}
showDistributions()
exit
}
function distribute(rnd, cnt, symNum, sym, symPrb) {
cnt = length(symbMap)
for (symNum = 1; symNum <= cnt; symNum++) {
sym = symbMap[symNum];
symPrb = probMap[sym];
rnd -= symPrb;
if (rnd <= 0) {
counts[sym]++
return;
}
}
}
function showDistributions( s, sym, prb, actSum, expSum, totItr) {
actSum = 0.0
expSum = 0.0
totItr = 0
printf "%-7s %-7s %-5s %-5s\n", "symb", "num.", "act.", "expt."
print "------- ------- ----- -----"
for (s = 1; s <= length(symbMap); s++) {
sym = symbMap[s]
prb = counts[sym]/ITERATIONS
actSum += prb
expSum += probMap[sym]
totItr += counts[sym]
printf "%-7s %7d %1.3f %1.3f\n", sym, counts[sym], prb, probMap[sym]
}
print "------- ------- ----- -----"
printf "Totals: %7d %1.3f %1.3f\n", totItr, actSum, expSum
}
function initData( sym) {
srand()
probMap["aleph"] = 1.0 / 5.0
probMap["beth"] = 1.0 / 6.0
probMap["gimel"] = 1.0 / 7.0
probMap["daleth"] = 1.0 / 8.0
probMap["he"] = 1.0 / 9.0
probMap["waw"] = 1.0 / 10.0
probMap["zyin"] = 1.0 / 11.0
probMap["heth"] = 1759.0 / 27720.0
symbMap[1] = "aleph"
symbMap[2] = "beth"
symbMap[3] = "gimel"
symbMap[4] = "daleth"
symbMap[5] = "he"
symbMap[6] = "waw"
symbMap[7] = "zyin"
symbMap[8] = "heth"
for (sym in probMap)
counts[sym] = 0;
}
Example output:
symb num. act. expt.
aleph 200598 0.201 0.200
beth 166317 0.166 0.167
gimel 142391 0.142 0.143
daleth 125051 0.125 0.125
he 110658 0.111 0.111
waw 100464 0.100 0.100
zyin 90649 0.091 0.091
heth 63872 0.064 0.063
Totals: 1000000 1.000 1.000
Rounding off makes the results look perfect.
BBC BASIC
DIM item$(7), prob(7), cnt%(7)
item$() = "aleph","beth","gimel","daleth","he","waw","zayin","heth"
prob() = 1/5.0, 1/6.0, 1/7.0, 1/8.0, 1/9.0, 1/10.0, 1/11.0, 1759/27720
IF ABS(SUM(prob())-1) > 1E-6 ERROR 100, "Probabilities don't sum to 1"
FOR trial% = 1 TO 1E6
r = RND(1)
p = 0
FOR i% = 0 TO DIM(prob(),1)
p += prob(i%)
IF r < p cnt%(i%) += 1 : EXIT FOR
NEXT
NEXT
@% = &2060A
PRINT "Item actual theoretical"
FOR i% = 0 TO DIM(item$(),1)
PRINT item$(i%), cnt%(i%)/1E6, prob(i%)
NEXT
'''Output:'''
Item actual theoretical
aleph 0.200306 0.200000
beth 0.165963 0.166667
gimel 0.143089 0.142857
daleth 0.125387 0.125000
he 0.111057 0.111111
waw 0.100098 0.100000
zayin 0.091031 0.090909
heth 0.063069 0.063456
C
#include <stdio.h>
#include <stdlib.h>
/* pick a random index from 0 to n-1, according to probablities listed
in p[] which is assumed to have a sum of 1. The values in the probablity
list matters up to the point where the sum goes over 1 */
int rand_idx(double *p, int n)
{
double s = rand() / (RAND_MAX + 1.0);
int i;
for (i = 0; i < n - 1 && (s -= p[i]) >= 0; i++);
return i;
}
#define LEN 8
#define N 1000000
int main()
{
const char *names[LEN] = { "aleph", "beth", "gimel", "daleth",
"he", "waw", "zayin", "heth" };
double s, p[LEN] = { 1./5, 1./6, 1./7, 1./8, 1./9, 1./10, 1./11, 1e300 };
int i, count[LEN] = {0};
for (i = 0; i < N; i++) count[rand_idx(p, LEN)] ++;
printf(" Name Count Ratio Expected\n");
for (i = 0, s = 1; i < LEN; s -= p[i++])
printf("%6s%7d %7.4f%% %7.4f%%\n",
names[i], count[i],
(double)count[i] / N * 100,
((i < LEN - 1) ? p[i] : s) * 100);
return 0;
}
output Name Count Ratio Expected
aleph 199928 19.9928% 20.0000%
beth 166489 16.6489% 16.6667%
gimel 143211 14.3211% 14.2857%
daleth 125257 12.5257% 12.5000%
he 110849 11.0849% 11.1111%
waw 99935 9.9935% 10.0000%
zayin 91001 9.1001% 9.0909%
heth 63330 6.3330% 6.3456%
## C++
```cpp
#include <cstdlib>
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <ctime>
#include <iomanip>
int main( ) {
typedef std::vector<std::pair<std::string, double> >::const_iterator SPI ;
typedef std::vector<std::pair<std::string , double> > ProbType ;
ProbType probabilities ;
probabilities.push_back( std::make_pair( "aleph" , 1/5.0 ) ) ;
probabilities.push_back( std::make_pair( "beth" , 1/6.0 ) ) ;
probabilities.push_back( std::make_pair( "gimel" , 1/7.0 ) ) ;
probabilities.push_back( std::make_pair( "daleth" , 1/8.0 ) ) ;
probabilities.push_back( std::make_pair( "he" , 1/9.0 ) ) ;
probabilities.push_back( std::make_pair( "waw" , 1/10.0 ) ) ;
probabilities.push_back( std::make_pair( "zayin" , 1/11.0 ) ) ;
probabilities.push_back( std::make_pair( "heth" , 1759/27720.0 ) ) ;
std::vector<std::string> generated ; //for the strings that are generatod
std::vector<int> decider ; //holds the numbers that determine the choice of letters
for ( int i = 0 ; i < probabilities.size( ) ; i++ ) {
if ( i == 0 ) {
decider.push_back( 27720 * (probabilities[ i ].second) ) ;
}
else {
int number = 0 ;
for ( int j = 0 ; j < i ; j++ ) {
number += 27720 * ( probabilities[ j ].second ) ;
}
number += 27720 * probabilities[ i ].second ;
decider.push_back( number ) ;
}
}
srand( time( 0 ) ) ;
for ( int i = 0 ; i < 1000000 ; i++ ) {
int randnumber = rand( ) % 27721 ;
int j = 0 ;
while ( randnumber > decider[ j ] )
j++ ;
generated.push_back( ( probabilities[ j ]).first ) ;
}
std::cout << "letter frequency attained frequency expected\n" ;
for ( SPI i = probabilities.begin( ) ; i != probabilities.end( ) ; i++ ) {
std::cout << std::left << std::setw( 8 ) << i->first ;
int found = std::count ( generated.begin( ) , generated.end( ) , i->first ) ;
std::cout << std::left << std::setw( 21 ) << found / 1000000.0 ;
std::cout << std::left << std::setw( 17 ) << i->second << '\n' ;
}
return 0 ;
}
Output:
letter frequency attained frequency expected
aleph 0.200089 0.2
beth 0.16695 0.166667
gimel 0.142693 0.142857
daleth 0.124859 0.125
he 0.111258 0.111111
waw 0.099665 0.1
zayin 0.090654 0.0909091
heth 0.063832 0.063456
## C#
{{trans|Java}}
```c#
using System;
class Program
{
static long TRIALS = 1000000L;
private class Expv
{
public string name;
public int probcount;
public double expect;
public double mapping;
public Expv(string name, int probcount, double expect, double mapping)
{
this.name = name;
this.probcount = probcount;
this.expect = expect;
this.mapping = mapping;
}
}
static Expv[] items = {
new Expv("aleph", 0, 0.0, 0.0), new Expv("beth", 0, 0.0, 0.0),
new Expv("gimel", 0, 0.0, 0.0), new Expv("daleth", 0, 0.0, 0.0),
new Expv("he", 0, 0.0, 0.0), new Expv("waw", 0, 0.0, 0.0),
new Expv("zayin", 0, 0.0, 0.0), new Expv("heth", 0, 0.0, 0.0)
};
static void Main(string[] args)
{
double rnum, tsum = 0.0;
Random random = new Random();
for (int i = 0, rnum = 5.0; i < 7; i++, rnum += 1.0)
{
items[i].expect = 1.0 / rnum;
tsum += items[i].expect;
}
items[7].expect = 1.0 - tsum;
items[0].mapping = 1.0 / 5.0;
for (int i = 1; i < 7; i++)
items[i].mapping = items[i - 1].mapping + 1.0 / ((double)i + 5.0);
items[7].mapping = 1.0;
for (int i = 0; i < TRIALS; i++)
{
rnum = random.NextDouble();
for (int j = 0; j < 8; j++)
if (rnum < items[j].mapping)
{
items[j].probcount++;
break;
}
}
Console.WriteLine("Trials: {0}", TRIALS);
Console.Write("Items: ");
for (int i = 0; i < 8; i++)
Console.Write(items[i].name.PadRight(9));
Console.WriteLine();
Console.Write("Target prob.: ");
for (int i = 0; i < 8; i++)
Console.Write("{0:0.000000} ", items[i].expect);
Console.WriteLine();
Console.Write("Attained prob.: ");
for (int i = 0; i < 8; i++)
Console.Write("{0:0.000000} ", (double)items[i].probcount / (double)TRIALS);
Console.WriteLine();
}
}
```
Output:
```txt
Trials: 1000000
Items: aleph beth gimel daleth he waw zayin heth
Target prob.: 0.200000 0.166667 0.142857 0.125000 0.111111 0.100000 0.090909 0.063456
Attained prob.: 0.199975 0.166460 0.142290 0.125510 0.111374 0.100018 0.090746 0.063627
```
## Clojure
Works by first converting the provided Probability Distribution Function into a Cumulative Distribution Function, so that it can simply scan through the CDF list and return the current item as soon as the CDF at that point is greater than the random number generated. The code could be made more concise by skipping this step and instead tracking the whole PDF for each random number; but this code is both faster and more readable.
It uses the language built-in (frequencies) to count the number of occurrences of each distinct name. Note that while we actually generate a sequence of num-trials random samples, the sequence is lazily generated and lazily consumed. This means that the program will scale to an arbitrarily-large num-trials with no ill effects, by throwing away elements it's already processed.
```Clojure
(defn to-cdf [pdf]
(reduce
(fn [acc n] (conj acc (+ (or (last acc) 0) n)))
[]
pdf))
(defn choose [cdf]
(let [r (rand)]
(count
(filter (partial > r) cdf))))
(def *names* '[aleph beth gimel daleth he waw zayin heth])
(def *pdf* (map double [1/5 1/6 1/7 1/8 1/9 1/10 1/11 1759/27720]))
(let [num-trials 1000000
cdf (to-cdf *pdf*)
indexes (range (count *names*)) ;; use integer key internally, not name
expected (into (sorted-map) (zipmap indexes *pdf*))
actual (frequencies (repeatedly num-trials #(choose cdf)))]
(doseq [[idx exp] expected]
(println "Expected number of" (*names* idx) "was"
(* num-trials exp) "and actually got" (actual idx))))
```
```txt
Expected number of aleph was 200000.0 and actually got 199300
Expected number of beth was 166666.66666666672 and actually got 166291
Expected number of gimel was 142857.1428571429 and actually got 143297
Expected number of daleth was 125000.0 and actually got 125032
Expected number of he was 111111.11111111111 and actually got 111540
Expected number of waw was 100000.0 and actually got 100062
Expected number of zayin was 90909.09090909091 and actually got 90719
Expected number of heth was 63455.98845598846 and actually got 63759
```
## Common Lisp
This is a straightforward, if a little verbose implementation based upon the Perl one.
```lisp
(defvar *probabilities* '((aleph 1/5)
(beth 1/6)
(gimel 1/7)
(daleth 1/8)
(he 1/9)
(waw 1/10)
(zayin 1/11)
(heth 1759/27720)))
(defun calculate-probabilities (choices &key (repetitions 1000000))
(assert (= 1 (reduce #'+ choices :key #'second)))
(labels ((make-ranges ()
(loop for (datum probability) in choices
sum (coerce probability 'double-float) into total
collect (list datum total)))
(pick (ranges)
(declare (optimize (speed 3) (safety 0) (debug 0)))
(loop with random = (random 1.0d0)
for (datum below) of-type (t double-float) in ranges
when (< random below)
do (return datum)))
(populate-hash (ranges)
(declare (optimize (speed 3) (safety 0) (debug 0)))
(loop repeat (the fixnum repetitions)
with hash = (make-hash-table)
do (incf (the fixnum (gethash (pick ranges) hash 0)))
finally (return hash)))
(make-table-data (hash)
(loop for (datum probability) in choices
collect (list datum
(float (/ (gethash datum hash)
repetitions))
(float probability)))))
(format t "Datum~10,2TOccured~20,2TExpected~%")
(format t "~{~{~A~10,2T~F~20,2T~F~}~%~}"
(make-table-data (populate-hash (make-ranges))))))
CL-USER> (calculate-probabilities *probabilities*)
Datum Occured Expected
ALEPH 0.200156 0.2
BETH 0.166521 0.16666667
GIMEL 0.142936 0.14285715
DALETH 0.124779 0.125
HE 0.111601 0.11111111
WAW 0.100068 0.1
ZAYIN 0.090458 0.09090909
HETH 0.063481 0.06345599
```
## D
### Basic Version
```d
void main() {
import std.stdio, std.random, std.string, std.range;
enum int nTrials = 1_000_000;
const items = "aleph beth gimel daleth he waw zayin heth".split;
const pr = [1/5., 1/6., 1/7., 1/8., 1/9., 1/10., 1/11., 1759/27720.];
double[pr.length] counts = 0.0;
foreach (immutable _; 0 .. nTrials)
counts[pr.dice]++;
writeln("Item Target prob Attained prob");
foreach (name, p, co; zip(items, pr, counts[]))
writefln("%-7s %.8f %.8f", name, p, co / nTrials);
}
```
{{out}}
```txt
Item Target prob Attained prob
aleph 0.20000000 0.19964000
beth 0.16666667 0.16753600
gimel 0.14285714 0.14283300
daleth 0.12500000 0.12515400
he 0.11111111 0.11074300
waw 0.10000000 0.10025800
zayin 0.09090909 0.09070400
heth 0.06345598 0.06313200
```
### A Faster Version
```d
void main() {
import std.stdio, std.random, std.algorithm, std.range;
enum int nTrials = 1_000_000;
const items = "aleph beth gimel daleth he waw zayin heth".split;
const pr = [1/5., 1/6., 1/7., 1/8., 1/9., 1/10., 1/11., 1759/27720.];
double[pr.length] cumulatives = pr[];
foreach (immutable i, ref c; cumulatives[1 .. $ - 1])
c += cumulatives[i];
cumulatives[$ - 1] = 1.0;
double[pr.length] counts = 0.0;
auto rnd = Xorshift(unpredictableSeed);
foreach (immutable _; 0 .. nTrials)
counts[cumulatives[].countUntil!(c => c >= rnd.uniform01)]++;
writeln("Item Target prob Attained prob");
foreach (name, p, co; zip(items, pr, counts[]))
writefln("%-7s %.8f %.8f", name, p, co / nTrials);
}
```
## E
This implementation converts the list of probabilities to sub-intervals of [0.0,1.0), then arranges those intervals in a binary tree for searching based on a random number input.
It is rather verbose, due to using the tree rather than a linear search, and having code to print the tree (which was used to debug it).
```e
pragma.syntax("0.9")
```
First, the algorithm:
```e
/** Makes leaves of the binary tree */
def leaf(value) {
return def leaf {
to run(_) { return value }
to __printOn(out) { out.print("=> ", value) }
}
}
/** Makes branches of the binary tree */
def split(leastRight, left, right) {
return def tree {
to run(specimen) {
return if (specimen < leastRight) {
left(specimen)
} else {
right(specimen)
}
}
to __printOn(out) {
out.print(" ")
out.indent().print(left)
out.lnPrint("< ")
out.print(leastRight)
out.indent().lnPrint(right)
}
}
}
def makeIntervalTree(assocs :List[Tuple[any, float64]]) {
def size :int := assocs.size()
if (size > 1) {
def midpoint := size // 2
return split(assocs[midpoint][1], makeIntervalTree(assocs.run(0, midpoint)),
makeIntervalTree(assocs.run(midpoint)))
} else {
def [[value, _]] := assocs
return leaf(value)
}
}
def setupProbabilisticChoice(entropy, table :Map[any, float64]) {
var cumulative := 0.0
var intervalTable := []
for value => probability in table {
intervalTable with= [value, cumulative]
cumulative += probability
}
def total := cumulative
def selector := makeIntervalTree(intervalTable)
return def probChoice {
# Multiplying by the total helps correct for any error in the sum of the inputs
to run() { return selector(entropy.nextDouble() * total) }
to __printOn(out) {
out.print("Probabilistic choice using tree:")
out.indent().lnPrint(selector)
}
}
}
```
Then the test setup:
```e
def rosetta := setupProbabilisticChoice(entropy, def probTable := [
"aleph" => 1/5,
"beth" => 1/6.0,
"gimel" => 1/7.0,
"daleth" => 1/8.0,
"he" => 1/9.0,
"waw" => 1/10.0,
"zayin" => 1/11.0,
"heth" => 0.063455988455988432,
])
var trials := 1000000
var timesFound := [].asMap()
for i in 1..trials {
if (i % 1000 == 0) { print(`${i//1000} `) }
def value := rosetta()
timesFound with= (value, timesFound.fetch(value, fn { 0 }) + 1)
}
stdout.println()
for item in probTable.domain() {
stdout.print(item, "\t", timesFound[item] / trials, "\t", probTable[item], "\n")
}
```
## Elixir
{{trans|Erlang}}
```elixir
defmodule Probabilistic do
@tries 1000000
@probs [aleph: 1/5,
beth: 1/6,
gimel: 1/7,
daleth: 1/8,
he: 1/9,
waw: 1/10,
zayin: 1/11,
heth: 1759/27720]
def test do
trials = for _ <- 1..@tries, do: get_choice(@probs, :rand.uniform)
IO.puts "Item Expected Actual"
fmt = " ~-8s ~.6f ~.6f~n"
Enum.each(@probs, fn {glyph,expected} ->
actual = length(for ^glyph <- trials, do: glyph) / @tries
:io.format fmt, [glyph, expected, actual]
end)
end
defp get_choice([{glyph,_}], _), do: glyph
defp get_choice([{glyph,prob}|_], ran) when ran < prob, do: glyph
defp get_choice([{_,prob}|t], ran), do: get_choice(t, ran - prob)
end
Probabilistic.test
```
{{out}}
```txt
Item Expected Actual
aleph 0.200000 0.200676
beth 0.166667 0.166103
gimel 0.142857 0.142543
daleth 0.125000 0.125055
he 0.111111 0.111165
waw 0.100000 0.100439
zayin 0.090909 0.090894
heth 0.063456 0.063125
```
## Erlang
{{trans|Java}}
The optimized version of Java.
```erlang
-module(probabilistic_choice).
-export([test/0]).
-define(TRIES, 1000000).
test() ->
Probs =
[{aleph,1/5},
{beth,1/6},
{gimel,1/7},
{daleth,1/8},
{he,1/9},
{waw,1/10},
{zayin,1/11},
{heth,1759/27720}],
random:seed(now()),
Trials =
[get_choice(Probs,random:uniform()) || _ <- lists:seq(1,?TRIES)],
[{Glyph,Expected,(length([Glyph || Glyph_ <- Trials, Glyph_ == Glyph])/?TRIES)}
|| {Glyph,Expected} <- Probs].
get_choice([{Glyph,_}],_) ->
Glyph;
get_choice([{Glyph,Prob}|T],Ran) ->
case (Ran < Prob) of
true ->
Glyph;
false ->
get_choice(T,Ran - Prob)
end.
```
Output:
```txt
[{aleph,0.2,0.200325},
{beth,0.16666666666666666,0.167108},
{gimel,0.14285714285714285,0.142246},
{daleth,0.125,0.124851},
{he,0.1111111111111111,0.111345},
{waw,0.1,0.099912},
{zayin,0.09090909090909091,0.091352},
{heth,0.06345598845598846,0.062861}]
```
## ERRE
```ERRE
PROGRAM PROB_CHOICE
DIM ITEM$[7],PROB[7],CNT[7]
BEGIN
ITEM$[]=("aleph","beth","gimel","daleth","he","waw","zayin","heth")
PROB[0]=1/5.0 PROB[1]=1/6.0 PROB[2]=1/7.0 PROB[3]=1/8.0
PROB[4]=1/9.0 PROB[5]=1/10.0 PROB[6]=1/11.0 PROB[7]=1759/27720
SUM=0
FOR I%=0 TO UBOUND(PROB,1) DO
SUM=SUM+PROB[I%]
END FOR
IF ABS(SUM-1)>1E-6 THEN
PRINT("Probabilities don't sum to 1")
ELSE
FOR TRIAL=1 TO 1E6 DO
R=RND(1)
P=0
FOR I%=0 TO UBOUND(PROB,1) DO
P+=PROB[I%]
IF RUSE: rosettacode.proba
1000000 data example
```
outputs
Key Value expected
heth: 0.063469 0.063456
waw: 0.100226 0.100000
daleth: 0.125844 0.125000
beth: 0.166264 0.166667
zayin: 0.090806 0.090909
he: 0.110562 0.111111
aleph: 0.199868 0.200000
gimel: 0.142961 0.142857
```
## Forth
```forth
include random.fs
\ common factors of desired probabilities (1/5 .. 1/11)
2 2 * 2 * 3 * 3 * 5 * 7 * 11 * constant denom \ 27720
\ represent each probability as the numerator with 27720 as the denominator
: ,numerators ( max min -- )
do denom i / , loop ;
\ final item is 27720 - sum(probs)
: ,remainder ( denom addr len -- )
cells bounds do i @ - 1 cells +loop , ;
create probs 12 5 ,numerators denom probs 7 ,remainder
create bins 8 cells allot
: choose ( -- 0..7 )
denom random
8 0 do
probs i cells + @ -
dup 0< if drop i unloop exit then
loop
abort" can't get here" ;
: trials ( n -- )
0 do 1 bins choose cells + +! loop ;
: str-table
create ( c-str ... n -- ) 0 do , loop
does> ( n -- str len ) swap cells + @ count ;
here ," heth" here ," zayin" here ," waw" here ," he"
here ," daleth" here ," gimel" here ," beth" here ," aleph"
8 str-table names
: .header
cr ." Name" #tab emit ." Prob" #tab emit ." Actual" #tab emit ." Error" ;
: .result ( n -- )
cr dup names type #tab emit
dup cells probs + @ s>f denom s>f f/ fdup f. #tab emit
dup cells bins + @ s>f 1e6 f/ fdup f. #tab emit
f- fabs fs. ;
: .results .header 8 0 do i .result loop ;
```
```txt
bins 8 cells erase
3 set-precision
1000000 trials .results
Name Prob Actual Error
aleph 0.2 0.2 9.90E-5
beth 0.167 0.167 4.51E-4
gimel 0.143 0.142 4.99E-4
daleth 0.125 0.125 1.82E-4
he 0.111 0.111 2.10E-4
waw 0.1 0.1 3.30E-5
zayin 0.0909 0.0912 2.77E-4
heth 0.0635 0.0636 9.70E-5 ok
```
## Fortran
{{works with|Fortran|90 and later}}
```fortran
PROGRAM PROBS
IMPLICIT NONE
INTEGER, PARAMETER :: trials = 1000000
INTEGER :: i, j, probcount(8) = 0
REAL :: expected(8), mapping(8), rnum
CHARACTER(6) :: items(8) = (/ "aleph ", "beth ", "gimel ", "daleth", "he ", "waw ", "zayin ", "heth " /)
expected(1:7) = (/ (1.0/i, i=5,11) /)
expected(8) = 1.0 - SUM(expected(1:7))
mapping(1) = 1.0 / 5.0
DO i = 2, 7
mapping(i) = mapping(i-1) + 1.0/(i+4.0)
END DO
mapping(8) = 1.0
DO i = 1, trials
CALL RANDOM_NUMBER(rnum)
DO j = 1, 8
IF (rnum < mapping(j)) THEN
probcount(j) = probcount(j) + 1
EXIT
END IF
END DO
END DO
WRITE(*, "(A,I10)") "Trials: ", trials
WRITE(*, "(A,8A10)") "Items: ", items
WRITE(*, "(A,8F10.6)") "Target Probability: ", expected
WRITE(*, "(A,8F10.6)") "Attained Probability:", REAL(probcount) / REAL(trials)
ENDPROGRAM PROBS
```
Sample Output:
```txt
Trials: 1000000
Items: aleph beth gimel daleth he waw zayin heth
Target Probability: 0.200000 0.166667 0.142857 0.125000 0.111111 0.100000 0.090909 0.063456
Attained Probability: 0.199631 0.166907 0.142488 0.124920 0.110906 0.099943 0.091775 0.063430
```
## FreeBASIC
```freebasic
' FB 1.05.0 Win64
Dim letters (0 To 7) As String = {"aleph", "beth", "gimel", "daleth", "he", "waw", "zayin", "heth"}
Dim actual (0 To 7) As Integer '' all zero by default
Dim probs (0 To 7) As Double = {1/5.0, 1/6.0, 1/7.0, 1/8.0, 1/9.0, 1/10.0, 1/11.0}
Dim cumProbs (0 To 7) As Double
cumProbs(0) = probs(0)
For i As Integer = 1 To 6
cumProbs(i) = cumProbs(i - 1) + probs(i)
Next
cumProbs(7) = 1.0
probs(7) = 1.0 - cumProbs(6)
Randomize
Dim rand As Double
Dim n As Double = 1000000
Dim sum As Double = 0.0
For i As Integer = 1 To n
rand = Rnd '' random number where 0 <= rand < 1
Select case rand
Case Is <= cumProbs(0)
actual(0) += 1
Case Is <= cumProbs(1)
actual(1) += 1
Case Is <= cumProbs(2)
actual(2) += 1
Case Is <= cumProbs(3)
actual(3) += 1
Case Is <= cumProbs(4)
actual(4) += 1
Case Is <= cumProbs(5)
actual(5) += 1
Case Is <= cumProbs(6)
actual(6) += 1
Case Else
actual(7) += 1
End Select
Next
Dim sumActual As Double = 0
Print "Letter", " Actual", "Expected"
Print "------", "--------", "--------"
For i As Integer = 0 To 7
Print letters(i),
Print Using "#.######"; actual(i)/n;
sumActual += actual(i)/n
Print , Using "#.######"; probs(i)
Next
Print , "--------", "--------"
Print , Using "#.######"; sumActual;
Print , Using "#.######"; 1.000000
Print
Print "Press any key to quit"
Sleep
```
{{out}}
```txt
Letter Actual Expected
------ -------- --------
aleph 0.199987 0.200000
beth 0.166663 0.166667
gimel 0.143134 0.142857
daleth 0.125132 0.125000
he 0.110772 0.111111
waw 0.100236 0.100000
zayin 0.090664 0.090909
heth 0.063412 0.063456
-------- --------
1.000000 1.000000
```
## Go
```go
package main
import (
"fmt"
"math/rand"
"time"
)
type mapping struct {
item string
pr float64
}
func main() {
// input mapping
m := []mapping{
{"aleph", 1 / 5.},
{"beth", 1 / 6.},
{"gimel", 1 / 7.},
{"daleth", 1 / 8.},
{"he", 1 / 9.},
{"waw", 1 / 10.},
{"zayin", 1 / 11.},
{"heth", 1759 / 27720.}} // adjusted so that probabilities add to 1
// cumulative probability
cpr := make([]float64, len(m)-1)
var c float64
for i := 0; i < len(m)-1; i++ {
c += m[i].pr
cpr[i] = c
}
// generate
const samples = 1e6
occ := make([]int, len(m))
rand.Seed(time.Now().UnixNano())
for i := 0; i < samples; i++ {
r := rand.Float64()
for j := 0; ; j++ {
if r < cpr[j] {
occ[j]++
break
}
if j == len(cpr)-1 {
occ[len(cpr)]++
break
}
}
}
// report
fmt.Println(" Item Target Generated")
var totalTarget, totalGenerated float64
for i := 0; i < len(m); i++ {
target := m[i].pr
generated := float64(occ[i]) / samples
fmt.Printf("%6s %8.6f %8.6f\n", m[i].item, target, generated)
totalTarget += target
totalGenerated += generated
}
fmt.Printf("Totals %8.6f %8.6f\n", totalTarget, totalGenerated)
}
```
Output:
```txt
Item Target Generated
aleph 0.200000 0.199509
beth 0.166667 0.167194
gimel 0.142857 0.143293
daleth 0.125000 0.124869
he 0.111111 0.110896
waw 0.100000 0.099849
zayin 0.090909 0.090789
heth 0.063456 0.063601
Totals 1.000000 1.000000
```
## Haskell
```haskell
import System.Random (newStdGen, randomRs)
dataBinCounts :: [Float] -> [Float] -> [Int]
dataBinCounts thresholds range =
let sampleSize = length range
xs = ((-) sampleSize . length . flip filter range . (<)) <$> thresholds
in zipWith (-) (xs ++ [sampleSize]) (0 : xs)
main :: IO ()
main = do
g <- newStdGen
let fractions = recip <$> [5 .. 11] :: [Float]
expected = fractions ++ [1 - sum fractions]
actual =
((/ 1000000.0) . fromIntegral) <$>
dataBinCounts (scanl1 (+) expected) (take 1000000 (randomRs (0, 1) g))
piv n = take n . (++ repeat ' ')
putStrLn " expected actual"
mapM_ putStrLn $
zipWith3
(\l s c -> piv 7 l ++ piv 13 (show s) ++ piv 12 (show c))
["aleph", "beth", "gimel", "daleth", "he", "waw", "zayin", "heth"]
expected
actual
```
{{Out}}
Sample
```txt
expected actual
aleph 0.2 0.200597
beth 0.16666667 0.167192
gimel 0.14285715 0.142781
daleth 0.125 0.124556
he 0.11111111 0.111128
waw 0.1 9.9671e-2
zayin 9.090909e-2 9.0294e-2
heth 6.345594e-2 6.3781e-2
```
## HicEst
```HicEst
REAL :: trials=1E6, n=8, map(n), limit(n), expected(n), outcome(n)
expected = 1 / ($ + 4)
expected(n) = 1 - SUM(expected) + expected(n)
map = expected
map = map($) + map($-1)
DO i = 1, trials
random = RAN(1)
limit = random > map
item = INDEX(limit, 0)
outcome(item) = outcome(item) + 1
ENDDO
outcome = outcome / trials
DLG(Text=expected, Text=outcome, Y=0)
```
Exported from the spreadsheet-like DLG function:
```txt
0.2 0.199908
0.1666667 0.166169
0.1428571 0.142722
0.125 0.124929
0.1111111 0.111706
0.1 0.099863
0.0909091 0.090965
0.063456 0.063738
```
=={{header|Icon}} and {{header|Unicon}}==
```Icon
record Item(value, probability)
procedure find_item (items, v)
sum := 0.0
every item := !items do {
if v < sum+item.probability
then return item.value
else sum +:= item.probability
}
fail # v exceeded 1.0
end
# -- helper procedures
# count the number of occurrences of i in list l,
# assuming the items are strings
procedure count (l, i)
result := 0.0
every x := !l do
if x == i then result +:= 1
return result
end
procedure rand_float ()
return ?1000/1000.0
end
# -- test the procedure
procedure main ()
items := [
Item("aleph", 1/5.0),
Item("beth", 1/6.0),
Item("gimel", 1/7.0),
Item("daleth", 1/8.0),
Item("he", 1/9.0),
Item("waw", 1/10.0),
Item("zayin", 1/11.0),
Item("heth", 1759/27720.0)
]
# collect a sample of results
sample := []
every (1 to 1000000) do push (sample, find_item(items, rand_float ()))
# return comparison of expected vs actual probability
every item := !items do
write (right(item.value, 7) || " " ||
left(item.probability, 15) || " " ||
left(count(sample, item.value)/*sample, 6))
end
```
Output:
```txt
aleph 0.2 0.1988
beth 0.1666666667 0.1676
gimel 0.1428571429 0.1431
daleth 0.125 0.1249
he 0.1111111111 0.1112
waw 0.1 0.0996
zayin 0.09090909091 0.0908
heth 0.06345598846 0.0636
```
## J
```J
main=: verb define
hdr=. ' target actual '
lbls=. ; ,:&.> ;:'aleph beth gimel daleth he waw zayin heth'
prtn=. +/\ pt=. (, 1-+/)1r1%5+i.7
da=. prtn I. ?y # 0
pa=. y%~ +/ da =/ i.8
hdr, lbls,. 9j6 ": |: pt,:pa
)
Note 'named abbreviations'
hdr (header)
lbls (labels)
pt (target proportions)
prtn (partitions corresponding to target proportions)
da (distribution of actual values among partitions)
pa (actual proportions)
)
```
Example use:
```j
main 1e6
target actual
aleph 0.200000 0.200344
beth 0.166667 0.166733
gimel 0.142857 0.142611
daleth 0.125000 0.124458
he 0.111111 0.111455
waw 0.100000 0.099751
zayin 0.090909 0.091121
heth 0.063456 0.063527
```
Note that there is no rounding error in summing the proportions, as they are represented as rational numbers, not floating-point approximations.
```J
pt=. (, 1-+/)1r1%5+i.7
pt
1r5 1r6 1r7 1r8 1r9 1r10 1r11 1759r27720
+/pt
1
```
## Java
{{trans|C}}
```java
public class Prob{
static long TRIALS= 1000000;
private static class Expv{
public String name;
public int probcount;
public double expect;
public double mapping;
public Expv(String name, int probcount, double expect, double mapping){
this.name= name;
this.probcount= probcount;
this.expect= expect;
this.mapping= mapping;
}
}
static Expv[] items=
{new Expv("aleph", 0, 0.0, 0.0), new Expv("beth", 0, 0.0, 0.0),
new Expv("gimel", 0, 0.0, 0.0),
new Expv("daleth", 0, 0.0, 0.0),
new Expv("he", 0, 0.0, 0.0), new Expv("waw", 0, 0.0, 0.0),
new Expv("zayin", 0, 0.0, 0.0),
new Expv("heth", 0, 0.0, 0.0)};
public static void main(String[] args){
int i, j;
double rnum, tsum= 0.0;
for(i= 0, rnum= 5.0;i < 7;i++, rnum+= 1.0){
items[i].expect= 1.0 / rnum;
tsum+= items[i].expect;
}
items[7].expect= 1.0 - tsum;
items[0].mapping= 1.0 / 5.0;
for(i= 1;i < 7;i++){
items[i].mapping= items[i - 1].mapping + 1.0 / ((double)i + 5.0);
}
items[7].mapping= 1.0;
for(i= 0;i < TRIALS;i++){
rnum= Math.random();
for(j= 0;j < 8;j++){
if(rnum < items[j].mapping){
items[j].probcount++;
break;
}
}
}
System.out.printf("Trials: %d\n", TRIALS);
System.out.printf("Items: ");
for(i= 0;i < 8;i++)
System.out.printf("%-8s ", items[i].name);
System.out.printf("\nTarget prob.: ");
for(i= 0;i < 8;i++)
System.out.printf("%8.6f ", items[i].expect);
System.out.printf("\nAttained prob.: ");
for(i= 0;i < 8;i++)
System.out.printf("%8.6f ", (double)(items[i].probcount)
/ (double)TRIALS);
System.out.printf("\n");
}
}
```
Output:
```txt
Trials: 1000000
Items: aleph beth gimel daleth he waw zayin heth
Target prob.: 0.200000 0.166667 0.142857 0.125000 0.111111 0.100000 0.090909 0.063456
Attained prob.: 0.199615 0.167517 0.142612 0.125211 0.110970 0.099614 0.091002 0.063459
```
{{works with|Java|1.5+}}
```java5
import java.util.EnumMap;
public class Prob {
public static long TRIALS= 1000000;
public enum Glyph{
ALEPH, BETH, GIMEL, DALETH, HE, WAW, ZAYIN, HETH;
}
public static EnumMap probs = new EnumMap(Glyph.class){{
put(Glyph.ALEPH, 1/5.0);
put(Glyph.BETH, 1/6.0);
put(Glyph.GIMEL, 1/7.0);
put(Glyph.DALETH, 1/8.0);
put(Glyph.HE, 1/9.0);
put(Glyph.WAW, 1/10.0);
put(Glyph.ZAYIN, 1/11.0);
put(Glyph.HETH, 1759./27720);
}};
public static EnumMap counts = new EnumMap(Glyph.class){{
put(Glyph.ALEPH, 0.);put(Glyph.BETH, 0.);
put(Glyph.GIMEL, 0.);put(Glyph.DALETH, 0.);
put(Glyph.HE, 0.);put(Glyph.WAW, 0.);
put(Glyph.ZAYIN, 0.);put(Glyph.HETH, 0.);
}};
public static void main(String[] args){
System.out.println("Target probabliities:\t" + probs);
for(long i = 0; i < TRIALS; i++){
Glyph choice = getChoice();
counts.put(choice, counts.get(choice) + 1);
}
//correct the counts to probablities in (0..1]
for(Glyph glyph:counts.keySet()){
counts.put(glyph, counts.get(glyph) / TRIALS);
}
System.out.println("Actual probabliities:\t" + counts);
}
private static Glyph getChoice() {
double rand = Math.random();
for(Glyph item:Glyph.values()){
if(rand < probs.get(item)){
return item;
}
rand -= probs.get(item);
}
return null;
}
}
```
Output:
```txt
Target probabliities: {ALEPH=0.2, BETH=0.16666666666666666, GIMEL=0.14285714285714285, DALETH=0.125, HE=0.1111111111111111, WAW=0.1, ZAYIN=0.09090909090909091, HETH=0.06345598845598846}
Actual probabliities: {ALEPH=0.200794, BETH=0.165916, GIMEL=0.143286, DALETH=0.124727, HE=0.110818, WAW=0.100168, ZAYIN=0.090878, HETH=0.063413}
```
## JavaScript
### ES5
Fortunately, iterating over properties added to an object maintains the insertion order.
```javascript
var probabilities = {
aleph: 1/5.0,
beth: 1/6.0,
gimel: 1/7.0,
daleth: 1/8.0,
he: 1/9.0,
waw: 1/10.0,
zayin: 1/11.0,
heth: 1759/27720
};
var sum = 0;
var iterations = 1000000;
var cumulative = {};
var randomly = {};
for (var name in probabilities) {
sum += probabilities[name];
cumulative[name] = sum;
randomly[name] = 0;
}
for (var i = 0; i < iterations; i++) {
var r = Math.random();
for (var name in cumulative) {
if (r <= cumulative[name]) {
randomly[name]++;
break;
}
}
}
for (var name in probabilities)
// using WSH
WScript.Echo(name + "\t" + probabilities[name] + "\t" + randomly[name]/iterations);
```
output:
```txt
aleph 0.2 0.200597
beth 0.16666666666666666 0.166527
gimel 0.14285714285714285 0.142646
daleth 0.125 0.124613
he 0.1111111111111111 0.111342
waw 0.1 0.099888
zayin 0.09090909090909091 0.091141
heth 0.06345598845598846 0.063246
```
### ES6
By functional composition:
{{Trans|Haskell}}
```JavaScript
(() => {
'use strict';
// GENERIC FUNCTIONS -----------------------------------------------------
// transpose :: [[a]] -> [[a]]
const transpose = xs =>
xs[0].map((_, iCol) => xs.map(row => row[iCol]));
// justifyLeft :: Int -> Char -> Text -> Text
const justifyLeft = (n, cFiller, strText) =>
n > strText.length ? (
(strText + cFiller.repeat(n))
.substr(0, n)
) : strText;
// 2 or more arguments
// curry :: Function -> Function
const curry = (f, ...args) => {
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
function () {
return go(xs.concat([].slice.apply(arguments)));
};
return go([].slice.call(args, 1));
};
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = (f, xs, ys) => {
const ny = ys.length;
return (xs.length <= ny ? xs : xs.slice(0, ny))
.map((x, i) => f(x, ys[i]));
};
// subtract :: (Num a) => a -> a -> a
const subtract = (x, y) => y - x;
// scanl1 :: (a -> a -> a) -> [a] -> [a]
const scanl1 = (f, xs) =>
xs.length > 0 ? scanl(f, xs[0], xs.slice(1)) : [];
// scanl :: (b -> a -> b) -> b -> [a] -> [b]
const scanl = (f, startValue, xs) =>
xs.reduce((a, x) => {
const v = f(a.acc, x);
return {
acc: v,
scan: a.scan.concat(v)
};
}, {
acc: startValue,
scan: [startValue]
})
.scan;
// unwords :: [String] -> String
const unwords = xs => xs.join(' ');
// PROBABILISTIC CHOICE --------------------------------------------------
// samples :: Int -> Int -> [Float]
const samples = n =>
Array.from({
length: n
}, Math.random);
// thresholds :: Float
const thresholds = scanl1(
(a, b) => a + b, [5, 6, 7, 8, 9, 10, 11].map(x => 1 / x)
)
.concat(1);
// expected :: Float -> Float
const expected = limits =>
limits.map((x, i, xs) => i > 0 ? (x - xs[i - 1]) : x);
// dataBinCounts :: [Float] -> [Float] -> [Int]
const dataBinCounts = (thresholds, samples) => {
const
lng = samples.length,
xs = thresholds
.map(x => lng - samples.filter(v => v > x)
.length);
return zipWith(subtract, [0].concat(xs), xs.concat(lng));
};
// intSamples :: Integer
const intSamples = 1000000;
// aligned :: a -> String
const aligned = x => justifyLeft(12, ' ', isNaN(x) ? x : x.toFixed(7));
return transpose([
['', 'Aleph', 'Beit', 'Gimel', 'Dalet', 'He', 'Vav', 'Zayin', 'Chet']
.map(curry(justifyLeft)(7, ' ')),
['Expected'].concat(expected(thresholds))
.map(aligned),
['Observed'].concat(dataBinCounts(thresholds, samples(intSamples))
.map(x => x / intSamples))
.map(aligned)
])
.map(unwords)
.join('\n');
})();
```
{{Out}}
Sample:
```txt
Expected Observed
Aleph 0.2000000 0.2002440
Beit 0.1666667 0.1665330
Gimel 0.1428571 0.1433880
Dalet 0.1250000 0.1244630
He 0.1111111 0.1112830
Vav 0.1000000 0.0998390
Zayin 0.0909091 0.0909630
Chet 0.0634560 0.0632870
```
## Julia
I made the solution to this task more difficult than I had anticipated by using the Hebrew characters (rather than their anglicised names) as labels for the sampled collection of objects. In doing so, I encountered an interesting subtlety of bidirectional text in Unicode. Namely, that strong right-to-left characters, such as those of Hebrew, override the directionality of European digits, which have weak directionality. Because of this property of Unicode, my table of items and yields had its lines of data interpreted as if it were entirely Hebrew and output in reverse order (from my English speaking perspective). I was able to get the table to display as I liked on my terminal by preceding the the Hebrew characters by the Unicode RLI (right-to-left isolate) control character (U+2067). However, when I pasted this output into this Rosetta Code entry, the display reverted to the "backwards" version. Rather than continue the struggle, trying to force this entry to display as it does on my terminal, I created an alternative version of the table. This "Displayable Here" table adds "yields" to to each line, and this strong left-to-right text makes the whole line display as left-to-right (without the need for a RLI characer).
```Julia
p = [1/i for i in 5:11]
plen = length(p)
q = [0.0, [sum(p[1:i]) for i = 1:plen]]
plab = [char(i) for i in 0x05d0:(0x05d0+plen)]
hi = 10^6
push!(p, 1.0 - sum(p))
plen += 1
accum = zeros(Int, plen)
for i in 1:hi
accum[sum(rand() .>= q)] += 1
end
r = accum/hi
println("Rates at which items are selected (", hi, " trials).")
println(" Item Expected Actual")
for i in 1:plen
println(@sprintf(" \u2067%s %8.6f %8.6f", plab[i], p[i], r[i]))
end
println()
println("Rates at which items are selected (", hi, " trials).")
println(" Item Count Expected Actual")
for i in 1:plen
println(@sprintf(" %s yields %6d %8.6f %8.6f",
plab[i], accum[i], p[i], r[i]))
end
```
{{out}}
'''Original'''
This table displays properly on my terminal, but the lines of data are reversed in this display.
```txt
Rates at which items are selected (1000000 trials).
Item Expected Actual
א 0.200000 0.199872
ב 0.166667 0.166618
ג 0.142857 0.143302
ד 0.125000 0.125040
ה 0.111111 0.110602
ו 0.100000 0.099833
ז 0.090909 0.091313
ח 0.063456 0.063420
```
'''Displayable Here'''
This is the same data, less elegantly presented but accurately displayed on both my terminal and here at Rosetta Code.
```txt
Rates at which items are selected (1000000 trials).
Item Count Expected Actual
א yields 199872 0.200000 0.199872
ב yields 166618 0.166667 0.166618
ג yields 143302 0.142857 0.143302
ד yields 125040 0.125000 0.125040
ה yields 110602 0.111111 0.110602
ו yields 99833 0.100000 0.099833
ז yields 91313 0.090909 0.091313
ח yields 63420 0.063456 0.063420
```
## Kotlin
{{trans|FreeBASIC}}
```scala
// version 1.0.6
fun main(args: Array) {
val letters = arrayOf("aleph", "beth", "gimel", "daleth", "he", "waw", "zayin", "heth")
val actual = IntArray(8)
val probs = doubleArrayOf(1/5.0, 1/6.0, 1/7.0, 1/8.0, 1/9.0, 1/10.0, 1/11.0, 0.0)
val cumProbs = DoubleArray(8)
cumProbs[0] = probs[0]
for (i in 1..6) cumProbs[i] = cumProbs[i - 1] + probs[i]
cumProbs[7] = 1.0
probs[7] = 1.0 - cumProbs[6]
val n = 1000000
(1..n).forEach {
val rand = Math.random()
when {
rand <= cumProbs[0] -> actual[0]++
rand <= cumProbs[1] -> actual[1]++
rand <= cumProbs[2] -> actual[2]++
rand <= cumProbs[3] -> actual[3]++
rand <= cumProbs[4] -> actual[4]++
rand <= cumProbs[5] -> actual[5]++
rand <= cumProbs[6] -> actual[6]++
else -> actual[7]++
}
}
var sumActual = 0.0
println("Letter\t Actual Expected")
println("------\t-------- --------")
for (i in 0..7) {
val generated = actual[i].toDouble() / n
println("${letters[i]}\t${String.format("%8.6f %8.6f", generated, probs[i])}")
sumActual += generated
}
println("\t-------- --------")
println("\t${"%8.6f".format(sumActual)} 1.000000")
}
```
{{out}}
```txt
Letter Actual Expected
------ -------- --------
aleph 0.199427 0.200000
beth 0.166862 0.166667
gimel 0.142756 0.142857
daleth 0.125442 0.125000
he 0.110868 0.111111
waw 0.100405 0.100000
zayin 0.090799 0.090909
heth 0.063441 0.063456
-------- --------
1.000000 1.000000
```
## Liberty BASIC
```lb
names$="aleph beth gimel daleth he waw zayin heth"
dim sum(8)
dim counter(8)
s = 0
for i = 1 to 7
s = s+1/(i+4)
sum(i)=s
next
N =1000000 ' number of throws
for i =1 to N
rand =rnd( 1)
for j = 1 to 7
if sum(j)> rand then exit for
next
counter(j)=counter(j)+1
next
print "Observed", "Intended"
for i = 1 to 8
print word$(names$, i), using( "#.#####", counter(i) /N), using( "#.#####", 1/(i+4))
next
```
## Lua
```lua
items = {}
items["aleph"] = 1/5.0
items["beth"] = 1/6.0
items["gimel"] = 1/7.0
items["daleth"] = 1/8.0
items["he"] = 1/9.0
items["waw"] = 1/10.0
items["zayin"] = 1/11.0
items["heth"] = 1759/27720
num_trials = 1000000
samples = {}
for item, _ in pairs( items ) do
samples[item] = 0
end
math.randomseed( os.time() )
for i = 1, num_trials do
z = math.random()
for item, _ in pairs( items ) do
if z < items[item] then
samples[item] = samples[item] + 1
break;
else
z = z - items[item]
end
end
end
for item, _ in pairs( items ) do
print( item, samples[item]/num_trials, items[item] )
end
```
Output
```txt
gimel 0.142606 0.14285714285714
heth 0.063434 0.063455988455988
beth 0.166788 0.16666666666667
zayin 0.091097 0.090909090909091
daleth 0.124772 0.125
aleph 0.200541 0.2
he 0.1107 0.11111111111111
waw 0.100062 0.1
```
## Mathematica
Built-in function can already do a weighted random choosing. Example for making a million random choices would be:
```Mathematica
choices={{"aleph", 1/5},{"beth", 1/6},{"gimel", 1/7},{"daleth", 1/8},{"he", 1/9},{"waw", 1/10},{"zayin", 1/11},{"heth", 1759/27720}};
data=RandomChoice[choices[[All,2]]->choices[[All,1]],10^6];
```
To compare the data we use the following code to make a table:
```Mathematica
Grid[{#[[1]],N[Count[data,#[[1]]]/10^6],N[#[[2]]]}&/@choices]
```
gives back (item, attained prob., target prob.):
```txt
aleph 0.200036 0.2
beth 0.166591 0.166667
gimel 0.142699 0.142857
daleth 0.125018 0.125
he 0.111306 0.111111
waw 0.100433 0.1
zayin 0.090671 0.0909091
heth 0.063246 0.063456
```
## MATLAB
{{works with|MATLAB|with Statistics Toolbox}}
```MATLAB
function probChoice
choices = {'aleph' 'beth' 'gimel' 'daleth' 'he' 'waw' 'zayin' 'heth'};
w = [1/5 1/6 1/7 1/8 1/9 1/10 1/11 1759/27720];
R = randsample(length(w), 1e6, true, w);
T = tabulate(R);
fprintf('Value\tCount\tPercent\tGoal\n')
for k = 1:size(T, 1)
fprintf('%6s\t%.f\t%.2f%%\t%.2f%%\n', ...
choices{k}, T(k, 2), T(k, 3), 100*w(k))
end
end
```
{{out}}
```txt
Value Count Percent Goal
aleph 199635 19.96% 20.00%
beth 166427 16.64% 16.67%
gimel 143342 14.33% 14.29%
daleth 125014 12.50% 12.50%
he 111031 11.10% 11.11%
waw 99920 9.99% 10.00%
zayin 91460 9.15% 9.09%
heth 63171 6.32% 6.35%
```
{{works with|MATLAB|without toolboxes}}
```MATLAB
function probChoice
choices = {'aleph' 'beth' 'gimel' 'daleth' 'he' 'waw' 'zayin' 'heth'};
w = [1/5 1/6 1/7 1/8 1/9 1/10 1/11 1759/27720];
nSamp = 1e6;
nChoice = length(w);
R = rand(nSamp, 1);
wCS = cumsum(w);
results = zeros(1, nChoice);
fprintf('Value\tCount\tPercent\tGoal\n')
for k = 1:nChoice
choiceKIdxs = R < wCS(k);
R(choiceKIdxs) = k;
results(k) = sum(choiceKIdxs);
fprintf('%6s\t%.f\t%.2f%%\t%.2f%%\n', ...
choices{k}, results(k), 100*results(k)/nSamp, 100*w(k))
end
end
```
{{out}}
```txt
Value Count Percent Goal
aleph 200327 20.03% 20.00%
beth 166318 16.63% 16.67%
gimel 143040 14.30% 14.29%
daleth 125136 12.51% 12.50%
he 111251 11.13% 11.11%
waw 99946 9.99% 10.00%
zayin 90974 9.10% 9.09%
heth 63008 6.30% 6.35%
```
## Nim
```nim
import tables, math, strutils, times
const
num_trials = 1000000
precsn = 6
var start = cpuTime()
var probs = initTable[string,float](16)
probs.add("aleph", 1/5.0)
probs.add("beth", 1/6.0)
probs.add("gimel", 1/7.0)
probs.add("daleth", 1/8.0)
probs.add("he", 1/9.0)
probs.add("waw", 1/10.0)
probs.add("zayin", 1/11.0)
probs.add("heth", 1759/27720)
var samples = initTable[string,int](16)
for i, j in pairs(probs):
samples.add(i,0)
randomize()
for i in 1 .. num_trials:
var z = random(1.0)
for j,k in pairs(probs):
if z < probs[j]:
samples[j] = samples[j] + 1
break
else:
z = z - probs[j]
var s1, s2: float
echo("Item ","\t","Target ","\t","Results ","\t","Difference")
echo("==== ","\t","====== ","\t","
### =
","\t","
### ====
")
for i, j in pairs(probs):
s1 += samples[i]/num_trials*100.0
s2 += probs[i]*100.0
echo( i,
"\t", formatFloat(probs[i],ffDecimal,precsn),
"\t", formatFloat(samples[i]/num_trials,ffDecimal,precsn),
"\t", formatFloat(100.0*(1.0-(samples[i]/num_trials)/probs[i]),ffDecimal,precsn),"%")
echo("======","\t","
### =
","\t","
### ==
")
echo("Total:","\t",formatFloat(s2,ffDecimal,2)," \t",formatFloat(s1,ffDecimal,2))
echo("\n",formatFloat(cpuTime()-start,ffDecimal,2)," secs")
```
{{out}}
```txt
Item Target Results Difference
==== ======
### =
### ====
he 0.111111 0.110760 0.316000%
heth 0.063456 0.063777 -0.505881%
beth 0.166667 0.166386 0.168400%
aleph 0.200000 0.200039 -0.019500%
zayin 0.090909 0.090923 -0.015300%
waw 0.100000 0.100513 -0.513000%
gimel 0.142857 0.142691 0.116300%
daleth 0.125000 0.124911 0.071200%
======
### =
### ==
Total: 100.00 100.00
7.06 secs
```
## OCaml
```ocaml
let p = [
"Aleph", 1.0 /. 5.0;
"Beth", 1.0 /. 6.0;
"Gimel", 1.0 /. 7.0;
"Daleth", 1.0 /. 8.0;
"He", 1.0 /. 9.0;
"Waw", 1.0 /. 10.0;
"Zayin", 1.0 /. 11.0;
"Heth", 1759.0 /. 27720.0;
]
let rec take k = function
| (v, p)::tl -> if k < p then v else take (k -. p) tl
| _ -> invalid_arg "take"
let () =
let n = 1_000_000 in
Random.self_init();
let h = Hashtbl.create 3 in
List.iter (fun (v, _) -> Hashtbl.add h v 0) p;
let tot = List.fold_left (fun acc (_, p) -> acc +. p) 0.0 p in
for i = 1 to n do
let sel = take (Random.float tot) p in
let n = Hashtbl.find h sel in
Hashtbl.replace h sel (succ n) (* count the number of each item *)
done;
List.iter (fun (v, p) ->
let d = Hashtbl.find h v in
Printf.printf "%s \t %f %f\n" v p (float d /. float n)
) p
```
Output:
```txt
Aleph 0.200000 0.200272
Beth 0.166667 0.166381
Gimel 0.142857 0.142497
Daleth 0.125000 0.125005
He 0.111111 0.111272
Waw 0.100000 0.100069
Zayin 0.090909 0.091136
Heth 0.063456 0.063368
```
## PARI/GP
```parigp
pc()={
my(v=[5544,10164,14124,17589,20669,23441,25961,27720],u=vector(8),e);
for(i=1,1e6,
my(r=random(27720));
for(j=1,8,
if(r 1e6;
sub prob_choice_picker {
my %options = @_;
my ($n, @a) = 0;
while (my ($k,$v) = each %options) {
$n += $v;
push @a, [$n, $k];
}
return sub {
my $r = rand;
( first {$r <= $_->[0]} @a )->[1];
};
}
my %ps =
(aleph => 1/5,
beth => 1/6,
gimel => 1/7,
daleth => 1/8,
he => 1/9,
waw => 1/10,
zayin => 1/11);
$ps{heth} = 1 - sum values %ps;
my $picker = prob_choice_picker %ps;
my %results;
for (my $n = 0 ; $n < TRIALS ; ++$n) {
++$results{$picker->()};
}
print "Event Occurred Expected Difference\n";
foreach (sort {$results{$b} <=> $results{$a}} keys %results) {
printf "%-6s %f %f %f\n",
$_, $results{$_}/TRIALS, $ps{$_},
abs($results{$_}/TRIALS - $ps{$_});
}
```
Sample output:
```txt
Event Occurred Expected Difference
aleph 0.198915 0.200000 0.001085
beth 0.166804 0.166667 0.000137
gimel 0.142992 0.142857 0.000135
daleth 0.125155 0.125000 0.000155
he 0.111160 0.111111 0.000049
waw 0.100229 0.100000 0.000229
zayin 0.091014 0.090909 0.000105
heth 0.063731 0.063456 0.000275
```
## Perl 6
{{works with|Rakudo|2018.10}}
```perl6
constant TRIALS = 1e6;
constant @event = ;
constant @P = flat (1 X/ 5 .. 11), 1759/27720;
constant @cP = [\+] @P;
my atomicint @results[+@event];
(^TRIALS).race.map: { @results[ @cP.first: { $_ > once rand }, :k ]⚛++; }
say 'Event Occurred Expected Difference';
for ^@results {
my ($occurred, $expected) = @results[$_], @P[$_] * TRIALS;
printf "%-9s%8.0f%9.1f%12.1f\n",
@event[$_],
$occurred,
$expected,
abs $occurred - $expected;
}
```
{{out}}
```txt
Event Occurred Expected Difference
aleph 200369 200000.0 369.0
beth 167005 166666.7 338.3
gimel 142690 142857.1 167.1
daleth 125061 125000.0 61.0
he 110563 111111.1 548.1
waw 100214 100000.0 214.0
zayin 90617 90909.1 292.1
heth 63481 63456.0 25.0
```
## Phix
```Phix
constant {names, probs} = columnize({{"aleph", 1/5},
{"beth", 1/6},
{"gimel", 1/7},
{"daleth", 1/8},
{"he", 1/9},
{"waw", 1/10},
{"zayin", 1/11},
{"heth", 1759/27720}})
sequence results = repeat(0,length(names))
atom r
constant lim = 1000000
for j=1 to lim do
r = rnd()
for i=1 to length(probs) do
r -= probs[i]
if r<=0 then
results[i]+=1
exit
end if
end for
end for
printf(1," Name Actual Expected\n")
for i=1 to length(probs) do
printf(1,"%6s %8.6f %8.6f\n",{names[i],results[i]/lim,probs[i]})
end for
```
{{out}}
```txt
Name Actual Expected
aleph 0.201010 0.200000
beth 0.166311 0.166667
gimel 0.143354 0.142857
daleth 0.124841 0.125000
he 0.110544 0.111111
waw 0.100228 0.100000
zayin 0.090270 0.090909
heth 0.063442 0.063456
```
## PicoLisp
```PicoLisp
(let (Count 1000000 Denom 27720 N Denom)
(let Probs
(mapcar
'((I S)
(prog1 (cons N (*/ Count I) 0 S)
(dec 'N (/ Denom I)) ) )
(range 5 12)
'(aleph beth gimel daleth he waw zayin heth) )
(do Count
(inc (cddr (rank (rand 1 Denom) Probs T))) )
(let Fmt (-6 12 12)
(tab Fmt NIL "Probability" "Result")
(for X Probs
(tab Fmt
(cdddr X)
(format (cadr X) 6)
(format (caddr X) 6) ) ) ) ) )
```
Output:
```txt
Probability Result
aleph 0.200000 0.199760
beth 0.166667 0.166878
gimel 0.142857 0.142977
daleth 0.125000 0.124983
he 0.111111 0.111200
waw 0.100000 0.100173
zayin 0.090909 0.090591
heth 0.083333 0.063438
```
## PL/I
```pli
probch: Proc Options(main);
Dcl prob(8) Dec Float(15) Init((1/5.0), /* aleph */
(1/6.0), /* beth */
(1/7.0), /* gimel */
(1/8.0), /* daleth */
(1/9.0), /* he */
(1/10.0), /* waw */
(1/11.0), /* zayin */
(1759/27720));/* heth */
Dcl what(8) Char(6) Init('aleph ','beth ','gimel ','daleth',
'he ','waw ','zayin ','heth ');
Dcl ulim(0:8) Dec Float(15) Init((9)0);
Dcl i Bin Fixed(31);
Dcl ifloat Dec Float(15);
Dcl one Dec Float(15) Init(1);
Dcl num Dec Float(15) Init(1759);
Dcl denom Dec Float(15) Init(27720);
Dcl x Dec Float(15) Init(0);
Dcl pr Dec Float(15) Init(0);
Dcl (n,nn) Bin Fixed(31);
Dcl cnt(8) Bin Fixed(31) Init((8)0);
nn=1000000;
Do i=1 To 8;
ifloat=i+4;
If i<8 Then
prob(i)=one/ifloat;
Else
prob(i)=num/denom;
Ulim(i)=ulim(i-1)+prob(i);
/* Put Skip list(i,prob(i),ulim(i));*/
End;
Do n=1 To nn;
x=random();
Do i=1 To 8;
If x 1 then some choices may be impossible
(define (probabalistic-choice choices)
(let-values
(((_ choice) ;; the fold provides two values, we only need the second
;; the first will always be a negative number showing that
;; I've run out of random steam
(for/fold
((rnd (random))
(choice #f))
(((v p) choices)
#:break (<= rnd 0))
(values (- rnd p) v))))
choice))
;;; ditto, but all probabilities must be exact rationals
;;; the optional lcd
;;;
;;; not the most efficient, since it provides a wrapper (and demo)
;;; for p-c/i-w below
(define (probabalistic-choice/exact
choices
#:gcd (GCD (/ (apply gcd (hash-values choices)))))
(probabalistic-choice/integer-weights
(for/hash (((k v) choices))
(values k (* v GCD)))
#:sum-of-weights GCD))
;;; this proves useful in Rock-Paper-Scissors
(define (probabalistic-choice/integer-weights
choices
#:sum-of-weights (sum-of-weights (apply + (hash-values choices))))
(let-values
(((_ choice)
(for/fold
((rnd (random sum-of-weights))
(choice #f))
(((v p) choices)
#:break (< rnd 0))
(values (- rnd p) v))))
choice))
(module+ test
(define test-samples (make-parameter 1000000))
(define (test-p-c-function f w)
(define test-selection (make-hash))
(for* ((i (in-range 0 (test-samples)))
(c (in-value (f w))))
(when (zero? (modulo i 100000)) (eprintf "~a," (quotient i 100000)))
(hash-update! test-selection c add1 0))
(printf "~a~%choice\tcount\texpected\tratio\terror~%" f)
(for* (((k v) (in-hash test-selection))
(e (in-value (* (test-samples) (hash-ref w k)))))
(printf "~a\t~a\t~a\t~a\t~a%~%"
k v e
(/ v (test-samples))
(real->decimal-string
(exact->inexact (* 100 (/ (- v e) e)))))))
(define test-weightings/rosetta
(hash
'aleph 1/5
'beth 1/6
'gimel 1/7
'daleth 1/8
'he 1/9
'waw 1/10
'zayin 1/11
'heth 1759/27720; adjusted so that probabilities add to 1
))
(define test-weightings/50:50 (hash 'woo 1/2 'yay 1/2))
(define test-weightings/1:2:3 (hash 'woo 1 'yay 2 'foo 3))
(test-p-c-function probabalistic-choice test-weightings/50:50)
(test-p-c-function probabalistic-choice/exact test-weightings/50:50)
(test-p-c-function probabalistic-choice test-weightings/rosetta)
(test-p-c-function probabalistic-choice/exact test-weightings/rosetta))
```
Output (note that the progress counts, which go to standard error, are
interleaved with the output on standard out)
```txt
0,1,2,3,4,5,6,7,8,9,#
choice count expected ratio error
yay 499744 500000 15617/31250 -0.05%
woo 500256 500000 15633/31250 0.05%
0,1,2,3,4,5,6,7,8,9,#
choice count expected ratio error
yay 499852 500000 124963/250000 -0.03%
woo 500148 500000 125037/250000 0.03%
0,1,2,3,4,5,6,7,8,9,#
choice count expected ratio error
daleth 124964 125000 31241/250000 -0.03%
zayin 90233 1000000/11 90233/1000000 -0.74%
gimel 142494 1000000/7 71247/500000 -0.25%
heth 64045 43975000/693 12809/200000 0.93%
aleph 199690 200000 19969/100000 -0.15%
beth 166861 500000/3 166861/1000000 0.12%
waw 100075 100000 4003/40000 0.07%
he 111638 1000000/9 55819/500000 0.47%
0,1,2,3,4,5,6,7,8,9,#
choice count expected ratio error
beth 166423 500000/3 166423/1000000 -0.15%
heth 63462 43975000/693 31731/500000 0.01%
daleth 125091 125000 125091/1000000 0.07%
waw 99820 100000 4991/50000 -0.18%
aleph 200669 200000 200669/1000000 0.33%
gimel 142782 1000000/7 71391/500000 -0.05%
zayin 90478 1000000/11 45239/500000 -0.47%
he 111275 1000000/9 4451/40000 0.15%
```
## REXX
Note: REXX can generate random numbers up to 100,000.
```rexx
/*REXX program displays results of probabilistic choices, gen random #s per probability.*/
parse arg trials digs seed . /*obtain the optional arguments from CL*/
if trials=='' | trials=="," then trials=1000000 /*Not specified? Then use the default.*/
if digs=='' | digs=="," then digs=15 /* " " " " " " */
if datatype(seed, 'W') then call random ,,seed /*allows repeatability for RANDOM nums.*/
numeric digits digs /*use a specific number of decimal digs*/
names= 'aleph beth gimel daleth he waw zayin heth ───totals───►' /*names of the cells.*/
HI=100000 /*max REXX RANDOM num*/
z=words(names); #=z - 1 /*#≡the number of actual/useable names.*/
$=0 /*initialize sum of the probabilities. */
do n=1 for #; prob.n=1 / (n+4); if n==# then prob.n= 1759 / 27720
$=$ + prob.n; Hprob.n=prob.n * HI
end /*n*/
prob.z=$ /*define the value of the ───totals───.*/
@.=0 /*initialize all counters in the range.*/
@.z=trials /*define the last counter of " " */
do j=1 for trials; r=random(HI) /*gen TRIAL number of random numbers.*/
do k=1 for # /*for each cell, compute percentages. */
if r<=Hprob.k then @.k=@.k + 1 /* " " " range, bump the counter*/
end /*k*/
end /*j*/
_= '═' /*_: a literal used for CENTER BIF pad*/
w=digs + 6 /*W: display width for the percentages*/
d=4 + max( length(trials), length('count') ) /* [↓] display a formatted top header.*/
say center('name',15,_) center('count',d,_) center('target %',w,_) center('actual %',w,_)
do cell=1 for z /*display each of the cells and totals.*/
say ' ' left( word(names, cell), 13) right(@.cell, d-2) " ",
left( format( prob.cell * 100, d), w-2),
left( format( @.cell/trials * 100, d), w-2)
if cell==# then say center(_,15,_) center(_,d,_) center(_,w,_) center(_,w,_)
end /*c*/ /* [↑] display a formatted foot header*/
/*stick a fork in it, we are all done.*/
```
{{out|output|text= when using the default input:}}
```txt
═════name══════ ═══count═══ ══════target %═══════ ══════actual %═══════
aleph 200135 20 20.0135
beth 166912 16.6666666 16.6912
gimel 143222 14.2857142 14.3222
daleth 124991 12.5 12.4991
he 111259 11.1111111 11.1259
waw 100049 10 10.0049
zayin 90978 9.0909090 9.0978
heth 63278 6.3455988 6.3278
═══════════════ ═══════════ ═════════════════════ ═════════════════════
───totals───► 1000000 100 100
```
## Ring
```ring
# Project : Probabilistic choice
cnt = list(8)
item = ["aleph","beth","gimel","daleth","he","waw","zayin","heth"]
prob = [1/5.0, 1/6.0, 1/7.0, 1/8.0, 1/9.0, 1/10.0, 1/11.0, 1759/27720]
for trial = 1 to 1000000
r = random(10)/10
p = 0
for i = 1 to len(prob)
p = p + prob[i]
if r < p
cnt[i] = cnt[i] + 1
loop
ok
next
next
see "item actual theoretical" + nl
for i = 1 to len(item)
see "" + item[i] + " " + cnt[i]/1000000 + " " + prob[i] + nl
next
```
Output:
```txt
item actual theoretical
aleph 0.091307 0.200000
beth 0.181073 0.166667
gimel 0.181884 0.142857
daleth 0.090985 0.125000
he 0.090958 0.111111
waw 0.091064 0.100000
zayin 0.091061 0.090909
heth 0 0.063456
```
## Ruby
```ruby
probabilities = {
"aleph" => 1/5.0,
"beth" => 1/6.0,
"gimel" => 1/7.0,
"daleth" => 1/8.0,
"he" => 1/9.0,
"waw" => 1/10.0,
"zayin" => 1/11.0,
}
probabilities["heth"] = 1.0 - probabilities.each_value.inject(:+)
ordered_keys = probabilities.keys
sum, sums = 0.0, {}
ordered_keys.each do |key|
sum += probabilities[key]
sums[key] = sum
end
actual = Hash.new(0)
samples = 1_000_000
samples.times do
r = rand
for k in ordered_keys
if r < sums[k]
actual[k] += 1
break
end
end
end
puts "key expected actual diff"
for k in ordered_keys
act = Float(actual[k]) / samples
val = probabilities[k]
printf "%-8s%.8f %.8f %6.3f %%\n", k, val, act, 100*(act-val)/val
end
```
{{out}}
```txt
key expected actual diff
aleph 0.20000000 0.19949200 -0.254 %
beth 0.16666667 0.16689900 0.139 %
gimel 0.14285714 0.14309300 0.165 %
daleth 0.12500000 0.12494200 -0.046 %
he 0.11111111 0.11037800 -0.660 %
waw 0.10000000 0.10030100 0.301 %
zayin 0.09090909 0.09162700 0.790 %
heth 0.06345599 0.06326800 -0.296 %
```
## Rust
```Rust
extern crate rand;
use rand::distributions::{IndependentSample, Sample, Weighted, WeightedChoice};
use rand::{weak_rng, Rng};
const DATA: [(&str, f64); 8] = [
("aleph", 1.0 / 5.0),
("beth", 1.0 / 6.0),
("gimel", 1.0 / 7.0),
("daleth", 1.0 / 8.0),
("he", 1.0 / 9.0),
("waw", 1.0 / 10.0),
("zayin", 1.0 / 11.0),
("heth", 1759.0 / 27720.0),
];
const SAMPLES: usize = 1_000_000;
/// Generate a mapping to be used by `WeightedChoice`
fn gen_mapping() -> Vec> {
DATA.iter()
.enumerate()
.map(|(i, &(_, p))| Weighted {
// `WeightedChoice` requires `u32` weights rather than raw probabilities. For each
// probability, we convert it to a `u32` weight, and associate it with an index. We
// multiply by a constant because small numbers such as 0.2 when casted to `u32`
// become `0`. This conversion decreases the accuracy of the mapping, which is why we
// provide an implementation which uses `f64`s for the best accuracy.
weight: (p * 1_000_000_000.0) as u32,
item: i,
})
.collect()
}
/// Generate a mapping of the raw probabilities
fn gen_mapping_float() -> Vec {
// This does the work of `WeightedChoice::new`, splitting a number into various ranges. The
// `item` of `Weighted` is represented here merely by the probability's position in the `Vec`.
let mut running_total = 0.0;
DATA.iter()
.map(|&(_, p)| {
running_total += p;
running_total
})
.collect()
}
/// An implementation of `WeightedChoice` which uses probabilities rather than weights. Refer to
/// the `WeightedChoice` source for serious usage.
struct WcFloat {
mapping: Vec,
}
impl WcFloat {
fn new(mapping: &[f64]) -> Self {
Self {
mapping: mapping.to_vec(),
}
}
// This is roughly the same logic as `WeightedChoice::ind_sample` (though is likely slower)
fn search(&self, sample_prob: f64) -> usize {
let idx = self.mapping
.binary_search_by(|p| p.partial_cmp(&sample_prob).unwrap());
match idx {
Ok(i) | Err(i) => i,
}
}
}
impl IndependentSample for WcFloat {
fn ind_sample(&self, rng: &mut R) -> usize {
// Because we know the total is exactly 1.0, we can merely use a raw float value.
// Otherwise caching `Range::new(0.0, running_total)` and sampling with
// `range.ind_sample(&mut rng)` is recommended.
let sample_prob = rng.next_f64();
self.search(sample_prob)
}
}
impl Sample for WcFloat {
fn sample(&mut self, rng: &mut R) -> usize {
self.ind_sample(rng)
}
}
fn take_samples(rng: &mut R, wc: &T) -> [usize; 8]
where
T: IndependentSample,
{
let mut counts = [0; 8];
for _ in 0..SAMPLES {
let sample = wc.ind_sample(rng);
counts[sample] += 1;
}
counts
}
fn print_mapping(counts: &[usize]) {
println!("Item | Expected | Actual ");
println!("-------+----------+----------");
for (&(name, expected), &count) in DATA.iter().zip(counts.iter()) {
let real = count as f64 / SAMPLES as f64;
println!("{:6} | {:.6} | {:.6}", name, expected, real);
}
}
fn main() {
let mut rng = weak_rng();
println!(" ~~~ U32 METHOD ~~~");
let mut mapping = gen_mapping();
let wc = WeightedChoice::new(&mut mapping);
let counts = take_samples(&mut rng, &wc);
print_mapping(&counts);
println!();
println!(" ~~~ FLOAT METHOD ~~~");
// initialize the float version of `WeightedChoice`
let mapping = gen_mapping_float();
let wc = WcFloat::new(&mapping);
let counts = take_samples(&mut rng, &wc);
print_mapping(&counts);
}
```
{{out}}
```txt
~~~ U32 METHOD ~~~
Item | Expected | Actual
-------+----------+----------
aleph | 0.200000 | 0.200195
beth | 0.166667 | 0.166182
gimel | 0.142857 | 0.142502
daleth | 0.125000 | 0.125503
he | 0.111111 | 0.110820
waw | 0.100000 | 0.100166
zayin | 0.090909 | 0.090927
heth | 0.063456 | 0.063705
~~~ FLOAT METHOD ~~~
Item | Expected | Actual
-------+----------+----------
aleph | 0.200000 | 0.199984
beth | 0.166667 | 0.166634
gimel | 0.142857 | 0.143218
daleth | 0.125000 | 0.124956
he | 0.111111 | 0.111047
waw | 0.100000 | 0.099805
zayin | 0.090909 | 0.090513
heth | 0.063456 | 0.063843
```
## Scala
This algorithm consists of a concise two-line tail-recursive loop (def weighted). The rest of the code is for API robustness, testing and display. weightedProb is for the task as stated (0 < p < 1), and weightedFreq is the equivalent based on integer frequencies (f >= 0).
```Scala
object ProbabilisticChoice extends App {
import scala.collection.mutable.LinkedHashMap
def weightedProb[A](prob: LinkedHashMap[A,Double]): A = {
require(prob.forall{case (_, p) => p > 0 && p < 1})
assume(prob.values.sum == 1)
def weighted(todo: Iterator[(A,Double)], rand: Double, accum: Double = 0): A = todo.next match {
case (s, i) if rand < (accum + i) => s
case (_, i) => weighted(todo, rand, accum + i)
}
weighted(prob.toIterator, scala.util.Random.nextDouble)
}
def weightedFreq[A](freq: LinkedHashMap[A,Int]): A = {
require(freq.forall{case (_, f) => f >= 0})
require(freq.values.sum > 0)
def weighted(todo: Iterator[(A,Int)], rand: Int, accum: Int = 0): A = todo.next match {
case (s, i) if rand < (accum + i) => s
case (_, i) => weighted(todo, rand, accum + i)
}
weighted(freq.toIterator, scala.util.Random.nextInt(freq.values.sum))
}
// Tests:
val probabilities = LinkedHashMap(
'aleph -> 1.0/5,
'beth -> 1.0/6,
'gimel -> 1.0/7,
'daleth -> 1.0/8,
'he -> 1.0/9,
'waw -> 1.0/10,
'zayin -> 1.0/11,
'heth -> 1759.0/27720
)
val frequencies = LinkedHashMap(
'aleph -> 200,
'beth -> 167,
'gimel -> 143,
'daleth -> 125,
'he -> 111,
'waw -> 100,
'zayin -> 91,
'heth -> 63
)
def check[A](original: LinkedHashMap[A,Double], results: Seq[A]) {
val freq = results.groupBy(x => x).mapValues(_.size.toDouble/results.size)
original.foreach{case (k, v) =>
val a = v/original.values.sum
val b = freq(k)
val c = if (Math.abs(a - b) < 0.001) "ok" else "**"
println(f"$k%10s $a%.4f $b%.4f $c")
}
println(" "*10 + f" ${1}%.4f ${freq.values.sum}%.4f")
}
println("Checking weighted probabilities:")
check(probabilities, for (i <- 1 to 1000000) yield weightedProb(probabilities))
println
println("Checking weighted frequencies:")
check(frequencies.map{case (a, b) => a -> b.toDouble}, for (i <- 1 to 1000000) yield weightedFreq(frequencies))
}
```
{{out}}
```txt
Checking weighted probabilities:
'aleph 0.2000 0.2001 ok
'beth 0.1667 0.1665 ok
'gimel 0.1429 0.1430 ok
'daleth 0.1250 0.1248 ok
'he 0.1111 0.1112 ok
'waw 0.1000 0.1000 ok
'zayin 0.0909 0.0911 ok
'heth 0.0635 0.0632 ok
1.0000 1.0000
Checking weighted frequencies:
'aleph 0.2000 0.2000 ok
'beth 0.1670 0.1672 ok
'gimel 0.1430 0.1432 ok
'daleth 0.1250 0.1243 ok
'he 0.1110 0.1105 ok
'waw 0.1000 0.1002 ok
'zayin 0.0910 0.0913 ok
'heth 0.0630 0.0632 ok
1.0000 1.0000
```
## Seed7
To reduce the runtime this program should be compiled.
```seed7
$ include "seed7_05.s7i";
include "float.s7i";
const type: letter is new enum
aleph, beth, gimel, daleth, he, waw, zayin, heth
end enum;
const func string: str (in letter: aLetter) is
return [] ("aleph", "beth", "gimel", "daleth", "he", "waw", "zayin", "heth") [succ(ord(aLetter))];
enable_output(letter);
const array [letter] integer: table is [letter] (
5544, 4620, 3960, 3465, 3080, 2772, 2520, 1759);
const func letter: randomLetter is func
result
var letter: resultLetter is aleph;
local
var integer: number is 0;
begin
number := rand(1, 27720);
while number > table[resultLetter] do
number -:= table[resultLetter];
incr(resultLetter);
end while;
end func;
const proc: main is func
local
var integer: count is 0;
var letter: aLetter is aleph;
var array [letter] integer: occurrence is letter times 0;
begin
for count range 1 to 1000000 do
aLetter := randomLetter;
incr(occurrence[aLetter]);
end for;
writeln("Name Count Ratio Expected");
for aLetter range letter.first to letter.last do
writeln(aLetter rpad 7 <& occurrence[aLetter] lpad 6 <&
flt(occurrence[aLetter]) / 10000.9 digits 4 lpad 8 <& "%" <&
100.0 * flt(table[aLetter]) / 27720.0 digits 4 lpad 8 <& "%");
end for;
end func;
```
Outout:
```txt
Name Count Ratio Expected
aleph 199788 19.9770% 20.0000%
beth 166897 16.6882% 16.6667%
gimel 143103 14.3090% 14.2857%
daleth 125060 12.5049% 12.5000%
he 110848 11.0838% 11.1111%
waw 99550 9.9541% 10.0000%
zayin 90918 9.0910% 9.0909%
heth 63836 6.3830% 6.3456%
```
## Scheme
Using guile scheme 2.0.11.
```scheme
(use-modules (ice-9 format))
(define (random-choice probs)
(define choice (random 1.0))
(define (helper val prob-lis)
(let ((nval (- val (cadar prob-lis))))
(if
(< nval 0)
(caar prob-lis)
(helper nval (cdr prob-lis)))))
(helper choice probs))
(define (add-result result delta table)
(cond
((null? table) (list (list result delta)))
((eq? (caar table) result)
(cons (list result (+ (cadar table) delta)) (cdr table)))
(#t (cons (car table) (add-result result delta (cdr table))))))
(define (choices trials probs)
(define (helper trial-num freq-table)
(if
(= trial-num trials)
freq-table
(helper
(+ trial-num 1)
(add-result (random-choice probs) (/ 1 trials) freq-table))))
(helper 0 '()))
(define (format-results probs results)
(for-each
(lambda (x)
(format
#t
"~10a~10,5f~10,5f~%"
(car x)
(cadr x)
(cadr (assoc (car x) results))))
probs))
(define probs
'((aleph 1/5) (beth 1/6) (gimel 1/7) (daleth 1/8)
(he 1/9) (waw 1/10) (zayin 1/11) (heth 1759/27720)))
(format-results probs (choices 1000000 probs))
```
Example output:
aleph 0.20000 0.20051
beth 0.16667 0.16680
gimel 0.14286 0.14231
daleth 0.12500 0.12538
he 0.11111 0.11136
waw 0.10000 0.09955
zayin 0.09091 0.09096
heth 0.06346 0.06313
## Sidef
{{trans|Perl}}
```ruby
define TRIALS = 1e4;
func prob_choice_picker(options) {
var n = 0;
var a = [];
options.each { |k,v|
n += v;
a << [n, k];
}
func {
var r = 1.rand;
a.first{|e| r <= e[0] }[1];
}
}
var ps = Hash(
aleph => 1/5,
beth => 1/6,
gimel => 1/7,
daleth => 1/8,
he => 1/9,
waw => 1/10,
zayin => 1/11
)
ps{:heth} = (1 - ps.values.sum)
var picker = prob_choice_picker(ps)
var results = Hash()
TRIALS.times {
results{picker()} := 0 ++;
}
say "Event Occurred Expected Difference";
for k,v in (results.sort_by {|k| results{k} }.reverse) {
printf("%-6s %f %f %f\n",
k, v/TRIALS, ps{k},
abs(v/TRIALS - ps{k})
);
}
```
{{out}}
```txt
Event Occurred Expected Difference
aleph 0.196300 0.200000 0.003700
beth 0.165600 0.166667 0.001067
gimel 0.143700 0.142857 0.000843
daleth 0.123900 0.125000 0.001100
he 0.111800 0.111111 0.000689
waw 0.101900 0.100000 0.001900
zayin 0.088100 0.090909 0.002809
heth 0.068800 0.063456 0.005344
```
## Stata
```stata
clear
mata
letters="aleph","beth","gimel","daleth","he","waw","zayin","heth"
a=letters[rdiscrete(10000,1,(1/5,1/6,1/7,1/8,1/9,1/10,1/11,1759/27720))]'
st_addobs(10000)
st_addvar("str10","a")
st_sstore(.,.,a)
end
```
## Tcl
```tcl
package require Tcl 8.5
set map [dict create]
set sum 0.0
foreach name {aleph beth gimel daleth he waw zayin} \
prob {1/5.0 1/6.0 1/7.0 1/8.0 1/9.0 1/10.0 1/11.0} \
{
set prob [expr $prob]
set sum [expr {$sum + $prob}]
dict set map $name [dict create probability $prob limit $sum count 0]
}
dict set map heth [dict create probability [expr {1.0 - $sum}] limit 1.0 count 0]
set samples 1000000
for {set i 0} {$i < $samples} {incr i} {
set n [expr {rand()}]
foreach name [dict keys $map] {
if {$n <= [dict get $map $name limit]} {
set count [dict get $map $name count]
dict set map $name count [incr count]
break
}
}
}
puts "using $samples samples:"
puts [format "%-10s %-21s %-9s %s" "" expected actual difference]
dict for {name submap} $map {
dict with submap {
set actual [expr {$count * 1.0 / $samples}]
puts [format "%-10s %-21s %-9s %4.2f%%" $name $probability $actual \
[expr {abs($actual - $probability)/$probability*100.0}]
]
}
}
```
```txt
using 1000000 samples:
expected actual difference
aleph 0.2 0.199641 0.18%
beth 0.16666666666666666 0.1674 0.44%
gimel 0.14285714285714285 0.143121 0.18%
daleth 0.125 0.124864 0.11%
he 0.1111111111111111 0.111036 0.07%
waw 0.1 0.100021 0.02%
zayin 0.09090909090909091 0.09018 0.80%
heth 0.06345598845598843 0.063737 0.44%
```
## Ursala
The stochasm library function used here constructs a weighted non-deterministic choice of
a set of functions. The pseudo-random number generator is a 64 bit Mersenne twistor
implemented by the run time system.
```Ursala
#import std
#import nat
#import flo
outcomes = <'aleph ','beth ','gimel ','daleth','he ','waw ','zayin ','heth '>
probabilities = ^lrNCT(~&,minus/1.+ plus:-0) div/*1. float* skip/5 iota12
simulation =
^(~&rn,div+ float~~rmPlX)^*D/~& iota; ^A(~&h,length)*K2+ * stochasm@p/probabilities !* outcomes
format =
:/' frequency probability'+ * ^lrlrTPT/~&n (printf/'%12.8f')^~/~&m outcomes-$probabilities@n
#show+
results = format simulation 1000000
```
output:
```txt
frequency probability
daleth 0.12484500 0.12500000
beth 0.16680600 0.16666667
aleph 0.19973700 0.20000000
waw 0.10016900 0.10000000
gimel 0.14293100 0.14285714
he 0.11131100 0.11111111
zayin 0.09104700 0.09090909
heth 0.06315400 0.06345599
```
## VBScript
Derived from the BBC BASIC version
```vb
item = Array("aleph","beth","gimel","daleth","he","waw","zayin","heth")
prob = Array(1/5.0, 1/6.0, 1/7.0, 1/8.0, 1/9.0, 1/10.0, 1/11.0, 1759/27720)
Dim cnt(7)
'Terminate script if sum of probabilities <> 1.
sum = 0
For i = 0 To UBound(prob)
sum = sum + prob(i)
Next
If sum <> 1 Then
WScript.Quit
End If
For trial = 1 To 1000000
r = Rnd(1)
p = 0
For i = 0 To UBound(prob)
p = p + prob(i)
If r < p Then
cnt(i) = cnt(i) + 1
Exit For
End If
Next
Next
WScript.StdOut.Write "item" & vbTab & "actual" & vbTab & vbTab & "theoretical"
WScript.StdOut.WriteLine
For i = 0 To UBound(item)
WScript.StdOut.Write item(i) & vbTab & FormatNumber(cnt(i)/1000000,6) & vbTab & FormatNumber(prob(i),6)
WScript.StdOut.WriteLine
Next
```
{{out}}
```txt
item actual theoretical
aleph 0.199755 0.200000
beth 0.166861 0.166667
gimel 0.143240 0.142857
daleth 0.124474 0.125000
he 0.110879 0.111111
waw 0.100341 0.100000
zayin 0.090745 0.090909
heth 0.063705 0.063456
```
## XPL0
```XPL0
include c:\cxpl\codes;
def Size = 10_000_000;
int Tbl(12+1);
int I, J, N;
real X, S0, S1;
[for J:= 5 to 12 do Tbl(J):= 0;
for I:= 0 to 1_000_000-1 do \generate one million items
[N:= Ran(Size);
for J:= 5 to 11 do
[N:= N - Size/J;
if N < 0 then [Tbl(J):= Tbl(J)+1; J:= 100];
];
if J=12 then Tbl(12):= Tbl(12)+1;
];
S0:= 0.0; S1:= 0.0;
for J:= 5 to 11 do
[X:= 1.0/float(J); RlOut(0, X); S0:= S0+X;
X:= float(Tbl(J)) / 1_000_000.0; RlOut(0, X); S1:= S1+X;
CrLf(0);
];
X:= 1759.0 / 27720.0; RlOut(0, X); S0:= S0+X;
X:= float(Tbl(12)) / 1_000_000.0; RlOut(0, X); S1:= S1+X;
CrLf(0);
Text(0, " ------- -------
");
RlOut(0, S0); RlOut(0, S1);
]
```
Output:
```txt
0.20000 0.20012
0.16667 0.16679
0.14286 0.14305
0.12500 0.12510
0.11111 0.11113
0.10000 0.09990
0.09091 0.09077
0.06346 0.06313
------- -------
1.00000 1.00000
```
## zkl
{{trans|C}}
```zkl
var names=T("aleph", "beth", "gimel", "daleth",
"he", "waw", "zayin", "heth");
var ptable=T(5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0).apply('/.fp(1.0));
ptable=ptable.append(1.0-ptable.sum(0.0)); // add last weight to sum to 1.0
var [const] N=ptable.len();
fcn ridx{ i:=0; s:=(0.0).random(1);
while((s-=ptable[i]) > 0) { i+=1 }
i
}
const M=0d1_000_000;
var r=(0).pump(N,List,T(Ref,0)); // list of references to int 0
(0).pump(M,Void,fcn{r[ridx()].inc()}); // 1,000,000 weighted random #s
r=r.apply("value").apply("toFloat"); // (reference to int)-->int-->float
println(" Name Count Ratio Expected");
foreach i in (N){
"%6s%7d %7.4f%% %7.4f%%".fmt(names[i], r[i], r[i]/M*100,
ptable[i]*100).println();
}
```
{{out}}
```txt
Name Count Ratio Expected
aleph 200214 20.0214% 20.0000%
beth 166399 16.6399% 16.6667%
gimel 143100 14.3100% 14.2857%
daleth 125197 12.5197% 12.5000%
he 111167 11.1167% 11.1111%
waw 100097 10.0097% 10.0000%
zayin 90692 9.0692% 9.0909%
heth 63162 6.3162% 6.3456%
```