tags: rng prng random
I needed a repeatable mixed number stream for a game using only 32 bit signed integers. Monkey's rnd function is good but I wanted numbers that were more mixed, even if only slightly more mixed. None of the formulas I could find which worked within 31 bits would produce an output I liked. After much experimentation I finally tweaked all of the ideas into a formula that did what I wanted.
#rem
'Mars MNG (Mixed Number Generator) presented 2013-09 by apo
'A routine for producing scrambled number sequences
' using 32 bit signed integers.
#end
Import mojo
Private
Const bits31 = 2147483647
Const marsSize = 64
Const marsMax = marsSize - 1
Const marsD1 = marsSize / 3
Const marsD2 = marsSize * 2 / 3
Global marsX:int = 521288629
Global marsY:int = 362436069
Global marsZ:int = 217598643
Global marsNdx:int = marsMax
Global marsA:Int[] =[20814, 20970, 21153, 21243, 21423, 21723, 21954, 22125, 22188, 22293,
22860, 22938, 22965, 22974, 23109, 23124, 23163, 23208, 23508, 23520,
23553, 23658, 23865, 24114, 24219, 24660, 24699, 24864, 24948, 25023,
25308, 25443, 26004, 26088, 26154, 26550, 26679, 26838, 27183, 27258,
27753, 27795, 27810, 27834, 27960, 28320, 28380, 28689, 28710, 28794,
28854, 28959, 28980, 29013, 29379, 29889, 30135, 30345, 30459, 30714,
30903, 30963, 31059, 31083]
Public
'--- inspired by the Marsaglia MNG ---
Function mnMars:Int(r) 'mn = mixed number output function
marsNdx = (marsNdx + 1) & marsMax
marsX = ( (marsX * marsA[marsNdx]) ~ (marsY shr 15)) & bits31
marsY = ( (marsY * marsA[ (marsNdx + marsD1) & marsMax]) ~ (marsZ shr 15)) & bits31
marsZ = ( (marsZ * marsA[ (marsNdx + marsD2) & marsMax]) ~ (marsX shr 15)) & bits31
Return ( (marsX + marsY + marsZ) & bits31) * 4.6566125E-10 * r
End Function
#rem
using: mnMarsInit(123, 456, 789) and mnMars(1000000000)
first 5 values: 282319147
972584087
694988900
773596550
859902265
#end
Function mnMarsInit(s1, s2, s3) 's <= 0 = arbitrary seed
Local date:= GetDate()
If (s1 <= 0) or (s2 <= 0) or (s3 <= 0)
marsX = date[1] * 9301 + date[6] * 100000
marsY = date[2] * 9301 + date[5] * 100000
marsZ = date[3] * 9301 + date[4] * 100000
Else
marsX = s1
marsY = s2
marsZ = s3
End If
marsNdx = marsMax
mnMars(99) 'the first output is discarded because seeds having
End Function ' low values produce a low initial ouput
Global mnMonkey = False
Function mn:Int(range)
If mnMonkey Then Return Rnd(range)
Return mnMars(range)
End Function
Function mnInit()
Local date:= GetDate()
If mnMonkey
Seed = date[1] * 9999 + date[2] * 8888 + date[3] * 7777 +
date[4] * 6666 + date[5] * 5555 + date[6] * 444444
Else
mnMarsInit(0, 0, 0)
End If
End Function
Class mnResults Extends App
Const MNSTREAM_SIZE = 20 * 6
Field mnStream[MNSTREAM_SIZE]
Field birthdays[60]
Field p[2], x[100], y[100]
Field ClrView, k, n, row, GetData
Field c0, c1, r0, r1, t0, t1
Method OnCreate()
SetUpdateRate 30
mnMarsInit(123, 456, 789)
' For Local k = 0 To 4
' Print(mnMars(1000000000))
' End For
GetData = True; ClrView = True
c0 = 0; c1 = 0; r0 = 0; r1 = 0; t0 = 0; t1 = 0
End Method
Method OnUpdate()
If KeyHit(KEY_SPACE) Then GetData = True 'get more data
If KeyHit(KEY_R) 'reseed
mnInit(); GetData = True; ClrView = True
c0 = 0; c1 = 0; r0 = 0; r1 = 0; t0 = 0; t1 = 0
End If
If KeyHit(KEY_M) 'change MNG
If mnMonkey Then mnMonkey = False Else mnMonkey = True
mnInit(); GetData = True; ClrView = True
c0 = 0; c1 = 0; r0 = 0; r1 = 0; t0 = 0; t1 = 0
End If
If GetData
GetData = False
For k = 0 Until MNSTREAM_SIZE
mnStream[k] = mn(1000000000)
End For
For k = 0 Until 60
birthdays[k] = 1 + mn(365)
End For
End If
For k = 0 Until 100
For n = 0 Until 2
p[n] = mn(150)
If (p[n] mod 2) < 1
c0 += 1; r0 += 1; r1 = 0 'count 0's
If r0 > t0 Then t0 = r0
Else
c1 += 1; r1 += 1; r0 = 0 'count 1's
If r1 > t1 Then t1 = r1
End If
End For
x[k] = p[0]; y[k] = p[1] 'pixel coordinates
End For
End Method
Method OnRender()
If ClrView
ClrView = False
Cls
Else
SetColor(0, 0, 0)
DrawRect(0, 0, 510, 280)
DrawRect(0, 300, 370, 174)
SetColor(255, 255, 255)
EndIf
For k = 0 To 19
row = k * 14
DrawText(k, 5, row)
For n = 0 To 5
DrawText(mnStream[k + n * 20], 40 + n * 80, row)
End For
End For
For k = 0 To 9
row = 300 + k * 14
DrawText(k + 1, 5, row)
For n = 0 To 5
DrawText(birthdays[k + n * 10], 40 + n * 40, row)
End For
End For
If mnMonkey
DrawText("Monkey MNG", 280, 300)
Else
DrawText("Mars MNG", 280, 300)
End If
DrawText("# same total", 280, 398)
DrawText("0 " + t0 + " " + c0, 280, 412)
DrawText("1 " + t1 + " " + c1, 280, 426)
DrawText("Space = more data, R = Reseed, M = change MNG", 5, 460)
For k = 0 Until 100
DrawPoint(440 + x[k], 300 + y[k])
End For
End Method
End
Function Main ()
New mnResults()
End Function
|