# Random objects

STACK can generate structured random objects.  STACK provides a [Maxima](Maxima_background.md) function `rand()` which can be used in the question and answer variables.

STACK creates pseudo-random numbers from a definite seed.
This ensures that when a particular student returns they see the same variant of the question.
(Note to site maintainers: if you upgrade your Maxima version mid-way through an academic cycle, then there is no gurantee that the random numbers will remain the same.  It is unlikely Maxima will change its random number generation between versions, but if it important to you please check first!)

For the purposes of learning and teaching, we do not need an algorithm which is statistically perfect. We are much more interested in simplicity, efficiency and reproducibility across platforms. Hence, we adopt pseudo-random numbers.

It is very important to test each random version a student is likely to see and not to leave this to chance.  To pre-generate and test random variants see the separate documentation on [deploying random variants](../STACK_question_admin/Deploying.md).

Users may also [systematically deploy](../STACK_question_admin/Deploying_systematically.md) all variants of a question in a simple manner.

## rand() {#rand}

STACK provides its own function `rand()`.

* `rand(n)` generates an integer between \(0\) and \(n-1\).
* `rand(n.0)` generates a floating point number between \(0\) and \(n\).  It is probably more useful to use something like a=float(rand(1000)/1000)
  to obtain an accurate number of decimal places.  An alternative is to use the [Maxima](Maxima_background.md) function `round()`
* `rand([a,b,...,z])` makes a random selection from a list.
* `rand({a,b,...,z})` makes a random selection from a set.
* `rand(matrix(..))` applies rand to each element of the matrix.

STACK provides the following functions for random generation of sets.

* `random_subset(u)` returns a random subset of `u`.
* `random_subset_n(u,n)` returns a random subset of `u` with `n` elements (if possible).
* `random_ne_subset(u)` returns a non-empty random subset of `u`.

There are also Maxima's random functions.  For example, to create a random list use `random_permutation`.

It is probably much better **not** to use conditional statements when creating random objects.
For example, if you would like to create a random small prime number, try

    p : rand([2,3,5,7,11,13,17,19]);

This might not appear to be the neatest mathematical solution, but it is probably the most reliable.

### rand_with_step(lower,upper,step) ###

Returns a random number from the set `{lower, lower+step, lower+2*step, ... , final}`. The examples below explain behaviour the best.
Examples:

* `rand_with_step(-5,5,1)` returns a random number from the set \(\{-5,-4,-3,-2,-1,0,1,2,3,4,5\}\).
* `rand_with_step(-5,5,2)` returns a random number from the set \(\{-5,-3,-1,1,3,5\}\).
* `rand_with_step(-5,3,3)` returns a random number from the set \(\{-5,-2,1\}\).

The function `rand_range(lower,upper,step)` does the same thing.

### rand_with_prohib(lower,upper,list) ###

Returns a random integer from the set [lower,upper] such that it cannot be any value in `list`.
This list can include values which are also random variables, for example, generated by `rand_with_step`.
Examples:

* `rand_with_prohib(-5,5,[0])` returns a random number from the set \(\{-5,-4,-3,-2,-1,1,2,3,4,5\}\).
* `rand_with_prohib(-5,5,[-1,0,1,sqrt(pi)])` returns a random number from the set \(\{-5,-4,-3,-2,2,3,4,5\}\).
* `rand_with_prohib(-5,3,[-5/2,a])` returns a random number from the set \(\{-5,-4,-3,-2,-1,0,1,2,3\}\backslash\{a\}\).

This can be used with matrices, to generate a matrix with non-zero entries for example.  The unnamed function in this example ignores its arguments.

    matrixmap(lambda([ex],rand_with_prohib(-5,5,[0])),zeromatrix(5,5));

To create a matrix of a random size you can use Maxima's `makelist` function, e.g.

    M1:apply(matrix, makelist(makelist(2^n/3^m, n,1,4), m,1,3));

### rand_selection(ex, n) ###

Returns a list containing a random selection of `n` different items from the list/set `ex`.  If `ex` contains duplicates, then the result may also contain duplicates.

### rand_selection_with_replacement(ex, n) ###

Returns a list containing a random selection of `n` items from the list/set `ex`.

## Generating random polynomials

Here is an example which generates a random polynomial, of degree 5, with coefficients between 0 and 6.

    apply("+",makelist(rand(7)*x^(k-1),k,6));

## Generating random expressions which needs to be "gathered and sorted"

It is relatively common to want to be able to generate random expressions which need to be "gathered and sorted".  For example in \(2y-y+3y+1\) we need to collect together the \(y\) terms.

    simp:false;
    p:apply("+",makelist(ev(rand_with_prohib(-5,5,[0])*y^rand(2),simp), ev(rand(6)+2,simp)));
    p:unary_minus_sort(p);

Now, the output from the first expression will be a random expression in constants and \(y\) variables.   The second line tidies up the unary minus.  For more details of this, see [simplification](Simplification.md).

    4*y+5*y+(-2*y)
    4*y+5*y-2*y

## Random objects with corresponding information

It is often necessary to generate a random object with a number of separate aspects to it.  For example, if you have scientific data and you need to include this in a question.

    t:rand(5)+3;
    idx:rand(3)+1;  /* Array indexes in Maxima start at 1, rand(n) returns 0,...,n-1.  */
    l1:["Mercury","Earth","Mars"];
    l2:[3.61,9.8,3.75];
    p:l1[idx];
    ta:t*l2[idx]/(4*%pi^2);

The question text can then be

    A pendulum is located on {@p@}. What length should the pendulum have in order to have a period of {@t@}s?

This indexing with the variable `idx` is quite robust.  Note that indexes in Maxima start at \(1\), whereas `rand(n)` could return zero.

Another option is to use `rand()` on a list of lists, allowing to group the information of an object in a slick way:

    t:rand(5)+3;
    [p, g] : rand([["Mercury",3.61], ["Earth",9.81], ["Mars",3.75]]);
    ta:t*g/(4*%pi^2);

Here, `rand()` will return one random list of the given lists, say `["Earth",9.81]`. The assignment `[p, g] : ["Earth",9.81]` then works as one would expect, namely just as `p : "Earth"; g : 9.81;` would.

## Random objects satisfying a condition

It is often necessary to create random objects which satisfy constraints.  For example, if you want to randomly generate a "small" prime number, just select one from a list.

    p:rand([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]);

It is much better to (i) enumerate specific cases or (ii) reverse engineer the question to avoid conditional statements than randomly generate examples and "hope" one pops up eventually.  The reason is that the pseudo-random number generator will repeat the process from a seed _every time_ the question is generated!  If you put in loops, this could risk delays and time-outs etc.

The following is NOT RECOMMENDED, but enough people have insisted on doing it to document this approach.

If you must (and you risk an infinite loop of course....) you can use Maxima's `for` command.  A simple example is as follows.

    q:1;
    for k while not(is(primep(q))) do block(q:rand(98)+1);


## See also

[Maxima reference topics](index.md#reference)
