Algol 68 random numbers
last modified November 14, 2026
random returns a real number between 0 and 1, and the program
turns that number into a die roll, a card position, or a step of a simulation.
Algol 68 Genie provides the generator; it does not provide a procedure for
seeding it, so a run cannot be repeated exactly from the source alone.
This article rolls dice, draws a histogram, shuffles a row, and walks a random path. The examples that need repeatable output use a small generator written in the program itself, which is also a useful demonstration of how a linear congruential generator works.
The random procedure
random is a procedure of mode PROC REAL that returns
a value in the half-open interval from 0 to 1. It is called like a constant,
without parentheses or arguments, and each call advances the sequence.
BEGIN
print(("five values from random:", newline));
FOR i TO 5 DO
print((fixed(random, 8, 4), newline))
OD
END
The values are printed with four decimals in an eight-character field. The
sequence is not reproducible from the source, so the transcript below is only
one sample; a program that needs a fixed sequence must not depend on these
numbers. The value is
never exactly 1, which is what makes ENTIER(random * n) a safe
index for a row of n elements.
$ a68g random.a68 five values from random: +0.4428 +0.9472 +0.1512 +0.3065 +0.9669
Random integers
A die roll scales the interval to six values: multiplying by 6 gives a real
from 0 up to but not including 6, and ENTIER removes the
fractional part. Adding 1 shifts the result from 0 to 5 into the range from 1
to 6.
PROC roll = INT:
ENTIER(random * 6) + 1;
BEGIN
print(("ten rolls:"));
FOR i TO 10 DO print((whole(roll, 3))) OD;
print(newline)
END
ENTIER truncates towards zero, and the argument is never
negative, so it acts as the floor here. The result has six equally likely
values as far as the underlying generator is uniform. A different range is
written the same way: a number from 1 to 100 is
ENTIER(random * 100) + 1, and a number from 0 to 9 is
ENTIER(random * 10).
$ a68g dice.a68 ten rolls: +3 +6 +1 +2 +6 +2 +6 +4 +4 +5
A generator you can reproduce
A linear congruential generator keeps one integer of state and replaces it
with (seed * a + c) MOD m on every call. The constants used here
are small, so the multiplication cannot overflow an INT, and the
modulus 65537 is prime, which gives a long period for a generator of this
size.
INT seed := 12345; PROC next = INT: ( seed := (seed * 75 + 74) MOD 65537; seed ); PROC next below = (INT n) INT: (next * n) OVER 65537; BEGIN FOR i TO 10 DO print((whole(next below(100), -4))) OD; print(newline) END
next updates the global seed and returns the new
state, so two calls give two different numbers. next below
scales the state to the range from 0 to n - 1 by multiplying and
dividing by the modulus; dividing rather than taking a remainder keeps the
values spread over the whole range. The sequence starts from the same seed on
every run, which is what makes the following examples reproducible.
$ a68g lcg.a68 12 64 71 49 43 82 67 43 32 67
A histogram
Six thousand rolls of a die should give about a thousand of each face. The program counts the faces in a row and draws one asterisk for every hundred hits, which turns the counts into a bar chart without any graphics.
INT seed := 12345;
PROC next = INT:
(
seed := (seed * 75 + 74) MOD 65537;
seed
);
PROC next below = (INT n) INT:
(next * n) OVER 65537;
BEGIN
[1:6] INT counts;
FOR i TO 6 DO counts[i] := 0 OD;
FOR i TO 6000 DO
counts[next below(6) + 1] +:= 1
OD;
FOR i TO 6 DO
print((whole(i, 0), ": ", whole(counts[i], 5), " "));
FOR j TO counts[i] OVER 100 DO print(("*")) OD;
print(newline)
OD
END
A row declared without an initialiser holds undefined values, so the first
loop sets every counter to zero before the rolls start. The bar length is the
count divided by 100 with OVER, which discards the remainder and
keeps the chart narrow. The six counts differ by a few dozen, which is the
expected spread for six thousand independent rolls.
$ a68g histogram.a68 1: +1050 ********** 2: +1023 ********** 3: +965 ********* 4: +995 ********* 5: +1003 ********** 6: +964 *********
Shuffling a row
A shuffle visits the row from the last element to the second, picks an index at random from the part that has not been fixed yet, and swaps the two elements. After each visit the current position is final and the prefix before it is still unshuffled; the procedure is the Fisher-Yates shuffle.
INT seed := 12345;
PROC next = INT:
(
seed := (seed * 75 + 74) MOD 65537;
seed
);
PROC next below = (INT n) INT:
(next * n) OVER 65537;
PROC shuffle = (REF []INT a) VOID:
(
FOR i FROM UPB a BY -1 TO 2 DO
INT j = next below(i) + 1;
INT t = a[i];
a[i] := a[j];
a[j] := t
OD
);
BEGIN
[1:10] INT a := (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
shuffle(a);
FOR i TO UPB a DO print((whole(a[i], -3))) OD;
print(newline)
END
The parameter is REF []INT, so the swap writes into the caller's
row. next below(i) returns a value from 0 to i - 1,
and adding 1 makes it a valid subscript from 1 to i. The loop
stops at 2, because a one-element prefix is already shuffled. The output is
one permutation of the numbers from 1 to 10, and the same seed always produces
the same permutation.
$ a68g shuffle.a68 7 1 10 8 5 3 4 9 6 2
A random walk
A random walk starts at zero and moves one step left or right at every turn. The program records the final position and the highest and lowest points reached, which shows how far a walk of a thousand steps can drift.
INT seed := 12345;
PROC next = INT:
(
seed := (seed * 75 + 74) MOD 65537;
seed
);
PROC next below = (INT n) INT:
(next * n) OVER 65537;
BEGIN
INT pos := 0, high := 0, low := 0;
FOR step TO 1000 DO
IF next below(2) = 0 THEN pos -:= 1 ELSE pos +:= 1 FI;
IF pos > high THEN high := pos FI;
IF pos < low THEN low := pos FI
OD;
print(("final ", whole(pos, 0), ", highest ", whole(high, 0),
", lowest ", whole(low, 0), newline))
END
next below(2) yields 0 or 1 with equal probability, so the walk
moves one step in either direction. The high and low water marks start at
zero, which is also the starting position, and are updated after every move.
A different seed produces a different walk, and replacing
next below(2) with ENTIER(random * 2) uses the
system generator instead.
$ a68g walk.a68 final -16, highest 3, lowest -34
Next steps
The date and time article uses similar integer arithmetic for its day numbers, and the rows article explains the subscripts and slices that the shuffle relies on. The sorting article shows how to put the shuffled row back in order.
Source
Revised Report on the Algorithmic Language Algol 68
In this article we have drawn values from random, rolled dice,
written a reproducible linear congruential generator, drawn a histogram,
shuffled a row, and simulated a random walk.
Author
List all Algol 68 tutorials.