Random objects
STACK can generate structured random objects. STACK provides a Maxima 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.
Users may also systematically deploy all variants of a question in a simple manner.
rand()
STACK provides its own function rand()
.
rand(n)
generates an integer between and .rand(n.0)
generates a floating point number between and . 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 functionround()
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 ofu
.random_subset_n(u,n)
returns a random subset ofu
withn
elements (if possible).random_ne_subset(u)
returns a non-empty random subset ofu
.
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 .rand_with_step(-5,5,2)
returns a random number from the set .rand_with_step(-5,3,3)
returns a random number from the set .
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 .rand_with_prohib(-5,5,[-1,0,1,sqrt(pi)])
returns a random number from the set .rand_with_prohib(-5,3,[-5/2,a])
returns a random number from the set .
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 we need to collect together the 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 variables. The second line tidies up the unary minus. For more details of this, see simplification.
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 , 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);