Thread: Random Numbers
View Single Post
 
Old 06-08-2001, 06:25 PM
BillSoo's Avatar
BillSoo BillSoo is offline
Code Meister

Retired Moderator
* Guru *
 
Join Date: Aug 2000
Location: Vancouver, BC, Canada
Posts: 10,441
Default Random Numbers

A question that often arises is "How do I generate a random number?". The purpose of this article is to answer that question and look into some of the background behind random numbers.

Simply put, you can get a pseudo-random number by using the Rnd() function. This function returns a double precision number between 0 and 1, inclusive of 0 but exclusive of 1. This means that you could theoretically get a 0 from this function, but you cannot get a 1. You can get close though...like maybe 0.9999999999.

You can use this function to generate a pseudo-random number in any range. For instance, suppose you wish to get a random number between 1 and 100, inclusive. You could write:

i = INT(RND() * 100) + 1

To explain:
a) the RND() function returns a number between 0 and 1 (exclusive of 1)
b) we multiply by 100 to get a number between 0 and 100 (exclusive of 100)
c) we use the INT function to truncate the decimals and get just the integer portion. This gives us a number between 0 and 99 inclusive
d) We add 1 to get a number between 1 and 100

Similarily, if we wanted a number between 5 and 57, we could write:

i = INT(RND() * 53) + 5

A common error is to write the function like:

i = CINT(RND() * 99) + 1

The difference here is that the CINT function rounds fractions above 0.5 up instead of dropping them totally. So we still get a number between 1 and 100, but the probabilities are not equal. You are only half as likely to get a 1 or a 100 as any other number.

Let's look at a simpler example. A function to get a number between 1 and 4.

BAD WAY:
i = CInt( Rnd() * 3) + 1

In this example, rnd() can be a number between 0 and 1 so multiplying by 3 gives us a number between 0 and 3. We then use CINT to round the number to a whole number between 0 and 3, then add 1. So what's the problem? Well, the problem is that the odds are not equal.

Code:
From 	To	Integer		Range (Difference between From and To)
0	0.5	0		0.5
0.5	1.5	1		1
1.5	2.5	2		1
2.5	3	3		0.5

From this table, you can see that the chance of getting a 1 or 2 is twice as great as getting a 0 or 3.

GOODWAY:
i = INT(Rnd() * 4) + 1

Code:
From 	To	Integer		Range (Difference between From and To)
0	0.999...	0		1
1	1.999...	1		1
2	2.999...	2		1
3	3.999...	3		1
Technically, the range is actually 0.999999999 etc. but essentially, it is 1.

The main point is that the range is the same for all possible outcomes.

Why do I call them Pseudo random numbers instead just plain random numbers anyway?

The reason is that the rnd() function doesn't actually generate a "true" random number. Instead, it uses an algorithm that mixes numbers up and generates a number that appears to be "random", but in actuallity, it is one of a fixed sequence of numbers.

Typically, the function starts with a "seed" number. It uses this seed to calculate a pseudorandom number . This number is then used as the next seed. The initial seed value is the SAME everytime you start your application. So if you print out 5 random numbers, then next time you run your app, you will find that the numbers you get are the same as before.

To prevent this, you explicitly load a seed value with the RANDOMIZE statement. This command inputs a specified seed value, or if no value is given, it uses the current time as a seed value. This way, you get different results everytime you run the program.

You could put a RANDOMIZE statement before every call to Rnd if you want, but it's not necessary and may be counter productive. Remember, if you don't specify a seed value with Randomize, Rnd will use the previous number as a seed. So you have a pseudo random number as a seed to get the next random number. Whereas if you use Randomize all the time, you are using the current time as a seed. A value that is not nearly so random and is, in fact linear in growth and has a small amount of variability.

So how does the RND() function generate a pseudo random number then?

There are a number of methods, but the most common is the Linear Congruential Generator. Basically, it involves multiplying your seed by a constant, adding another constant, and using the MOD operator with yet another constant.

RandomNumber = (SeedValue * FactorConstant + OffsetConstant) MOD ModConstant.

To get a random number between 0 and 1, the number is then divided by ModConstant.

The actual values are fairly important here...

First, to get a reasonable amount of resolution, ModConstant should be quite large. If, for instance, you had a ModConstant value of 4 then your random number generator would only generate 4 possible values. eg. 0, 0.25, 0.5 and 0.75

Second, your FactorConstant should be a fairly large number. When you multiply it by seed value, you should get a number several times larger than modconstant.

Third, Offset constant should be less than ModConstant. Mainly because the mod operation makes larger values pointless. The main function of Offset is to prevent certain errors. For instance, suppose you had

RandomNumber = SeedValue * factorConstand MOD ModConstant.

If SeedValue happened to be 0, you would be locked into 0 forever.


Fourth, your function should not get locked into a cycle, as above. You have to pick your values carefully but a good rule of thumb is to use Prime Numbers.


That concludes this little article. I hope it helps...


"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder
__________________
"I have a plan so cunning you could put a tail on it and call it a weasel!" - Edmund Blackadder

Last edited by loquin; 10-19-2005 at 06:06 PM. Reason: changed "it" to "Rnd" to clarify that Rnd uses prior random number as the seed.
Reply With Quote