Author Topic: How to generate normal / Gaussian randoms  (Read 4056 times)

0 Members and 1 Guest are viewing this topic.

Offline Joshua

  • Newbie
  • Posts: 10
    • View Profile
How to generate normal / Gaussian randoms
« on: December 29, 2018, 06:56:33 pm »
Given RND, does anyone have function that can give a fairly decent approximation to normally distributed values?  It doesn't have to be exact, speed of computation is more important.

Offline Joshua

  • Newbie
  • Posts: 10
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #1 on: December 30, 2018, 08:36:11 pm »
For anyone interested, a very approximate normally distributed randoms can be generated by multiplying two random numbers:

NRND = (RND*2-1)*(RND*2-1)

This will give values between -1 and 1, and if you plot them in a histogram, they will be mostly clustered around 0.

Not great, but it's fast, good for introducing a little natural variability to simulations.

Offline MWheatley

  • Newbie
  • Posts: 64
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #2 on: December 31, 2018, 08:21:02 am »
Twice in a week!  Something I can make a contribution on.

The code below both calculates normally-distributed random numbers, and plots them in a histogram.  I have similar code for a couple of other distributions (Poisson, etc) as well as the original academic citations, somewhere.

I also have code for the t-distribution, for assessing statistical significance.

Code: QB64: [Select]
  1. ' Program to generate random numbers from a Normal distribution
  2. ' Program developed in early 1980s in Commodore Basic from published listing
  3. ' Moved to Liberty Basic in November 2007
  4. ' Converted to QuickBasic 4.5 and QB64 in December 2014
  5. ' Edit values in lines 510 and 515 to change the distribution's SD and mean
  6.  
  7.  
  8.  
  9. 10 Z = 3000: REM NUMBER OF RANDOM NUMBERS
  10. 20 DIM A(Z): DIM B(20)
  11. 30 GOSUB 500
  12. 40 L = .05
  13. 50 FOR J = 0 TO 1 STEP L
  14. 60 FOR I = 1 TO Z
  15. 70 IF A(I) <= J + L AND A(I) > J THEN B(((J * 2) * 10) + 1) = B(((J * 2) * 10) + 1) + 1
  16. 80 NEXT I
  17. 90 NEXT J
  18. 100 FOR I = 1 TO 20
  19. 110 IF B(I) > BMAX THEN BMAX = B(I)
  20. 120 NEXT I
  21. 130 L = 1
  22. 140 IF BMAX <= 75 THEN 175
  23. 145 BTEMP = BMAX
  24. 150 BMAX = (BMAX / L + 1): IF BMAX <= 75 THEN 170
  25. 155 BMAX = BTEMP
  26. 160 L = L + 1: GOTO 150
  27. 170 FOR I = 1 TO 20: B(I) = INT(B(I) / L): NEXT I
  28. 175 CLS
  29. 180 FOR I = 1 TO 20
  30. 185 IF B(I) = 0 THEN PRINT : GOTO 200
  31. 190 FOR J = 1 TO B(I): PRINT "Q"; : NEXT J: PRINT
  32. 200 NEXT I
  33. 205 PRINT "EACH BLOCK EQUALS"; L; "RANDOM NUMBERS"
  34. 207 PRINT "FROM A NORMAL DISTRIBUTION"
  35. 210 END
  36. 500 REM NORMAL DISTRIBUTION GENERATOR
  37. 505 N = VAL(MID$(TIME$, 7, 2)): RANDOMIZE N: REM RANDOM SEED
  38. 510 EX = .5: REM DISTRIBUTION MEAN
  39. 515 SD = .25: REM DISTRIBUTION STANDARD DEVIATION
  40. 520 FOR I = 1 TO Z
  41. 530 FOR J = 1 TO 12
  42. 540 R = RND(1)
  43. 550 SUM = SUM + R
  44. 560 NEXT J
  45. 570 A(I) = SD * (SUM - 6) + EX
  46. 580 SUM = 0
  47. 590 NEXT I
  48. 595 RETURN
  49.  

Offline Joshua

  • Newbie
  • Posts: 10
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #3 on: December 31, 2018, 02:27:16 pm »
Looks like it was written in GWbasic!  Would you be able to put the random number generator code portion and put it in a function?

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #4 on: December 31, 2018, 03:35:24 pm »
Interesting subject, I myself have wanted "normal" random distributions about a point when graphing.

Here is a function that lets you pick the level of scatter about a point. Level 1 is an even distribution on a range around X, 2 is close to the above codes, a parabolic distribution and levels beyond 2 are powers graphs...
Code: QB64: [Select]
  1. _TITLE "nRnd test" 'B+ 2018-12-31
  2. 'test a function to return a random number around X with +- range
  3. ' is it like a normal distribution?
  4. 'more like a power series
  5.  
  6. 'cont = -1
  7. 'WHILE cont
  8. '    CLS
  9. '    sr = 1
  10. '    r = RND
  11. '    FOR i = 1 TO 10
  12. '        sr = sr * r
  13. '        PRINT sr
  14. '    NEXT
  15. '    INPUT "press enter to repeat, press any + enter to quit..."; wate$
  16. '    IF LEN(wate$) THEN EXIT WHILE
  17. 'WEND
  18. ''OK keep multiplying a rnd number and it gets smaller
  19.  
  20. DIM dat(20)
  21. FOR l = 1 TO 6
  22.     FOR i = 1 TO 1000
  23.         'add .5 for rounding
  24.         r = INT(nRand(10, 5, l) + .5) 'expect nRand to return 5 to 15, so integer "categories" 0 to 20 should
  25.         dat(r) = dat(r) + 1
  26.     NEXT
  27.  
  28.     CLS
  29.     FOR i = 0 TO 20
  30.         PRINT STRING$(INT(dat(i) / 10), "X");
  31.         LOCATE i + 1, 40: PRINT nFill$(i, 3); nFill$(dat(i), 4); nFill$(INT(100 * dat(i) / 1000), 3); "%"
  32.     NEXT
  33.     PRINT "nRand(10, 5,"; l; ")  dat(9) + dat(10) + dat(11) = "; INT(100 * (dat(9) + dat(10) + dat(11)) / 1000); "%"
  34.     INPUT "press enter to continue or end..."; wate$
  35.     ERASE dat
  36.  
  37.  
  38. FUNCTION nRand (aroundX, pmRange, level)
  39.     IF RND < .5 THEN r = -pmRange * RND ^ level ELSE r = pmRange * RND ^ level
  40.     nRand = aroundX + r
  41.  
  42. FUNCTION nFill$ (n, fillLength)
  43.     nFill$ = RIGHT$(STRING$(fillLength, " ") + STR$(n), fillLength)
  44.  

Offline Joshua

  • Newbie
  • Posts: 10
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #5 on: December 31, 2018, 04:27:45 pm »
I'm not sure it's behaving correctly for me.  When I hit enter, the category 10 has a big spike that goes off screen.  Or how should I read the graph?   Maybe I'm doing it wrong.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #6 on: December 31, 2018, 04:41:05 pm »
Read it sideways, the function is finding random numbers around 10 with a plus minus (pm) range of 5.

The data is rounded to integer intervals so 5 is first showing hits and 15 is last showing hits.

It is a frequency distribution most frequent around 10 when level is above 1.

As level increases, the more and more the points are closer to 10.

There is a calculation to see the percent of hits for middle 3 intervals. At level = 1, it is an even distribution so 30% hits, as level increases this percent does spike past 80% heading off screen around 10.
« Last Edit: December 31, 2018, 04:47:19 pm by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #7 on: December 31, 2018, 04:54:37 pm »
Here is graphic demo using function for drawing points randomly on line across screen, level 1 is even distribution, level 2 gathers points around the center x = 300 with range +-300...
Code: QB64: [Select]
  1. _TITLE "nRnd demo" 'B+ 2018-12-31
  2. 'test a function to return a random number around X with +- range
  3. ' is it like a normal distribution?
  4. 'more like a power series
  5.  
  6. SCREEN _NEWIMAGE(600, 600, 32)
  7.  
  8. FOR l = 1 TO 3
  9.     CLS
  10.     FOR n = 1 TO 50
  11.         FOR y = 0 TO 600
  12.             x = nRand(300, 300, l)
  13.             PSET (x, y), _RGB32(255, 255, 255)
  14.         NEXT
  15.     NEXT
  16.     PRINT "Level "; l; " distribution:"
  17.     _DELAY 2
  18.  
  19. FUNCTION nRand (aroundX, pmRange, level)
  20.     IF RND < .5 THEN r = -pmRange * RND ^ level ELSE r = pmRange * RND ^ level
  21.     nRand = aroundX + r
  22.  
  23.  

Offline MWheatley

  • Newbie
  • Posts: 64
    • View Profile
Re: How to generate normal / Gaussian randoms
« Reply #8 on: January 01, 2019, 11:17:16 am »
Looks like it was written in GWbasic!  Would you be able to put the random number generator code portion and put it in a function?

It will run in QB45 and QB64 just fine.  I'm not sure I'm up to putting it into a function (I very rarely use them, preferring subroutines), but the relevant code is the lines beginning with 500.

Malcolm