PowerBASIC Forums
  Source Code
  A Swiss Army random number generator

Post New Topic  Post A Reply
profile | register | preferences | faq | search

UBBFriend: Email This Page to Someone! next newest topic | next oldest topic
Author Topic:   A Swiss Army random number generator
Emil Menzel
Member
posted March 10, 2005 01:36 PM     Click Here to See the Profile for Emil Menzel     Edit/Delete Message   Reply w/Quote
'RandINT.BAS -- A Swiss Army linear congruential pseudo-random number generator

' by E.W. Menzel Jr. March 2005.
' THIS IS FREEWARE
' Language: PowerBASIC PBCC
' Purpose: Provide a choice of random number generators -- all with known, open-source,
' well-tested algorithms, & some with much longer periods than PB's built-in RND. You can
' even switch your algorithms on the fly, should you care to do so.

' NOTES:
' 1. To "RANDOMIZE" simply set Rand.Seed = any positive integer between 1 and Rand.M -2.
' PB's RANDOMIZE and RND are not, however, used here.

' 2. For the theory behind these and other generators see:
' S.K. Park & K.W. Miller, "Random number generators: Good ones are hard to find",
' Communications of the ACM, Oct 1988, vol 31, 1192-1201.
' Donald Knuth, "The art of computer programming", 1998, vol 2, Chapter 3, 3rd edition.

' 3. Knuth lists many good (and a few "infamous") pairs of constants, together with their
' performances on tests of randomness. Here I pick four good pairs. If you need more than
' that, it will be easy to add them.


#COMPILE EXE
#DIM ALL

DECLARE FUNCTION RandInt() AS QUAD
DECLARE FUNCTION RNG() AS EXT
DECLARE FUNCTION RandPeriod() AS QUAD
DECLARE FUNCTION RandPick(whichOne AS LONG, seed AS QUAD) AS QUAD

'seed & constants for linear congruential random number generators
TYPE RandType
seed AS QUAD
A AS QUAD 'constant a
M AS QUAD 'constant m
END TYPE

GLOBAL Rand AS RandType

'Function RandInt (plus the Type definition above) might be all you
'really need; the rest is window-dressing. It returns a 10-digit integer.
'Note that if the generator has not been defined, default values are used.
FUNCTION RandINT AS QUAD
DIM Rand AS GLOBAL RandType
IF Rand.M < 1&& THEN Rand.A=16807&&: Rand.M=(2^31)-1&&
IF Rand.seed < 1&& THEN Rand.seed=TIMER
Rand.seed = (Rand.seed * Rand.A) MOD Rand.M
FUNCTION = Rand.seed
END FUNCTION

'RNG is an equivalent of PB's RND,
'in yielding uniformly distributed numbers ranging from 0 to 1,
'with theoretical exact mean=0.5 and var=(1/12) or 0.08333...
FUNCTION RNG AS EXT
FUNCTION = RandINT / Rand.M
END FUNCTION

'which generator & starting seed number do you want?
FUNCTION RandPick(whichOne AS LONG, seed AS QUAD) AS QUAD
DIM A AS STRING, N AS LONG
N =4 'however many generators are listed below

IF seed <1 OR whichOne<1 OR whichOne>N THEN
CLS
LOCATE 18,1
END IF
IF WhichOne >N OR WhichOne <1 THEN
'Rand. is a GLOBAL TYPE variable. Function RandInt DIM's it and will screen it.
PRINT "Which random number generator do you want?"
PRINT "(1)=Lavaux&Jannsens (2)=Cray X-MP (3) Marsaglia (4)Park&Miller "
PRINT "[default = ";N;
LINE INPUT "] ";A$
WhichOne=VAL(A$)
END IF
SELECT CASE WhichOne
CASE 1: Rand.A=31167285&&: Rand.M=2^48 'see Knuth, 1998, p.106, line 23
CASE 2: Rand.A=44485709377909: Rand.M=2^46 'see Knuth, 1998, p.106, line 22
CASE 3: Rand.A=69069: Rand.M=2^32 'see Knuth, 1998, p.106, line 22; G.Marsaglia
CASE ELSE: Rand.A=16807&&: Rand.M=(2^31)-1 'see Knuth, 1998, p. 106, line 19; Park-Miller
END SELECT

IF seed THEN
Rand.seed=seed
ELSE
LINE INPUT "Enter a seed number (0 or neg. = TIMER) ";A$
Rand.seed = VAL(a$)
END IF
IF Rand.seed < 1 THEN Rand.seed=INT(TIMER)
FUNCTION=Rand.seed

END FUNCTION

'Note: the period for PB's RND is 2^32 or over 4 billion, and
'the period for the generator using Rand.A=31167285&& & Rand.M=2^48
'is 2^48. Don't expect Function RandPeriod to reach the latter number
'any time soon. (I quit after a day or so, and count of a mere 350 billion.)
FUNCTION RandPeriod AS QUAD
DIM X(1 TO 5) AS QUAD, N AS QUAD, Hit AS LONG, UB AS LONG
DIM Y AS LONG, I AS LONG, J AS LONG
UB=UBOUND(X)
Y=24
LOCATE Y-1,10: PRINT "Testing for RandINT's period or length of cycle"
FOR N=1 TO UB
X(N)=RandInt
NEXT N
DO
FOR I=1 TO 1000000
INCR N
IF RandInt=X(1) THEN
Hit=1
FOR J=2 TO UB
IF RandInt=X(J) THEN INCR Hit
NEXT J
IF Hit >= UB THEN EXIT DO
END IF
NEXT I
LOCATE Y,10: PRINT N;
LOOP UNTIL INSTAT
IF INKEY$<>"" THEN N = -N
LOCATE Y,10: PRINT N
PRINT "press any key to continue"
WAITKEY$
FUNCTION = N
END FUNCTION

'demo
'Note, from RandINT above, that Rand. is a GLOBAL TYPE variable & that Function RandInt DIM's it.
'You can change Rand.seed, Rand.a, & Rand.m values as you wish -- but don't say I didn't warn you.
FUNCTION PBMAIN () AS LONG
DIM I AS LONG, A AS STRING, Period AS QUAD, RandConstM AS QUAD
DIM freq(9) AS LONG, Y AS LONG
DIM X AS EXT, sumX AS EXT, sumX2 AS EXT, N AS EXT, mean AS EXT, var AS EXT


Y=RandPick(4,666) 'pick generator# & seed.. If you don't, RandINT will use its defaults
Period=RandPeriod 'Start FUNCTION to determine how many randoms we can draw before the same
'sequence repeats. (Note: This could take many days for some generators)

Rand.seed = 1 're-seed & start over
DO
PRINT
FOR I=1 TO 10000
X= RNG 'same as RandINT / Rand.M
sumX = sumX+X
sumX2=sumX2+X*X:
N=N+1
A$=FORMAT$(X,".000000000000000000 ")
IF I>9900 THEN PRINT A$;
A$=MID$(A$,2,1) ' #2 is first digit within the fractional number
Y=VAL(A$)
INCR freq(Y)
NEXT

PRINT
PRINT "frequencies of the digits 0-9"
FOR I=0 TO UBOUND (freq)
PRINT freq(I);
NEXT
PRINT
PRINT "current seed num ";Rand.seed
'If initial seed was 1 and seed #10,001 is not 1043618065, exactly, then
' the Park & Miller minimal standard RNG was not implemented
IF N=10000 AND Rand.seed=1043618065 THEN
PRINT "Alright already... You used seed=1, a=16807, m=(2^31)-1. "
END IF

mean=sumX/N: var=(sumX2-(sumX^2/N)) /(N-1)
A$="mean "+ STR$(mean##,18)+" variance "+STR$(var##,18)+ " N "+STR$(N)
PRINT A$
PRINT "Enter q to quit, any number 1-"; Rand.M-1;
LINE INPUT " for new seed ";A$
IF VAL(A$) THEN Rand.seed = VAL(a$)
LOOP UNTIL UCASE$(A$)="Q"
END FUNCTION

------------------


[This message has been edited by Emil Menzel (edited March 23, 2005).]

IP: Logged

Emil Menzel
Member
posted March 23, 2005 08:51 PM     Click Here to See the Profile for Emil Menzel     Edit/Delete Message   Reply w/Quote

Just ran into: http://www.gnu.org/software/gsl/manual/gsl-ref_17.html

It makes my program look very elementary. The basic idea is
more or less the same, but the GNU Scientific Library
incorporates dozens of different generators -- many of them
more sophisticated than linear congruential ones.

If you know C and want to do other PB programmers a real
service, translate this whole library, or whichever sections
interest you the most, into PB! The source code is
available at no cost.

------------------

IP: Logged

All times are EasternTime (US)

next newest topic | next oldest topic

Administrative Options: Close Topic | Archive/Move | Delete Topic
Post New Topic  Post A Reply
Hop to:

Contact Us | PowerBASIC BASIC Compilers

Copyright © 1999-2005 PowerBASIC, Inc. All Rights Reserved.


Ultimate Bulletin Board 5.45c