ZetCode

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.

random.a68
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.

dice.a68
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.

lcg.a68
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.

histogram.a68
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.

shuffle.a68
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.

walk.a68
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

The Algol 68 Genie project

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

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Algol 68 tutorials.