KeyWords: RND, RANDOMIZE, TIMER
Here are three programs that will solve the problems at the end of chapter six. Keep in mind that your label and variable names will be different, and some of you more ingenious people may have found a more efficient method of doing them. The programs listed here work, but they use the simplest logic available, except for the Prime Number program, which has one little improvement. The reversal programs can be written with fewer variables. To test your programs, just run them and make sure they give you the correct answers. Here is the program that reverses a three-digit number:
GETNUM: PRINT "Input a Three-Digit Number"; INPUT NUM IF NUM - INT(NUM) <> 0 THEN GOTO GETNUM IF NUM < 100 THEN GOTO GETNUM IF NUM > 999 THEN GOTO GETNUM LET QUOTIENT = NUM / 100 LET DIG3 = FIX(QUOTIENT) LET NUM = NUM - 100 * DIG3 LET QUOTIENT = NUM / 10 LET DIG2 = FIX(QUOTIENT) LET NUM = NUM - 10 * DIG2 PRINT NUM; DIG2; DIG3 PRINT DIG3 + 10 * DIG2 + 100 * NUM END
Notice that we used a method that allows us to print out the answer in both methods suggested in the problem for the leading zero case. To turn it into a 4-digit program, we simply expand on the 3-digit algorithm (set of instructions) to handle one more digit, and adjust the input checking accordingly. Here is our program:
GETNUM: PRINT "Input a Four-Digit Number"; INPUT NUM IF NUM - INT(NUM) <> 0 THEN GOTO GETNUM IF NUM < 1000 THEN GOTO GETNUM IF NUM > 9999 THEN GOTO GETNUM LET QUOTIENT = NUM / 1000 LET DIG4 = FIX(QUOTIENT) LET NUM = NUM - 1000 * DIG4 LET QUOTIENT = NUM / 100 LET DIG3 = FIX(QUOTIENT) LET NUM = NUM - 100 * DIG3 LET QUOTIENT = NUM / 10 LET DIG2 = FIX(QUOTIENT) LET NUM = NUM - 10 * DIG2 PRINT NUM; DIG2; DIG3; DIG4 PRINT DIG4 + 10 * DIG3 + 100 * DIG2 + 1000 * NUM END
Notice how the two programs are very similar? You may trace through the program listings yourself to discover the logic that we used. Chances are it is very close to the way you did it, although it is possible to do it backwards. Not easy, but possible. Instead of working with the quotient, you work with the remainder when you divide.
Let's move on to the Prime Number program. To find out if a number is prime, all we have to do is divide it by every integer between 2 and (itself-1). If we get an integer as an answer (no remainder), we have found a factor, and the number is not prime. However, to speed up the program a little bit, you will notice that factors always come in pairs, and the largest factor can never be more than half of the number. So to double the speed of the program, you only have to divide the number by every integer from 2 to 1/2 of the number. Here is the program (Notice how we test that the number is an integer in lines 5 and 10):
GETNUM: PRINT "Enter an Integer between 2 and 20,000 and" PRINT "I will tell you if it is a Prime Number" INPUT NUM IF NUM - INT(NUM) <> 0 THEN GOTO GETNUM IF NUM < 2 THEN GOTO GETNUM IF NUM > 20000 THEN GOTO GETNUM PRIME = 0 FOR TRY = 2 TO NUM / 2 IF NUM / TRY <> INT(NUM / TRY) THEN GOTO TRYMORE PRINT "Factor Found:"; TRY PRIME = 1 TRYMORE: NEXT TRY IF PRIME = 1 THEN GOTO NOTPRIME PRINT "Number is Prime!" GOTO DONE NOTPRIME: PRINT "Sorry, Number is not prime." DONE: END
There is one "trick" in this program, and you may have noticed it in the variable PRIME. This is what is called a FLAG variable. At the beginning of the program, this flag is set to zero, which in our case means we have not yet found a factor. We then divide the number by the first integer (2). If it does not turn out to be a factor, it jumps down to the NEXT command, and tries the next integer. On the other hand, if 2 is a factor, then the variable PRIME is assigned the value of 1, indicating we have found a factor, so we run it up the flag pole and see if anyone salutes. Got it? When the FOR...NEXT loop has finished, we drop down to line 15. If the entire loop has gone without finding a factor, then lines 11 and 12 would never have been executed, and PRIME would still be zero. We then drop down to line 16 and print out that the number is prime. If PRIME is 1, then line 15 is true, and the program branches to line 18, stating to the user that the number isn't prime. Notice also that line 11 prints out the factors as it finds them.
Let's get into some new material. We'll start out with the easier of the two ideas, random numbers. Random numbers are just that. You don't know what the next one will be. The function to generate a random number is RND. Type in this cute little program:
FOR X = 1 TO 5 PRINT RND NEXT X END
Now go ahead and run the program. RND always returns a number between (but not including) zero and one. Also, you may be interested to know that these are not really random numbers. Run the program three or four more times. Notice anything? Yes! You get the same sequence of numbers every time!! We'll show you how to get around that later. RND can also be used with an argument, like such: RND(x). Different values of x will produce different results. If x is positive, you will get the next number in the sequence. If x is negative, you will start the sequence all over again. If x is zero, RND repeats the last number generated.
How do we get a different number each time we run our program? QBASIC has included a function that allows us to seed or initialize the random number generator. It is the RANDOMIZE function. Add this line to the top of our program:
That's all there is to it. Now run the program. When the computer sees the RANDOMIZE function, it prints out a prompt:
Random number seed (-32768 to 32767)?_
and waits for you to enter an integer within the specified range. That allows you 65,536 different random sequences, which should be more than enough. Notice, however, that when you enter the same seed number, you get the same sequence of random numbers. Also, we probably don't want our user to have to come up with a number off the top of their head. How do we get around that? RANDOMIZE will also allow us to use an argument. QBASIC also has a function that returns a different number all the time, and only returns the same number once per day. It is the TIMER function, and all it does is tell you how many seconds it has been since midnight. It operates off of the DOS clock, so if you set the date and time correctly, the count will be pretty accurate. Let's see how many seconds have passed since midnight. Type in (in the immediate window)
and press Enter. If it is in the morning, you will get a pretty small number. In the afternoon, you will get a good sized number, and late at night, you will get an absolutely huge number. Incidently, there are 86,400 seconds in a day. The only problem is, our random number seed can only be up to about 33,000. How do we fix that? Easy enough. Just divide TIMER by three, and the largest you will get is 28,800. That is easily within range, and you will get a new seed every time. Once the random number is seeded in a program, there is no need to keep on reseeding it. Let's fix up our top line to read:
Now run the program. QBASIC will take into account the numbers after the decimal point for the seed and give you a different sequence of number no matter how fast you run the program.
The next problem we have is that RND only gives us numbers from 0-1. What if we want integers from 1 to 10?. It seems that we would want to multiply our random number by 10. This is true, because it will give us numbers between 0 and 10. But we want 1 through 10! If you take the integer part of the number, you will get numbers of 0 to 9. Remember that RND will never generate either a zero or a one. It will only be between these two numbers. Therefore, when we do either an INT(x) or FIX(x) we will get numbers from 0 to 9. Now we just add 1 to that result, and we will get numbers from 1 to 10. Here is the basic rule for generating a set of random integers:
To generate a set of random integers in the range of x to y, first multiply the random number by (y-x)+1, take the integer portion, then add x.
It works fine every time. For real numbers, just leave off the integer stuff. Let's put some of this into a program. Let's write a program that generates and prints out 20 random numbers in the range of 1 to 20 and see what happens:
RANDOMIZE TIMER / 3 FOR COUNT = 1 TO 20 LET NUM = RND LET NUM = INT(20 * NUM) + 1 PRINT NUM NEXT COUNT END
It's all pretty simple, really. Now why did we introduce all this stuff about random numbers here? Yes, there is a method to all this madness! Our next program will be a major sort program. We will be sorting a list of 50 numbers, and it is easier to let the computer generate the 50 numbers than to have either the programmer enter them with a DATA statement, or the user INPUT them. We'll let the computer do all the work. It's faster and easier that way.
Before we begin our sort program, we have to let you in on a little secret. Actually it is a big secret. No, really it's more of a powerful secret. As a matter of fact, it isn't really much of a secret at all! Here's the secret: Did you know that it is possible for a single numeric variable to have more than one value at the same time? How do we do that? It's by using subscripts. Those of your familiar with other languages will know them as arrays. We'll explain them next chapter, but to find out why we need them, let's average a set of numbers. Let's say we needed to average ten numbers using a READ...DATA statement. Our program would look like this:
READ A,B,C,D,E,F,G,H,I,J LET X = (A+B+C+D+E+F+G+H+I+J)/2 PRINT X DATA ... END
What's wrong with that, you ask? Nothing! The program works fine. But suppose you needed to average 100 numbers? You would need to read in 100 numbers into 100 different numeric variables, and the READ and LET statements would become so long as to be ridiculous! I see some light bulbs coming on out there! Some of you are saying "Why not just add each piece of data to a variable as you read it in? It would only take two variables total for the program!" Yes, that would work absolutely perfectly. Let's take it one step further, though. Say we need to determine how far away from the average each piece of data is - kind of a poor man's standard deviation, if you please. Now we have a problem. We need to keep all of the pieces of data stored in a variable, because we first need them to determine the average, then we have to go back to them again to subtract them from the average. The logic would go something like this (this is what our flowchart would look like):
1. Read and store a list of numbers.
2. Find the sum of the list.
3. Compute the average of the set.
4. Subtract the average from each number in the list.
5. Print each number and it's distance from the average.
Steps 1 and 2 could be done in a loop as talked about above, as could steps 4 and 5. If you have a list of ten numbers, this program would work fine:
READ A,B,C,D,E,F,G,H,I,J LET X = (A+B+C+D+E+F+G+H+I+J) / 10 PRINT A,A-X PRINT B,B-X PRINT C,C-X PRINT D,D-X PRINT E,E-X PRINT F,F-X PRINT G,G-X PRINT H,H-X PRINT I,I-X PRINT J,J-X DATA ... END
For a list of ten items, fourteen statements were required. No loop was used. For a list of 100 items, 104 lines would be needed! HELP! THERE HAS TO BE A BETTER WAY! There is. But we're not going to tell you about it yet, because I see that we have no more room to tell you about it. We will just say that this is where subscripted variables come in handy. Meanwhile, try to write some programs using random numbers. Here are a few suggestions:
Generate 1000 random digits (0-9) and count how many times each one occurs.
Flip a coin 100 times and count how many heads and how many tails you get. Here are two methods to use:
If the generated number is <0.5, it's heads; if the generated number is >0.5, it's tails.
Convert the random number to an integer in the range of 1-2. 1 is heads, 2 is tails.
Answers next chapter. See you then!
Introduced In This Chapter:
Keywords: RND, RANDOMIZE, TIMER
CONCEPTS: Using variables as flags, Random numbers,
Seeding the random number generator, Using the DOS clock as a seed value,
Problems with storing large amounts of data.