Randomness in VB6 (Part I)

Random Number Generation

Exploring the magical world of VB, you may have heard of the Rnd() function. What this function does is gives you a random Single value that is less than 1, but greater than or equal to 0. Think of the numbers that you can get from Rnd as a range. The range here would be [0.0 , 1.0}. (The parenthesis at the end means that 1.0 is never found, but the bracket at the front means that 0.0 can be found.) The first five random numbers that I receive when I first start my program: 0.7055475, 0.533424, 0.5795186, 0.2895625, 0.301948 All nice and within the range. Now, according to the range, we should get a 0 from Rnd() eventually. Let's see: Dim N As Long Do Until Rnd = 0 'Check if the random number is a 0. N = N + 1 'Count the number of calls to Rnd() made. Loop MsgBox N 'If we get here, ok. Using this, we get a 0 at 6488063. (It overflowed with an Integer) Checking for 1 will catch you in an endless loop (you'd have to hit Ctrl+Break to get out, don't Ctrl+Alt+Delete your way out).

Larger Random Number

Now, there is not much you can do with a random number that is less than one. But, with numbers that are less than 1.0, you can also treat them like a percent. So, to increase the range, you multiply the Rnd() function by a number. 2 * Rnd And now, you have a range from [0.0 , 2.0}. Similarly, 10 * Rnd [0.0 , 10.0), 20 * Rnd [0.0 , 20.0), and M * Rnd [0.0 , M).

Random Integers

Now, the range is larger and more useful. But, the numbers still have decimal points. 14.11095, 10.66848, 11.59037, 5.791249 (with 20 * Rnd) How would the numbers be converted to integers? There is an extensive bundle of integer conversion functions available: Int(), Fix(), CInt(), CLng() The latter two, a common mistake, will round the decimal up, which we do not want. 19.7 is within the range for 20, but rounding 19.7 up gives us 20, which is not in the range. Furthermore, the only way to get zero would be if 20 * Rnd returned a number in the range [0.0, 0.5), while getting a 1 is easier [0.5, 1.5), making the odds of getting 0 less than that of 1. The first two, Int() and Fix() differ from each other only by their treatment of negative numbers. Int(20 * Rnd) and Fix(20 * Rnd) Both of these functions round the decimal down to an integer, preserving the range [0 , 20) and each integer has an equal probability of appearing. Int rounds a number to a lesser number, while Fix rounds a number to a smaller number (-5 would be smaller than -6). Also, note that since we are working with integers, the range [0 , 20) is the same as the range [0 , 19], and now the number that multiplies Rnd is the number of integers in the range.

Supplying a Lower Bound

The lower number of all of our ranges so far is 0. If we wanted to make a lower bound, we might just add a number to the existing expression: Int(20 * Rnd) + 10 Now our range is [10, 29]. Notice the upper bound has changed. To push the upper bound back down to 19 for [10 , 19], we would not subtract by 10, since we just added 10. The size of the range will have to be altered (it should be 10, since there are 10 integers in the range [10 , 19]). Int(10 * Rnd) + 10

Generating Random Stuff in General

Two handy formulas for generating random integers: Int(Count * Rnd) + Lowest 'Generates 'count' random numbers starting at 'lowest'. [Lowest , Lowest + Count) Int((Highest + 1 - Lowest) * Rnd) + Lowest 'Generates random numbers in the range [Lowest , Highest] or [Lowest , Highest + 1) Generating a random uppercase letter requires us to generate a random number from [65 , 90] (this 26 numbers). X = Chr$(Int(26 * Rnd) + 65) And similarly for lowercase characters, the lowest will be 97 [97 , 122] X = Chr$(Int(26 * Rnd) + 97)

The Random List

Suppose you wanted a random list of numbers [0, 9]. Since we are generating random numbers, one number will more than likely appear twice before all 10 numbers are found. (The odds of getting a complete list without duplicates is 10! / 10^10, or 9! / 10^9, which is too low of odds to assume that there will be no duplicates... ). So, we will have to purposely check if the number has already been assigned. We will generate a random number ten times and then check the number against numbers already in the array. Dim A(0 To 9) As Integer Dim LV As Integer, RI As Integer, IV As Integer, Final As Integer Dim InvalidNumber As Boolean Final = -1 'The last index in the array that we've assigned. For LV = 0 To 9 'Loop for all ten numbers Do InvalidNumber = False 'Clear the boolean - the number is valid until proven otherwise. RI = Int(Rnd * 10) 'Create our random number [0 , 9] For IV = 0 To Final If A(IV) = RI Then 'Check if this number exists in the array. InvalidNumber = True 'If it does, we cannot use this number. End If Next Loop While InvalidNumber 'If we could not use the number, get another one. Final = Final + 1 'We fill the array and update the last index filled. A(Final) = RI Next Debug.Print A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9) And I get the numbers in this order: 7, 5, 2, 3, 0, 8, 4, 9, 6, 1 There are other ways of generating a randomized list of numbers: Dim A(0 To 9) As Integer Dim C As Collection 'Will hold the numbers that I want to select from. Set C = New Collection Dim LV As Integer, RI As Integer For LV = 0 To 9 C.Add LV 'Add the numbers from 0 to 9 to the collection. Next For LV = 0 To 9 RI = Int(Rnd * C.Count) + 1 'Get a random index from the collection. A(LV) = C.Item(RI) 'Assign the number at this index to the array. C.Remove RI 'Remove this number from the collection. Next Set C = Nothing Debug.Print A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9) And I get the numbers in this order: 7, 4, 5, 2, 1, 8, 0, 9, 6, 3 There are a few similarities with this list and the previous one.