Skip to content
Classroom

Randomizers

Randomization makes each student see different numbers in a question while keeping the question solvable. These are the randomizer macros the question engine actually exposes.

These are not PHP functions

Question control code looks like PHP but runs through the engine's own interpreter against a fixed allowlist. The core randomizers are below (the allowlist also has jointrandfrom($list1, $list2, …), which picks the same random position from each list, and nonzerodiffrrands). PHP's own shuffle(), array_rand() and mt_rand() are not — and neither are the randint / pickone / pickunique names that earlier versions of this page described. They never existed.

Random numbers

rand($min, $max)

A random integer from $min to $max, inclusive.

$a = rand(2, 9)       // integer from 2 to 9
$n = rand(-5, 5)      // integer from -5 to 5

nonzerorand($min, $max)

The same, but never returns 0. Use this instead of a retry loop when zero is the only value you need to exclude.

$a = nonzerorand(-5, 5)   // never 0

rrand($min, $max, $step)

A random value from $min to $max in increments of $step. This is how you get decimals — rand only ever returns integers.

$rate  = rrand(0.01, 0.15, 0.01)   // 0.01, 0.02, … 0.15
$price = rrand(5, 20, 0.25)        // 5, 5.25, 5.5, … 20

All three arguments are required; rrand errors if $step is missing or not positive. nonzerorrand($min, $max, $step) is the never-zero form.

rands($min, $max, $n, $ord)

An array of $n random integers. Values may repeat.

$ord orders the result: 'def' (default, the order drawn), 'inc' ascending, 'dec' descending.

$vals = rands(1, 6, 3)            // e.g. 4, 4, 1
$sorted = rands(1, 20, 4, 'inc')  // e.g. 3, 7, 7, 18

rrands($min, $max, $step, $n, $ord) is the stepped form, and nonzerorands / nonzerorrands the never-zero forms.

diffrands($min, $max, $n, $ord)

An array of $n distinct random integers — the macro to reach for instead of a do/while retry loop.

$two = diffrands(1, 9, 2)     // two different integers, e.g. 3, 8

nonzerodiffrands($min, $max, $n, $ord) additionally excludes zero. diffrrands is the stepped form.

Picking from a list

randfrom($list)

One element chosen at random. Takes exactly one argument — a list, not a series of values.

$sign = randfrom(array(1, -1))
$word = randfrom(array("increasing", "decreasing"))
$coeff = randfrom(array(2, 3, 5, 7))

A comma-separated string works too, since randfrom runs a non-array argument through listtoarray first:

$word = randfrom("increasing,decreasing")

randsfrom($list, $n, $ord)

An array of $n picks from the list, with repetition.

$rolls = randsfrom(array(1,2,3,4,5,6), 3)   // may repeat, e.g. 2, 5, 2

diffrandsfrom($list, $n, $ord)

An array of $n distinct picks from the list — the macro that does what the old pickunique claimed to.

$choices = diffrandsfrom(array(1,2,3,4,5,6), 3)   // never repeats, e.g. 4, 1, 6

Shuffling

singleshuffle($array)

Returns the array in a random order.

It returns a new array — it does not shuffle in place

PHP's shuffle() modifies its argument. singleshuffle does not, so a bare singleshuffle($options) statement has no effect. Assign the result.

$options = array("increasing", "decreasing", "constant", "undefined")
$options = singleshuffle($options)

An optional second argument truncates the result, which is a compact way to draw a random subset:

$three = singleshuffle($options, 3)   // 3 of them, shuffled

jointshuffle($array1, $array2)

Shuffles two equal-length arrays the same way, so paired entries stay together. It does not update an index you stored before the shuffle, so recompute it afterwards. A multiple-choice question does not need it: the engine already shuffles $questions and grades against the unshuffled index.

$opts = array("3", "5", "7", "9")
$fb   = array("no", "no", "yes", "no")
$opts,$fb = jointshuffle($opts, $fb)
$correct = arrayfindindex("yes", $fb)   // where "7" landed

It errors if the two arrays are different lengths.

Names and places

The engine also ships randomizers for realistic word problems:

Macro Returns
randname(), randnames($n, $gender) person names
randmalename(), randfemalename() a name of that gender
randmalenames($n), randfemalenames($n) arrays of names (a single string when $n is 1 — true of every plural macro in this table)
randnamewpronouns($g) a name together with matching pronouns
randcity($country), randcities($n, $country) city names; $country is "USA" (default) or "Canada" only — anything else returns ""
randstate($country), randstates($n, $country) state names (provinces/territories for "Canada"); same "USA"/"Canada" restriction
randcountry(), randcountries($n) country names
randpythag($min, $max) a Pythagorean triple

randnamewpronouns($g) returns a six-element array — name, then the subject, object, possessive-determiner, possessive-pronoun and reflexive forms — so generated prose agrees with the name drawn:

$name,$sub,$obj,$posdet,$pospro,$refl = randnamewpronouns()

Pass 'neutral' for they/them:

$name,$sub,$obj,$posdet,$pospro,$refl = randnamewpronouns('neutral')

Avoiding degenerate values

Prefer the macro that already excludes the bad case over a retry loop:

$a = nonzerorand(-5, 5)      // not: a loop re-drawing until $a != 0
$a,$b = diffrands(1, 9, 2)   // not: a loop re-drawing until $b != $a

Where no macro covers the condition, a postfix where guards the assignment. It re-draws that one value until the condition holds, up to 200 attempts, and takes an optional else { fallback } for when it never does:

$b = rand(-6, 6)
$c = rand(-9, 9) where ($b^2 - 4*$c >= 0)

^ is exponentiation here — the interpreter rewrites a^b to safepow(a, b). If a line combines both postfix forms, where must come before if.

Seeding and reproducibility

Every student gets their own seed, and the same seed always regenerates the same values — that is what lets an instructor reopen a student's attempt and see the exact numbers they were graded on.

The seed is engine state, not a variable you can read: $seed is on the interpreter's disallowed list and referencing it fails the question with Eeek.. unallowed var. Use setseed if a question needs to control randomization explicitly.

A full randomized setup

// Two distinct nonzero integers
$a,$b = diffrandsfrom(array(-5,-4,-3,-2,2,3,4,5), 2)

$product = $a * $b      // c, the constant term
$bcoef = -($a + $b)     // b, the coefficient of x: (x - a)(x - b) = x^2 - (a + b)x + ab

Question text:

<p>Find a quadratic with roots `$a` and `$b`.</p>
<p>Write it in the form `x^2 + bx + c`.</p>