QBasic - Chance - Random numbers

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