HomePage | Optical Illusions | War Stories | QBasic | Dads Navy Days | Bristol | Bristol, USA | Bristol, Canada | Terre Haute | Miscellany | Web Stuff | About Ray | Site Map | Site Search | Messages | Credits | Links | Web Rings
QBasic | Errors | 40lb Weight | Bits | Chance | Colours | Dates | Delays | File Dialog | Files | Input | Matching | Menus | Mouse | Numbers | SeqNo | SIRDS | Sorts | Text | Timer | DLoads
Chance ~ using random numbers:
QBasics random number generator needs to be 'seeded'. This is done by using RANDOMIZE. To make the selection of random as possible randomize is usually used in conjunction with the timer. The most usual use for the random number generator is in games, as in the example below :-
'Random.bas Ray Thomas July 1999 'Program to show how to generate random numbers OPTION BASE 1 DIM Die1 AS INTEGER DIM Die2 AS INTEGER DIM Number AS INTEGER DIM Suit AS STRING DIM Value(13) AS STRING DATA Ace,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King FOR Number = 1 TO 13 READ Value(Number) NEXT Number DO RANDOMIZE TIMER Die1 = INT(RND * 6) + 1 Die2 = INT(RND * 6) + 1 Number = INT(RND * 4) + 1 SELECT CASE Number CASE 1 Suit = "Diamonds" CASE 2 Suit = "Clubs" CASE 3 Suit = "Hearts" CASE ELSE Suit = "Spades" END SELECT Number = INT(RND * 13) + 1 CLS PRINT PRINT " Your first die rolled "; Die1 PRINT " Your second die rolled "; Die2 PRINT PRINT " The total value is "; Die1 + Die2 IF Die1 = Die2 THEN PRINT PRINT " *** CONGRATULATIONS - you rolled a double "; Die1; " ***" END IF PRINT PRINT " The card you chose was :-" PRINT PRINT " The "; Value(Number); " of "; Suit PRINT PRINT PRINT " Play again?" DO Cmmnd$ = UCASE$(INKEY$) LOOP UNTIL Cmmnd$ <> "" LOOP UNTIL Cmmnd$ <> "Y" END
At work, a client may supply us with a large database for us to work with, after doing any necessary processing, we write a program in PReS to process the records and output them to production printer. The program has to be tested and to do this we usually do a 'X in Y' ie. 1 in 10 or 15 in 100 selection from the main database. This should give us a cross section of the main database to use for program testing and proofing purposes. Sometimes a client may want a completely random selection to be made.
X in Y selection:
This is relatively easy to code. The logic being :-
Get the total number of records
Ask how many groups to divide the file into (Y)
Ask how many records from each group to get (X)
Divide the total number of records by the number of groups
Extract the number (X) of records from each group
Create an array that contains the numbers 1 to 30,000 in order
The code for this is :-
'XINY.BAS Ray Thomas July 1999 ' X in Y data record selection program DIM DataFile AS STRING 'Path and name of data file DIM TestFile AS STRING 'Test file to create DIM InRec AS STRING 'Holds the record being worked on DIM TotRecs AS LONG 'Number of records in data file DIM StrtPosn AS LONG 'Start position of each group DIM RecLen AS INTEGER 'Record length of data file DIM YSel AS INTEGER 'Number of groups DIM XSel AS INTEGER 'Number of records from each group DIM Count AS INTEGER 'Loop counter DIM Counter AS INTEGER 'Loop counter DataFile$ = "E:\Ray\TestData\Ray5.dat" RecLen = 718 TestFile$ = "E:\Ray\TestData\Ray1.tst" CLS PRINT PRINT "Program to make a X in Y data record selection" PRINT PRINT INPUT "Number of groups? ", YSel PRINT INPUT "Number of records? ", XSel PRINT PRINT PRINT "You have asked for"; XSel * YSel; "records to be produced." PRINT PRINT "Record numbers selected :-" PRINT OPEN DataFile$ FOR RANDOM ACCESS READ LOCK READ WRITE AS #1 LEN = RecLen FIELD #1, RecLen AS InRec$ TotRecs = LOF(1) / RecLen 'calculates the number of records in the data file OPEN TestFile$ FOR OUTPUT ACCESS WRITE LOCK READ WRITE AS #2 StrtPosn = 1 FOR Count = 1 TO YSel FOR Counter = 1 TO XSel GET #1, StrtPosn PRINT StrtPosn; PRINT #2, InRec$ StrtPosn = StrtPosn + 1 NEXT Counter StrtPosn = Count * (TotRecs / YSel) NEXT Count CLOSE END
Random data file record selection:
To generate a test file from random record numbers the following logic can be used :-
Create an array containing the number of records in the data file
Create an array to hold the random numbers
Generate a random number between 1 and the length of the array
Take the number generated and put it into the random number array
From the number generated to the end of the data array shuffle the numbers upwards by one
Decrease the range of the random number by one (at each step through the loop)
Continue until the number of records selected is reached
Sort the random number array
Read the random number array and get the record from the data file
Write the record to the test file
The code now becomes :-
'RANDAT.BAS Ray Thomas July 1999 ' Random data record selection program OPTION BASE 1 'First element of array is 1 not 0 DIM DataFile AS STRING 'Path and name of data file DIM TestFile AS STRING 'Test file to create DIM InRec AS STRING 'Holds the record being worked on DIM TotRecs AS LONG 'Number of records in data file DIM RandNum AS LONG 'The random number generated DIM RecLen AS INTEGER 'Record length of data file DIM RecSel AS INTEGER 'Number of records to be selected DIM Count AS INTEGER 'Loop counter DIM Counter AS INTEGER 'Loop counter DataFile$ = "E:\Ray\TestData\Ray5.dat" RecLen = 718 TestFile$ = "E:\Ray\TestData\Ray1.tst" CLS PRINT PRINT "Program to make a test file from randomly selected data records" PRINT PRINT INPUT "Number of records? ", RecSel PRINT PRINT PRINT "You have asked for"; RecSel; "records to be produced." PRINT PRINT "Record numbers selected :-" PRINT OPEN DataFile$ FOR RANDOM ACCESS READ LOCK READ WRITE AS #1 LEN = RecLen FIELD #1, RecLen AS InRec$ TotRecs = LOF(1) / RecLen 'calculates the number of records in the data file OPEN TestFile$ FOR OUTPUT ACCESS WRITE LOCK READ WRITE AS #2 DIM RecArray(TotRecs) AS LONG 'Array to hold total amount of records DIM RandArray(RecSel) AS LONG 'Array to hold random numbers FOR Count = 1 TO TotRecs RecArray(Count) = Count NEXT Count RANDOMIZE TIMER FOR Count = 1 TO RecSel RandNum = (RND * (TotRecs - Count)) + 1 'Produce a random number RandArray(Count) = RandNum PRINT RandNum, FOR Counter = RandNum TO RecSel - Count RecArray(Counter) = RecArray(Counter + 1) NEXT Counter NEXT Count GOSUB ShellSort 'sort the chosen numbers back into order PRINT PRINT PRINT "The records placed in their proper order are :-" PRINT FOR Count = 1 TO RecSel GET #1, RandArray(Count) PRINT RandArray(Count), PRINT #2, InRec$ NEXT Count CLOSE END ShellSort: 'similar to a bubble sort but a lot faster ' Set comparison offset to half the number of records in RandArray: Offset = RecSel \ 2 DO WHILE Offset > 0 ' Loop until offset gets to zero. Limit = RecSel - Offset DO Switch = FALSE ' Assume no switches at this offset. ' Compare elements and switch ones out of order: FOR Row = 1 TO Limit IF RandArray(Row) > RandArray(Row + Offset) THEN SWAP RandArray(Row), RandArray(Row + Offset) Switch = Row END IF NEXT Row ' Sort on next pass only to where last switch was made: Limit = Switch - Offset LOOP WHILE Switch ' No switches at last offset, try one half as big: Offset = Offset \ 2 LOOP RETURN
For a further discussion on sorting techniques goto SORT
Random question quiz
Here's something I've seen people ask at least twice about in different QBasic forums. They want to know how to ask the user questions randomly from a list. The logic behind quizes like this is almost the same as the random test file generator above. In the program ranquiz.bas the questions and answers are held in two different arrays. A random number is generated from 1 to the number of questions in the array. Once the question has been answered correctly or viewed if wrong, the question and answer are removed from the array and the remaining elements moved up one. The number range that the random number is generated for is decreased by 1.
' ranquest.bas Ray Thomas March 2002 'A random question generator OPTION BASE 1 '*** Program variables *** DIM QuestArray(5) AS STRING 'Question array DIM AnsArray(5) AS STRING 'Answer array DIM Count AS INTEGER 'Loop counter DIM Num AS INTEGER 'General numeric DIM NumAry AS INTEGER 'Number of elements in the arrays DIM UserAns AS STRING 'User reply to questions DIM Qasked AS INTEGER 'Number of questions asked DIM QCrrct AS SINGLE 'Number of questions answered correctly DIM QLeft AS INTEGER 'Questions left to ask '*** Question and answer data *** DATA "When did Indiana become a state","1816" DATA "Who is considered the father of Communism","Karl Marx" DATA "In what year did the Titanic sink","1912" DATA "To what number base is hexadecimal","16" DATA "From what flower was digitalis derived","Foxglove" '*** Initialise variables *** RANDOMIZE TIMER NumAry = UBOUND(AnsArray$) QLeft = NumAry '*** Fill the question and answer arrays *** FOR Count = 1 TO UBOUND(QuestArray$) READ QuestArray$(Count) READ AnsArray$(Count) NEXT Count DO '*** Prepare the screen *** UserAns$ = "" CLS LOCATE 2, 2 PRINT "Please answer the questions, or Q to quit the program" PRINT '*** Get and ask the question *** '*** QLeft ensures the same question isn't asked twice *** Num = INT(RND * QLeft) + 1 LOCATE 4, 2 PRINT QuestArray$(Num) + "?" Qasked = Qasked + 1 '*** Get and compare the answer *** LOCATE 6, 2 LINE INPUT ; UserAns$ LOCATE 8, 2 IF UCASE$(UserAns$) = "Q" THEN EXIT DO IF UCASE$(AnsArray$(Num)) = UCASE$(UserAns$) THEN PRINT "*** Correct *** WELL DONE ***" QCrrct = QCrrct + 1 ELSE IF INSTR(UCASE$(AnsArray$(Num)), UCASE$(UserAns$)) > 0 THEN PRINT "*** Sort of Correct *** Half a mark ***" QCrrct = QCrrct + .5 ELSE PRINT "*** Oops, incorrect. Do you want to see the correct answer?" DO UserAns$ = UCASE$(INKEY$) LOOP UNTIL UserAns$ = "Y" OR UserAns$ = "N" END IF END IF '*** Confirm and print the correct answer *** '*** Only do this if the answer is correct *** '*** or the user wants to see the answer *** IF UserAns$ <> "N" THEN LOCATE 10, 2 PRINT "The answer to "; QuestArray$(Num); " is :-" LOCATE 12, 2 PRINT AnsArray$(Num) '*** Ensure the same question isn't asked twice *** '*** by moving all the elements in the arrays up by 1 *** '*** and decreasing QLeft, which limits the RND *** QLeft = QLeft - 1 FOR Count = Num TO NumAry - 1 QuestArray$(Count) = QuestArray$(Count + 1) AnsArray$(Count) = AnsArray$(Count + 1) NEXT Count END IF LOCATE 15, 2 PRINT "Questions left ="; QLeft LOCATE 17, 2 PRINT "Press any key to continue ..." DO LOOP UNTIL INKEY$ <> "" LOOP UNTIL QLeft = 0 CLS LOCATE 3, 3 PRINT "*** Thanks for playing ***" LOCATE LOCATE 5, 3 PRINT "Out of"; Qasked; "questions, you got"; QCrrct; "correct" END
Random numbers within a range
The random numbers so far produced all start from 1, but supposing you want to produce a number in the range Min - Max where Min is not 1.
In that case you need Num = INT(RND * (Max - Min + 1)) + Min
Suppose you need a number in the range 27 to 93 in that case the statement becomes :-
Num = INT(RND * (93 - 27 + 1) + 27
which gives
Num = INT(RND * 67) + 27
Here's a program that produces a set of random letters in the range A - Z and a - z. It works because the random number produced is the ASCII value of the various letters. The ASCII of A - Z is 65 - 90. The ASCII of a - z is 97 - 122.
65 is the minimum number you want to produce (ASCII of "A"). You want 52 numbers
(2 * 26). So the maximum number is 65 + 52 = 117. 117 - 65 + 1 = 53
The ASCII value of "Z" is 90, but the value of "a" is 97. If the number produced is 91 then it gets turned
into 97 by the addition of the 6.
'RanLet Ray Thomas April 2002 DIM Num AS INTEGER 'The random number DIM UserIn AS STRING 'User input CLS PRINT "This program will produce random letters in the range A - Z and a - z" PRINT PRINT "Press Esc to exit the program, any other key to produce the random letters" PRINT RANDOMIZE TIMER DO Num = INT(RND * 53) + 65 PRINT Num; IF Num >= 91 THEN Num = Num + 6 PRINT Num; CHR$(Num) DO UserIn$ = INKEY$ LOOP UNTIL UserIn$ <> "" LOOP UNTIL UserIn$ = CHR$(27) END
The programs on this page, like all the programs written for this site, can be downloaded from the DLoads page.
QBasic | Errors | 40lb Weight | Bits | Chance | Colours | Dates | Delays | File Dialog | Files | Input | Matching | Menus | Mouse | Numbers | SeqNo | SIRDS | Sorts | Text | Timer | DLoads
HomePage | Optical Illusions | War Stories | QBasic | Dads Navy Days | Bristol | Bristol, USA | Bristol, Canada | Terre Haute | Miscellany | Web Stuff | About Ray | Site Map | Site Search | Messages | Credits | Links | Web Rings