
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