[ABC home]    [ABC Archives by Issue]   [ABC Archives by Author]  [Search]  [Privacy]

 

ABC Home Page
ABC Home

 

Advertise in ABC

 

Learn more
about
Ray Blake
Ray Blake

Read
Ray's Archives

Ray's Archives

Ray's
Website
Ray's Website, GR Business Process Solutions

 

 

ABC ~ All 'Bout Computers
The Online Web-azine for Computer Enthusiasts
-- brought to you by
Visit Linda's Computer Stop

contents page for this issue

 


 Support ABC

 

Linda's Ebooks
Ebooks on Access, Excel, Outlook, PowerPoint, Publisher, and Word

Linda's CD
Order Linda's CD and learn all of the Office programs

The Newbie Club
Learn all about computers the easy way

Online classes
at Eclectic Academy
 Instructor led online courses at Eclectic Academy

My Newest Book
Excel 2003 Study Guide

published by Wiley
get it at Amazon,
at Barnes & Noble,
or at Borders

FrontLook
Add-ins
& Screen Capture

Great FrontPage addins and other software

WinBackup
SpeedUpMyPC
WinTasks5Pro

great Windows utilities


 

 
More Excel Dice
~~Ray Blake, GR Business Process Solutions

Making a VBA routine to provide a range of non-repeating, non-volatile random numbers

Some time ago, I wrote an article called "Throwing Dice with Excel". In that article.  I looked at how to generate random numbers using Excel’s built-in functions and how to use copy and paste special when non-volatile random numbers are needed.

Just recently, a reader emailed to ask for help. He needed to generate four random numbers between 1 and 35. Fine; this doesn’t represent a problem. The twist was, the four numbers had to be unique: there could be no numbers duplicated among the four. This raises a real problem, because the nature of random numbers is that they are random. We all know that even if the coin lands heads up ten times in a row, the chance of it doing so again on the next toss is still 50%: the coin has no memory. Similarly, throw the dice twice and there’s a possibility you’ll get the same result both times. What my correspondent really needed was a way of drawing numbers from a bag. Each time a number is drawn, that number ceases to be in the bag for the next draw.

Now, I am a great believer in the power of Excel’s built-in functions; I only resort to VBA code when I have to. Whilst it would probably be possible to create this ‘number bag’ with formulae, every approach I considered was very big and very complicated. Those are two adjectives I’ll go a long way to avoid, and my instinct was that VBA would yield a far more straightforward solution this time.

Let’s recap on what I needed to do. I needed four random numbers from a lowest possible value of 1 to a highest possible value of 35. In line with my normal practice, rather than hard-code these into my program, I assigned them as constants, which would make life a lot easier if I needed the code later for another situation. Here is my first section of code:

Sub GetRandomNumbers()

Const intLowest As Integer = 1 'Lowest number required

Const intHighest As Integer = 35 'Highest number required

Const intHowMany As Integer = 4 'Number of different values to return

'Set up array to hold the random value

Dim arrRandom(1 To intHowMany) As Integer

End Sub

Note that I’ve used constants rather than variables to hold my three main driving values. This allows me – for one thing – to use one of these values to define the dimensions of the array I’ll use to hold the random numbers (I can only use a constant to do this, not a variable.)

My basic approach in this code will be as outlined in the flowchart below:

flowchart

One of the dangers of using a loop structure is the possibility of the code locking up in an ‘infinite loop’, where the exit condition is never met. In my loop, this could happen if the number of different values required is bigger than the range of allowed numbers; there aren’t 10 whole numbers between 1 and 5, for example.

A little code before we get into the loop will take care of this:

'Check to prevent infinite loop

If intHowMany > (intHighest - intLowest + 1) Then

MsgBox "Too many numbers or too small a range."

Exit Sub

End If

And now we can put the loop into operation. Here is how we assign the first random number:

'Generate the first random value

arrRandom(1) = Int((intHighest + 1 - intLowest) * rnd) + intLowest

If you’ve read the ‘Throwing Dice’ article, you’ll recognise this as the VBA equivalent of the RAND formula I outlined there.

Of course, the first random number cannot possibly be a duplicate, so there is nothing to check it against. From this point in, we’re in the main loop. As part of the loop, I’ll need to be able to set a flag, so the first thing I’ll do is declare a variable for this purpose:

Dim booFlag As Boolean

Here is the main loop:

'Generate subsequent random values and ensure no dupes

For i = 2 To intHowMany

Do

booFlag = False

arrRandom(i) = Int((intHighest + 1 - intLowest) * rnd) + intLowest

For j = 1 To i - 1

If arrRandom(j) = arrRandom(i) Then booFlag = True

Next j

Loop Until booFlag = False 'Ensures value not kept if it is a dupe

Next i

Note that this is really three loops:

  • The For/Next ‘i’ loop which cycles through the second and subsequent random numbers required
  • The For/Next ‘j’ loop which, once a random number is provisionally assigned, cycles through each earlier number to check it is not a duplicate. If it is a duplicate, the flag variable booFlag is set to True.
  • The Do/While loop which prevents progress if booFlag has been set to True, sending the program back to choose a new number instead and test again.
  • By the time the code runs to the end of this section I will have an array full of random numbers with no duplicates. What I now need to do is to report these numbers back to the worksheet. This is a job for another loop:

    'Write the values back to the worksheet - change "B1" reference as required

    For i = 1 To intHowMany

    Range("B1").Offset(i, 0).Value = arrRandom(i)

    Next i

     

    There is just one more addition to make before the code is complete. In VBA, the Rnd function is only really ‘pseudo-random’ since it works from a seed based on the last result obtained. If I want to produce the most random result, I need to include the following command before first using Rnd:

    Randomize 'Resets random seed

    This will ensure the random seed is reset, based on the system timer.

    The full code looks like this:

    Sub GetRandomNumbers()

    Const intLowest As Integer = 1 'Lowest number required

    Const intHighest As Integer = 35 'Highest number required

    Const intHowMany As Integer = 4 'Number of different values to return

    Dim booFlag As Boolean

    'Check to prevent infinite loop

    If intHowMany > (intHighest - intLowest + 1) Then

    MsgBox "Too many numbers or too small a range."

    Exit Sub

    End If

    'Set up array to hold the random value

    Dim arrRandom(1 To intHowMany) As Integer

    Randomize 'Resets random seed

    'Generate the first random value

    arrRandom(1) = Int((intHighest + 1 - intLowest) * Rnd) + intLowest

    'Generate subsequent random values and ensure no dupes

    For i = 2 To intHowMany

    Do

    booFlag = False

    arrRandom(i) = Int((intHighest + 1 - intLowest) * Rnd) + intLowest

    For j = 1 To i - 1

    If arrRandom(j) = arrRandom(i) Then booFlag = True

    Next j

    Loop Until booFlag = False 'Ensures value not kept if it is a dupe

    Next i

    'Write the values back to the worksheet - change "B1" reference as required

    For i = 1 To intHowMany

    Range("B1").Offset(i, 0).Value = arrRandom(i)

    Next i

    End Sub

    The only thing I now have to put into place is a mechanism to call this code, since calling the macros dialog each time will quickly become tiresome. Which method I choose will depend on what I need the numbers to do, and this is one aspect of my correspondent’s needs about which I know nothing.

    One method would be to create a button on the worksheet or on a custom toolbar to run the code. Another would be to call if from the Open event of the workbook, to force it to get new random numbers whenever it is opened. Or, to use it like a volatile function, it could run from the Change event of the Worksheet.

    So, we’ve built a VBA program which will return a given number of random numbers within a given range without any duplicates. Rather than dice game lovers, this will be of most interest, I suppose, to bingo aficionados!

    Ray Blake lives in England and spent 15 years training people in the financial services industry there. He had always used PCs in his work, and gradually realized he might make a career out of them. He and his business partner set up GR Business Process Solutions (www.grbps.com) which specializes in innovative IT to support knowledge testing and skills assessment. Although he spends a lot of time these days developing in VB and Access, Excel remains his favorite development tool, because, as he says, 'It can do everything; there's no computer application you can think of that you couldn't develop in Excel.'

     Privacy Policy, Disclaimer, and Legal Stuff
    This page was last updated on Monday, December 31, 2007 . copyright © 2000 - 2008, Linda F. Johnson, Linda's Computer Stop, ABC ~ All 'Bout Computers. All rights reserved.
    [SEARCH THIS SITE]