Logo

Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.

RAND()

Ain’t life Rand? This function returns random numbers. Actually, they’re technically known as “pseudo-random” numbers. This means that you can produce the same sequence over and over. It’s important because, when testing, you need the ability to work on the same data.

Usage

nReturnValue = RAND( [nSeed] )

Parameter

Value

Meaning

nSeed

Any negative number or zero

Seeds the function with a value based on the system clock.

Any positive number

Seeds the function with the passed value.

Omitted

Seeds the function based on the last value returned by it. If this is the first call, the seed is 10001.

All random-number functions need a seed value based on which the function computes a random result. Passing the same number should (and does, in FoxPro) return the same “random” number. This ability allows you to test on the same data set.

To get the most random results, pass a negative number or zero as the initial seed. That seeds the function based on the system clock. On subsequent calls, omit the seed and FoxPro uses the previous number as a seed. You’ll always get the same sequence if you start with the same value, but with a negative initial seed, that’s very unlikely.

To mathematicians, a “random number” is always between 0 and 1, and that’s what RAND() returns. Since you’ll usually want a random integer in some range, you need to scale the result appropriately. The formula to convert the result to an integer between 0 and some boundary seems simple:

INT(nBoundary*RAND())

but things are seldom what they seem.

In all versions of FoxPro before VFP 5, RAND() returns both 0 and 1, but very rarely. Using the formula above, you get many fewer occurrences of nBoundary from a sequence of calls to RAND() then any other number in the range. You can fix that by adding 1 to the result (INT(nBoundary*RAND())+1), but then you get too few zeroes. There’s no good solution to the problem. The best solution is for RAND() to never return one or the other of the endpoints (that is, RAND() should either never return 0 or never return 1).

Good news. Starting in VFP 5, that’s exactly how it’s done. RAND() never returns 1, so you can use INT(nBoundary * RAND()) to get values from 0 to nBoundary-1 or INT(nBoundary * RAND()) + 1 to go from 1 to nBoundary. No more ugly correction code that fouls up statistics.

One question you’d think we’d hear a lot is “How random are the results of RAND()?” There are various tests you can run to test the randomness of a sequence of random numbers. The downloads (www.hentzenwerke.com) contains two tests. Each generates a sequence of random numbers and then computes a statistic about it. The first is the Chi-Squared test. It returns a value you can look up in a table (try a statistics textbook, or check out Excel’s CHIDIST() function) to see how random RAND() is. The second test is called the “Coupon Collectors” test. The return value is the average number of random numbers you have to generate in order to be sure you’ve gotten at least one of each value in a range.

Example

* Here's the right way to use RAND() in a program, if you
* want a truly random sequence.
* First, seed RAND() with -1 (or 0 or any other negative number)
RAND(-1)

* Then, use RAND() with no seed inside whatever loop
* you're performing.
FOR nCnt = 1 TO nIterations
  nRand = RAND()
  * Now do something with nRand
ENDFOR