Random

version 1.0
© 2003 Bernard Schutz

Study random walks: compute the mean distance traveled. This simulates the way a photon moves outwards from the energy-generating center of a star to its surface.



Contents


Description of Random

The unit called Random implements the Java program for computing a random walk, as described in Investigation 8.7. A random walk is defined, at least in this program, as a walk consisting of steps that all have the same length but in which successive steps are taken in random directions. The next result is a walk that gradually moves away from the origin.

The result of this program is used in the discussion of the way photons randomly walk outwards from the center of the Sun. But the mathematics of ransom walks apply to many other situations. The basic result, that adding together a lot of random values produces an expected result that increases as the squre toot of the number of items, is widely applicatble.


Using Random

Simply drag the Random icon from the toolbox into the working area, drag the SGTGrapher unit into the area, and connect the two if Triana has not already done so. If you press the Start button then the program will execute with its default settings, producing a curve giving the expected distance of a random walk against the number of steps. Since the resulting curve will be a parabola, it will be hard to tell if the results match the predictions if you look at the curve with linear axes. But if you set both axes to logarithmic, then the curve will be a straight line of slope 1/2. Set the axes to be logarithmic in the Plot menu of the SGTGrapher graph window.

The parameter window of Random (shown here) allows the user to choose just two parameters. One is the maximum number of steps in a random walk. The graph will give the mean distance gone in a number of steps for walks up to and including this number. The second is the number of times each walk is performed, in order to get the average length. There is a trade-off here: the larger the user sets this number, the more accurate will be the average result obtained, but the longer will be the computer run-time.
 


Understanding Random

Random introduces a number of interesting adn important computer techniques. We will treat them one by one. The main part of the program is structured as three nested loops. By "nested" is meant that, for each iteration of the outer loop, the inner loop runs through all its iterations completely. The loops do different things. The innermost loop is the random walk itself: we take random steps (described below) and find out how far we have gone. This is inside a loop whose job is to repeat this walk a certain number of times (as given by the user) so that we can get a fair average value of the net distance gone. These two loops are inside a third loop that determines how many steps are taken in the random walk. This iterates through a succession of walks of different length so that we can get a plot of the average length versus the number of steps.

The main work is done by the innermost loop. Here we make use of the Java function Math.Random() to generate the random numbers. Every time it is called it returns a value that is randomly distributed in a random way between 0 and 1. (Of course, no computer can do perfectly random things, but the way these numbers are generated makes them random enough for our purposes. They are technically called pseudo-random numbers.) The idea is to allow the computer to generate a "step" (a displacement vector) from the current location to the next one that is random in all three coordinates. Each coordinate component of the step should be randomly chosen in a uniform way between -1 and +1, so that the step can go backwards or forwards. To get this we do not need a different random number generator: the expression 2 * Math.random() - 1 produces a number with the desired distribution. (If this is not obvious to you, consider that 2 * Math.random() is randomly distributed between 0 and 2, and then if we subtract 1 from it the range of the distribution goes from -1 to +1.). The code for the loop that generates the random walk is
                for ( m = 0; m < nSteps; m++) {
                    dx = 2 * Math.random() - 1;
                    dy = 2 * Math.random() - 1;
                    dz = 2 * Math.random() - 1;
                    stepSize += Math.sqrt(dx*dx + dy*dy + dz*dz);
                    x += dx;
                    y += dy;
                    z += dz;
                }
This uses a loop index m, which runs from 0 to nSteps-1. Now, the value of nSteps is set in the outermost loop, which we will describe below. But for now, it is just the total number of steps in the random walk. Each step has components dx, dy, and dz. Although the code for each of these is the same, remember that each time Math.random() is invoked, it produces a different (randomly different) number. Then we do two things with these components. The first is to compute the total length of the step and add it to the variable stepSize, which keeps track of the total distance that the walk achieves. The second is that we add the individual step components to the variables x, y, and z, respectively, which are the components of the net displacement vector for all the steps so far. All the variables stepSize, x, y, and z are initialized to zero before the loop begins.

This loop is nested in another loop that executes this walk over and over again and averages the results. It begins with
            for ( k = 0; k < nTrials; k++) {
                x = 0.;
                y = 0.;
                z = 0.;
                stepSize = 0;
in which we see that there will be nTrials iterations, so that the walk described by the innermost loop will be repeated nTrials times. The value of nTrials was set by the user in the parameter window. The remaining lines here simply initialize variables to zero, as described above. This code is immediately followed by the innermost loop above, and after that there are the lines
                avgDist[j] += Math.sqrt(x*x + y*y + z*z)/(stepSize/nSteps);
            }
which finish the loop (at the closing "}"). The important line here is the computation of the average of the net distance of the walks. The expression on the right-hand-side of this assignment statement is the total net distance travelled (the length of the net displacement vector whose components are x, y, and z) divided by the average size of an individual step (which is the value of stepSize/nSteps because stepSize has accumulated the total distance travelled in all the steps and therefore dividing it by nSteps produces the average size of the steps in this walk).  This is our measure of the length of the walk: the net distance travelled for a given stepsize. This value is then added to a variable called avgDist[j], which is an array element that will eventually hold the average of the lengths of all the walks containing nSteps steps. For now, it accumulates the total length; it will be divided by the number of trials nTrials later to get the average.

The outermost loop controls the number of steps in each experiment. Since we want to investigate how the net distance travelled depends on the number of steps, we need to have many experiments with different numbers of steps. The loop to control this begins with the code
        for (j = nWalks-1; j >= 0; j-- ) {
            lengths[j] = nSteps;
We can see that there will be nWalks experiments; the value of this variable is set before the loops begin, using the value maxSteps that the user specifies in the parameter window. (Exactly how this is done is described below.) The unusual thing about this loop is that it runs "backwards": the counter j starts with its maximum value and runs down to zero. The reason for this will become clear below. Also before this loop begins the program has already defined the two arrays lengths[] and avgDist[] to have nWalks elements each. The first line of this loop then sets the current (j'th) element of the array lengths to nSteps. This will be the independent variable of our output: the number of steps in the random walks of the current experiment.

These lines are immediately followed by the two nested loops described above, and they are then followed by
            avgDist[j] /= nTrials;
            nSteps /= 2;
            if ( nSteps <= 1 ) break;
        }
which closes this outer loop. The first line does the division the converts the value of avgDist[j] into a true average. This will be the dependent variable in the output from this unit: the average net distance travelled in a random walk with nSteps steps. The second line then reduces the number of steps by a factor of two so that the next iteration of the outer loop tests random walks with just half as many steps as the current one. This reduction in the number of steps is the reason that the counter j runs backwards. That ensures that small j corresponds to small numbers of steps and large j to large numbers of steps. The final line is a safety measure, to ensure that if we reach trials with just one step, or even fewer than 1, we don't attempt to execute the experiment!

To complete our understanding of the program we must show how the total number of walks nWalks.  The user fixes the maximum number of steps in a random walk in the parameter window, and this is stored by the program in the variable maxSteps. We want to perform walks with this number of steps (at least approximately) and then keep dividing the number of steps in half to get other experiments. The number of times we can divide this number in half and still get a value greater than 1 is given by the logarithm of this number to the base 2. If the logarithm is not an integer (and it is usually not an integer) then we round it down to the nearest integer to get the number of times we can divide the number by 2 for our experiments. Now, Java does not have a base-2 logarithm function, but one can compute the base-2 log from Java's natural logarithm function Math.log() by dividing by the natural logarithm of 2. This leads to the follwing line in the program before the loops begin:
        int nWalks = (int)Math.floor( Math.log(maxSteps)/Math.log(2.) );
In it we construct the logarithm to the base 2 of maxSteps and then round it down with the Java function Math.floor, which produces the "floor" of its argument, which is the largest integer smaller than its argument. But this Java function returns its value as a double, not an integer, so we place "(int)" in front of it to convert it into an integer value, and then we assign it to nWalks. (The code "(int)" is called a cast. It performs a conversion from one type of data to another.)

This describes the basic idea of this random-number code.
 


Suggested modifications of Random

There is nothing special about our definition of the "length" of a random walk being the length of the net displacement vector divided by the average step-length. The same relationship, that length is proportional to the square root of the number of steps, will be found if you change to any other reasonable definition, such as leaving out the division by the average step-length. You could also change the random number distribution from uniform to Gaussian and run the program again; you should still see the same square-root behavior. (You will have to use the Java function Random.nextGaussian() to generate your Gaussian numbers.)

If you want to change the program you will have to re-compile it, as explained by the help file Using Triana for Gravity from the ground up.


Listing of the Java code for Random

Preliminary definitions of parameters and constants

    /*
      nTrials is the number of times each random walk is performed. The
      distance achieved by the walk will be averaged over this number of
      walks. This is set by the user in the user interface window.
    */
    private int nTrials;

    /*
      maxSteps is the maximum number of steps in any of the random walks.
      The program will perform walks of this length and shorter ones to
      find the trend in the distance as a function of the number of steps.
      This is set by the user in the user interface window.
    */
    private int maxSteps;
 

Program code

    public void process() throws Exception {

        /*
          Variables that we need each time the code is executed.
          - nWalks is the number of lengths of random walks that we will
            experiment with. The largest length is given by maxSteps.
            Smaller walks are obtained by dividing the number of steps
            of the previous walk by 2, rounding down to an integer
            if necessary. Thus, if maxSteps is 9 then there will be a
            walk of 4 steps and one of 2 as well. (We do not perform
            walks of length 1!). To find nWalks we take the logarithm
            of maxSteps to the base 2 and round it down to the nearest
            integer. The compuation below uses Java's logarithm method,
            which computes the natural logarithm. To get the logarithm
            to the base 2, we divide by the natural logarithm of 2.
          - arrays avgDist and lengths hold the data that will be
            output. For each length of walk, array lengths holds the
            length (number of steps), and array avgDist holds the
            average distance achieved by all nTrials walks of that number
            of steps.
          - j and k are integers needed for loops.
          - nSteps will be used to keep track of the number of steps
            in the current type of walk.
          - x, y, and z will accumulate the distances moved in the three
            directions.
          - dx, dy, and dz will hold the distances moved in a single step.
          - stepSize will be used to find the average step size.
        */
        int nWalks = (int)Math.floor( Math.log(maxSteps)/Math.log(2.) );
        double[] avgDist = new double[nWalks];
        double[] lengths = new double[nWalks];
        int j, k, m;
        int nSteps = maxSteps;
        double x, y, z, dx, dy, dz;
        double stepSize;

        /*
          Now start the main loop over the types of walks. Each type
          has a fixed number of steps. Within this loop we will perform
          each type of walk nTrials times and take the average of the
          resulting distances. This loop runs backwards, because we
          start with the maximum number of steps and keep reducing it
          unitl we get to walks with just 2 or 3 steps.
        */
        for (j = nWalks-1; j >= 0; j-- ) {
            lengths[j] = nSteps;
            /*
              Here we begin the independent trials for this number of
              steps. Each trial begins at the origin. Use stepSize to
              accumulate the total of the step-lengths and later
              divide it by nSteps to get an average step-length.
            */
            for ( k = 0; k < nTrials; k++) {
                x = 0.;
                y = 0.;
                z = 0.;
                stepSize = 0;
                /*
                  We perform the random walk here. We generate step-lengths
                  in each direction using the Java random-number generator,
                  Math.random(), which returns a pseudo-random number
                  uniformly distributed between 0 and 1. We multiply it by 2
                  and subtract 1 to get a number uniformly distributed
                  between -1 and 1.
                */
                for ( m = 0; m < nSteps; m++) {
                    dx = 2 * Math.random() - 1;
                    dy = 2 * Math.random() - 1;
                    dz = 2 * Math.random() - 1;
                    stepSize += Math.sqrt(dx*dx + dy*dy + dz*dz);
                    x += dx;
                    y += dy;
                    z += dz;
                }
                /*
                  Compute the scaled distance achieved by the walk by taking
                  the total distance from the origin and dividing by the
                  average step-length, which is stepSize/nSteps. Use the
                  array avgDist[j] to accumulate the sum of these scaled
                  distances.
                */
                avgDist[j] += Math.sqrt(x*x + y*y + z*z)/(stepSize/nSteps);
            }
            /*
              Divide the accumulated total distance by the number of trials
              to get the average distance in units of the step-length
            */
            avgDist[j] /= nTrials;
            nSteps /= 2; // integer division leaves no remainder
            if ( nSteps <= 1 ) break;
        }

        Curve out = new Curve( lengths, avgDist);
        out.setTitle("Random walk");
        out.setIndependentLabels(0,"number of steps");
        out.setDependentLabels(0,"average net distance");
        output( out );
    }



Return to index of all computer programs.