The Machines · Statistics & machine learning

random

Seedless Randomness — machines/random.shoddy

the random machine's icon

Summary

random gives you random numbers, plainly. Three number words: Random for a fraction between 0 and 1, RandomRange for a floating-point number (one that can carry a fractional part) between two bounds, and RandomInt for a whole number between two bounds. Plus two list words: Shuffle for a random ordering, and Sample for draws that never pick the same item twice. All of them sit directly on the Rnd builtin — a word the engine itself supplies — which draws evenly-spread values from the engine's own generator. The machine is seedless by construction: there is no seed (a starting value that fixes the sequence) to create, store, or thread through your code. You call a word, you get a fresh random number. That's the whole thing — and the simplicity is the point.

A Brief History of the State of Sin

Deterministic machines — computers that, given the same start, always do exactly the same thing — were asked for random numbers almost as soon as they existed. The first big customers ran on chance: the Monte Carlo simulations of the atomic-bomb laboratories, which answer hard questions by running thousands of random trials. John von Neumann proposed the first widely used generator in the late 1940s, the middle-square method: square a number, take its middle digits, repeat. He knew exactly how disreputable the enterprise was. He said so in the line every textbook has quoted since — anyone who considers arithmetical methods of producing random digits is, of course, in a state of sin. The sinning continued productively. Derrick Lehmer's linear congruential generator of 1949 powered decades of language runtimes. Its modern successors are faster, and their period — how far the sequence runs before it repeats — is enormously longer. What never changed is the theology. A generated sequence is not random, only unpredictable enough for the purpose — splendid for shuffles, samples and simulations, never for secrets. This machine keeps to the honest side of that line. Its seedless design leaves the state where it belongs, in the engine.

Why It's Useful

Randomness is how a program stops being predictable. A dice roll, a shuffled deck, a coin flip, a wandering game character, a scattering of stars, a sampled guess — all of them need a source of numbers that isn't the same every time. random is that source, with the two shapes you almost always want: a number somewhere in a range, or a whole number somewhere in a range. Because it's seedless, there's nothing to set up and nothing to carry around. Reach for it whenever you want a surprise and don't need to reproduce it exactly later. (When you do need reproducibility, the trade-off is spelled out below.)

User's Guide

Include the file and call the words — there's no state to manage. The one thing worth understanding is what makes this machine unusual, so here it is plainly: Random() is deliberately not a pure function. Everywhere else, Shoddy makes a firm promise: call a function twice with the same arguments and you get the same answer, every time, no exceptions. That predictability is the property the whole language is built to guarantee. Random is the one considered exception. Call it twice with no arguments and it can, and should, give you two different numbers. It's an effect, living at the edge of your program in exactly the same family as Print — something that reaches out to the world (here, the engine's running generator) rather than a closed calculation. Treat it that way. Keep it near the edges of your code, the way you'd keep printing there, and let the pure parts stay pure.

Include "random.shoddy"

Def Main()
    Print(Random())                 ' e.g. 0.417... — a fraction in [0, 1)
    Print(RandomRange(1, 6))        ' e.g. 4.82...  — a float in [1, 6)
    Print(RandomInt(1, 6))          ' e.g. 5        — a die roll, 1 to 6

    ' Called twice, RandomInt gives two (probably) different answers —
    ' that impurity is the whole point.
    Print(RandomInt(1, 100))        ' e.g. 73
    Print(RandomInt(1, 100))        ' e.g. 12

    Let deck = Shuffle(Range(1, 10))    ' e.g. [ 7 2 9 ... ] — all ten, reordered
    Let hand = Sample(deck, 3)          ' e.g. [ 4 9 1 ]     — three, no repeats
    Print(hand)

A few things worth remembering:

Builtins

The one word the runtime dispatches — not defined here, documented here

Rnd is not a Def. The engine dispatches it, and a Def whose name is a builtin is refused. It is documented on this page because random is the machine built directly on it. It needs no Include — the same entry is in machines/random.shoddy's own header block.

WordDescription
Rnd()A uniform random number in [0, 1) — zero possible, one never. Impure, in the same way Print is: calling it twice gives two different answers, and it is the single reason nothing in this machine is a pure function. It draws from the engine's own auto-seeded generator. That is what "seedless by construction" means here: there is no generator state for a program to hold.

Seed is not here, and its absence is the point. Seed(n) reseeds this same generator, so it does govern every word below. But this machine's whole design is that there is no seed to create, so it is the wrong place to document the word that creates one. Seed lives on math's page among the other arithmetic primitives. Calling it there makes Random, RandomRange, RandomInt, Shuffle and Sample reproducible from that point on.

Word Reference

Every word, built on the Rnd builtin

Random numbers effectful; not pure

WordDescription
Random()A random floating-point number in [0, 1) — 0 possible, 1 never. The Rnd builtin, given a plain name. Not a pure function: two calls can give two different answers.
RandomRange(lo, hi)A random floating-point number in [lo, hi). Stops the program with an error if hi is below lo.
RandomInt(lo, hi)A random whole number in [lo, hi], both ends included — a fair die between the two bounds. Stops the program with an error if hi is below lo.

Shuffling and sampling effectful; not pure

WordDescription
Shuffle(xs)A new list with the items of xs in uniformly random order. The original is untouched, and every ordering is equally likely.
Sample(xs, n)A new list of n items drawn from xs without replacement — no item is picked twice. Ask for more than the list holds and you get all of it, shuffled.
DropAt(xs, k)Helper the two above share: xs without its k-th item. Pure, and occasionally handy on its own.

Who Uses It

UserHow
neuralShuffle deals the mini-batches; RandomRange scatters the initial weights.

The Machines It Uses

None — a thin layer over the Rnd builtin, nothing else required.