Hypermedia Production Banner

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:

    randyOld = randy;

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.




University of Baltimore Logo
Copyright © 2000 Stuart Moulthrop