| Creating Quasi-Random Numbers Using the System Clock. (Revised and updated) |
A random number generator is useful in the real world for running Monti-Carlo simulations. But, DOS programming is not the real world. What I mean is that creating random numbers in a DOS batch file is almost strictly for fun or just to prove that it can be done. I defy anyone to justify it as truly useful. But being useful is hardly a reason for not doing it.Here are updated methods for creating two kinds of random numbers -- sort-of-random and nearly-random.
| The Simpler Way |
The first type is generated by determining what the hundredths-digit of the system's time-of-day clock is at any given instant. It is a significant departure from the approach first published here in 1997. The original looped repeatedly to test the time string using the FIND filter utility (see: OldRandm). The new approach uses characteristics of Command.com's internal PROMPT statement as illustrated by the following procedure, RandNumb.bat.
|
:: RandNumb.bat - Uses the system clock to pick a sort-of-random digit, 0 to 9. |
This approach creates two versions of the current time string: a complete version and a version missing its last (hundreths) digit. This is done with with the $T and$T$H substitution symbols in the PROMPT constructed in the first ECHO. The temporary batch procedure, ~Tmp.bat, holds the necessary PROMPT string (see: ANSI Helper for more information). The output from this procedure executed under the secondary command processor (the %COMSPEC% line) is piped into yet another instance of the command processor. This second instance of the command processor holds the key to the process. It caused the backspace created by the PROMPT string in the first running of ~Tmp.bat to delete the last character of the second TIME string. Note that the $G equivalence in the PROMPT becomes the redirection character (>) sending the output into a new version of ~Tmp.bat, which then looks something like this ...
randnumb 9:12:34:56.78 9:12:34:56.7 goto:2nd
When this new version of ~Tmp.bat is executed, RandNumb is reentered with the two versions of the time string as command line arguments and processing continues at the label, :2nd. The subsequent FOR statement tests all possible digits as suffixes to the second version to see which digit gives a match to the first version. The matching digit is stored in Rand.Numb, completing the process.The final FOR statement is merely for demonstration purposes and does nothing unless the Example variable is SET as described in the comment. If desired a PAUSE can also be added. I use such final statements for debugging from athe command prompt and just leave them after I'm done, since the overhead is very low.
Another feature in this procedure worth mentioning is the use of the Rand.Numb environment variable to hold text other than the final result. It is used here to hold part of the ultimate PROMPT string temporarily. This helps to shorten the next line (long lines are often a problem in Emailing routines to others). A temporary variable could have been used, but it would need to be cleared, adding to the length of the routine. Don't let this temporary use of the 'result' variable confuse you. It's just a little expedient I have used throughout the several procedures presented in this and related pages.
RandNumb returns a fairly random result if a single number is needed based on when the user issues the request for the number. That is, the user interacts with the procedure to create each number. The randomness is derived from the variability of the user's actions. However, if multiple calls are made in rapid succession in an automated loop, the results will be less random. They will instead be rather uniformly spaced as a function of the time it takes to execute the loop and the sequence will tend to repeat itself every few seconds. That's hardly a random sequence.
| A Better Way |
One solution that I can think of to make the selection more random is to pick from a saved list of numbers that gets shuffled a little with each selection. Thus, when a number is repeated in a subsequent cycle, it will likely return a different value out of the list. For example, picking the third item in the list several times in a row gives a different value since the list is in a different order each time. Another benefit of this approach is that items other than digits from 0 to 9, such as the letters of the alphabet, can be used. An implementation using a list is offered in the following example.
|
:: MoreRand.bat - Uses the system clock to pick a 'more random' digit, 0 to 9. |
The second working line constructs a starting list in normal order, if it doesn't already exist. The same basic approach described earlier is used to determine the clock's hundredth-of-a-second digit, but in this case it isn't returned directly. Instead, the value of the current list element corresponding to that number is returned. Of course, the digit that results is the same as the sequence count for the very first pick, but the list becomes more jumbled with each subsequent call. This happens because the list is reordered and the digit picked from the list is placed somewhere in the middle. Sort of like cutting a deck of cards, putting the bottom part on the top after taking the first card from the bottom part and placing between the two halves. For example, if the first number selected is '5', the value of Rand.Series after the first pick is ...Selecting '5' a second time returns the value '0' instead of '5', thus randomizing the result a little bit. From my informal testing, the list becomes fairly randomly ordered in just a few picks. However, given a starting value, the selection process for a fixed selection time interval is predictable, but it appears to be more random. Plus, there are at least ten such starting positions. That's why I can only claim this approach is 'more' random. As long as the selection interval remains fixed, it still cannot produce a truly random sequence of digits. That really requires user interation with the process to introduce true randomness. However, user interation can be as varied as moving the mouse, if the procedure is run in a non-exclusive Windows DOS session.RAND.SERIES=6 7 8 9 0 5 1 2 3 4 As mentioned earlier, this new approach doesn't use a loop nor the external FIND utility, making it faster. It also seems to generate a more varied sequence of digits than the one previously published here. If you would like to evaluate the two approaches, side-by-side, the earlier version (RANDOM.BAT) is still available from the Batch File index.
The only significant drawback I can see in this second procedure is the need for a little more environment space to store the Rand.Series list items and the need for the calling routine to clear it when done drawing random numbers. Otherwise, the Rand.Series variable remains in the environment after the procedure finishes (unless, of course, it is a DOS session under Windows).
Oh, if a random number between say 00 and 99 is desired, simply call the routine twice and save the first value in another variable before calling it a second time, something like this ...
call morerand
set Number=%Rand.Numb%
call morerand
set Number=%Number%%Rand.Numb%
| Where Do You Go from Here? |
So, what can you do with a random number generator like this? Play games, of course. If you're so inclined you can procede to the game page for procedures to simulate flipping a coin or dealing from an infinite deck of cards. It's not serious stuff, but it might be mildly amusing.