Using JavaScript to Generate Random Content
I. Generating (Pseudo)Random Numbers
The Math object in JavaScript contains a number of methods that may be used
to perform various mathematic operations. Among these is random(), which
generates an apparently random number between 0 and 1.
To generate a random-number value and assign it to a variable, use a statement
like this:
someVariable = Math.random();
Technically speaking
the output of Math.random() is only pseudo-random, because the
technique used to generate its values will display a detectable pattern if it is
repeated enough times. We're talking millions of repetitions, so for our purposes
pseudo- is as good as the real thing.
Below is the output of Math.random() invoked ten times. Click
the button to re-run the script and generate new values.
(View source for this page to examine the script. Because the scripts on this
page work with form elements, they occur at the bottom of the document.)
By themselves, long decimal numbers between 0 and 1 are not much help in generating
content for Web pages. To select from a range of words, images, or other elements,
you'll probably want to choose items from an array. The indices of an array are
integers. Two steps are required to get from the raw output of Math.random()
to useful integer values.
First, multiply the output of Math.random() by the length of the array
from which you wish to choose (i.e., the maximum number of choices). For an array of
ten items, we would use the statement:
someVariable = Math.random()*10;
But when you multiply an integer like 10 by a messy decimal number like 0.427009978920274,
the result is itself a messy decimal (4.27009978920274, to be exact), and we need
a simple integer.
To get to an integer value, apply another method of the Math object called
floor(). This method rounds the messy decimal down to the nearest
integer. In the case above, it yields 4. The statement looks like this:
someVariable = Math.floor(Math.random()*10);
Be careful with those embedded parentheses; you have to get them just right.
Here's a tenfold iteration of Math.random() again, this time multiplied
by 10 and rounded down with Math.floor():
If you study the output above, you'll see that the random values may
include 0 but never include 10. That's because we're always rounding down.
Since arrays are numbered from 0 to one less than the total value--from 0 to 9 for a ten-element array--the
numbers generated by the random function always correspond to indices of the array.
This lets us replace random numbers with random values:
II. Constraining Random Values
If you refresh the example above a few times, you should notice that it often contains duplicate items; sometimes these duplicates occur one after another.
This happens because we're choosing from a relatively small range of numbers (there are
15 words in the content array) and because nothing prevents JavaScript from generating
the same random integer twice in succession. (In other words, the problem lies
with the Math.floor operation, which takes nearly unique decimals and rounds
down to a much smaller range of integers.)
You can dampen down, if not completely eliminate repetition from your randomizing functions
by keeping track of the function's past performance and screening out repeat values.
In the interest of simplicity, we'll demonstrate this only for first-order repetitions,
that is, two identical items in direct succession.
Here's an example of random selection constrained against immediate repetitions:
You'll still see repetitions in the example above, but they will never be immediate
repetitions: there will always be at least one different item separating two that are
identical.
To achieve this effect, we have to add three features to the simple, unconstrained
randomizer.
First, we initialize a pair of variables to the same value:
var randy = 0;
var randyOld = 0;
These initialization statements must occur outside the function body:
you'll see why in a moment.
The first variable, randy, holds the current random value.
The other variable, randyOld, is used to store the random value
generated on the previous run of the function. For the first run we set
both these values to 0 (or to any number you like so long as both variables
have the same value).
The second addition involves the statement that generates a random number for
randy. In the unconstrained version of the function, the statement is:
randy = Math.floor(Math.random()*contentArray.length);
In the constrained case, however, we place this statement within a
special construction called a while loop:
while(randy == randyOld)
{
randy = Math.floor(Math.random()*contentArray.length);
}
A while loop executes so long as its conditional clause evaluates to true.
In this case, so long as the random number we generate is the same as the random
number we previously generated, we generate a new random number. In other words,
we keep trying numbers until we hit one that is not the same as the one that came
up previously. (This is why both randy and randyOld must have the same
value on the first run of the function: we only get a new, or in this case first
random value if the two variables are identical.)
One more refinement makes this trick complete. Outside the while loop
we add this statement:
This statement records the current random number and stores it in the variable
that will be used for comparison on the next run of the function. This line
lets the function remember its previous behavior each time through.
III. Extensions of Constraint
Using the logic explained above, it is possible to constrain a random number
generator against several orders of repetition, so that it is guaranteed to
produce non-repeating values on every three, four, five, or more successive
iterations. We can simply create more storage variables (randyOlder
and randyOldest, for instance), create a compound test condition for the
while loop, and push every new random value back through the record
variables as we go.
Somewhere around the fourth or fifth order of constraint, this procedure would
probably become too cumbersome for most people--it's awkward to write a test
condition with five or six compound cases. At this point one might want to
use an array instead of separate variables to store the record values, which means
writing code to scan that array; but
practically speaking, if the range of content choices is large enough,
there's usually no reason to constrain the randomizer to anything greater than
three or four unique selections.
To see random functions in action, have a look at the
demonstration page for this week.
Reload the page for new content. Do not expect coherence.
This demonstration is a single page, so you can see all its pieces simply
by viewing source; but the page code may also be downloaded from
productionShare on Crow.
|