[A86] Re: Random number generator


[Prev][Next][Index][Thread]

[A86] Re: Random number generator




There are lots of different methods.  The classic method used by most C
libraries is the linear congruential method, as described by Knuth in volume
2 of The Art of Computer Programming.  I'm not suggesting that you go out
and get those books, but they definitely have a lot of interesting stuff.

A good page on LCRNG's is at the following URL:

http://probabilitynet.com/lincong.htm

There is plenty of information out there on the technique, as a quick google
search will show you.  The following routine is from Matthew Shepcar's
Bomber Bloke.  I'm fairly sure it is a linear congruent generator:

; a spiffingly excellent random number generator:

Random:
        ld hl,0
        ld de,12345
Seed =$-2
        ld a,7921 & 255
        ld bc,1000h+(7921/256)
domult16:
        add hl,hl
        rla
        rl c
        jr nc,noadd16
        add hl,de
noadd16:
        djnz domult16
        inc hl
        ld (Seed),hl ;seed=(seed*7921+1) MOD 65536
        ld a,h
        ret

It's actually pretty simple if you see through the code (Shepcar has a thing
about writing awesome code that you can never figure out).  The comment that
shows what happens to the seed is indeed correct.

It starts out by loading the seed into DE.  It starts out at 12345.  B is
used as the loop counter, to multiply.  It's loaded with $10, or 16.  A and
C are used for the multiply constant, 7921.  The loop multiplies DE by AC,
with the result in HL.  HL is then incremented, as the addition constant is
1.  The seed is then saved, so it can be used the next time.  The routine
returns the value in HL and in A.  The 8 bit value (in A) is the high byte
of the value (as is recommended by Knuth).

If you want a good routine and can get by with about 800-900 t-states (quick
guess of it's speed), then I recommend using it.

I probably should have used that routine when writing Zelda 86, but I
decided to write my own.  The routine is probably not very good, but seemed
to work ok.  It's an interesting example of just trying stuff until you get
workable results.  Don't use it if you need statistically correct numbers,
or even close to perfectly distributed numbers.  I don't think it will
return every possible value.  Then again, I'm not sure exactly what it does,
and I don't feel like figuring it out :)

; a fast, yet decent random number generator
; returns: a = psuedo random number
; destroyed: flags
RandomNumber:
 push bc
 ld a,r
 ld b,a
 ld a,($1234)
_@RandomNumberSeed =$-2
 ld c,a
 ld a,(bc)
 ld (_@RandomNumberSeed),a
 xor b
 sub b
 add a,c
 pop bc
 ret

It basically does some junk operations against the R register, the seed, and
some "random" byte pulled out of the ROM.

Another approach that should work pretty well is to use a table.  Linear
congruential is mathematically proven to be correct, so it's likely better.
But I'm pretty sure a table would be faster and with the right algorithm,
return even distribution and provide a long period.  I used this method when
doing Game Boy programming.  This is the random number generator used in
Fall Down:

Random:
 push hl
 push de
 ld hl,RandSeed
 ldi a,(hl)
 ld h,(hl)
 ld l,a
 ld de,103
 add hl,de
 ld a,l
 ld (RandSeed),a
 ld a,h
 ld (RandSeed+1),a
 cpl
 add a,l
 ld d,0
 ld e,a
 ld hl,RandTable
 add hl,de
 ld a,(hl)
 pop de
 pop hl
 ret

RandTable:
 .db $69,$ce,$9b,$b4,$54,$a5,$9e,$cd,$57,$12,$4c,$65,$14,$4d,$92,$58
 .db $35,$8b,$a9,$05,$40,$dd,$ed,$bd,$0a,$1b,$bc,$b0,$c7,$15,$e7,$8c
 .db $4b,$88,$d7,$93,$50,$63,$d9,$fa,$af,$82,$7a,$00,$5a,$b9,$09,$bb
 .db $16,$75,$e3,$72,$f3,$c8,$ba,$d6,$e9,$37,$b6,$07,$e1,$c0,$79,$db
 .db $d3,$f2,$9a,$6a,$aa,$b5,$20,$56,$04,$5c,$e6,$59,$83,$2e,$95,$b7
 .db $c1,$5b,$28,$d4,$74,$9c,$5d,$66,$ec,$23,$67,$9d,$a1,$64,$0f,$2c
 .db $6c,$8e,$bf,$08,$99,$7f,$4a,$33,$b8,$6e,$fc,$6d,$a8,$36,$89,$5e
 .db $c4,$73,$b1,$d2,$32,$fe,$7b,$85,$55,$80,$53,$a4,$0e,$7e,$d0,$c5
 .db $f8,$18,$0b,$a6,$c2,$c9,$13,$dc,$94,$71,$eb,$86,$c3,$62,$84,$46
 .db $47,$90,$3f,$da,$a3,$6f,$06,$f5,$98,$ca,$8d,$2f,$e8,$9f,$60,$de
 .db $61,$fb,$ad,$3b,$f7,$3e,$4f,$0c,$f4,$e0,$d8,$ac,$d1,$31,$c6,$34
 .db $a2,$17,$8f,$ea,$2a,$5f,$96,$81,$45,$48,$f9,$30,$21,$68,$27,$f1
 .db $fd,$4e,$ee,$cc,$78,$77,$1d,$97,$1c,$24,$3d,$a0,$2d,$26,$03,$22
 .db $e5,$11,$ef,$6b,$b2,$e2,$02,$49,$3a,$d5,$87,$76,$39,$ab,$df,$1e
 .db $29,$cb,$1f,$52,$7c,$01,$3c,$38,$0d,$41,$f6,$1a,$2b,$ae,$f0,$10
 .db $91,$b3,$43,$7d,$42,$44,$70,$8a,$cf,$ff,$25,$a7,$be,$19,$51,$e4

The table is evenly distributed with 256 bytes.  Looking back at the code,
the last part seems pretty silly.  It loads a 16 bit random seed, adds 103
to it and  saves the random seed.  It then adds the complement of the high
byte with the low byte, and retreives that position in the table as the
random number.  I think it would work just fine if only the low byte of the
seed were used.  Then again, maybe it doesn't matter.

Amazingly, I even have the C code that generated the table.  I cut the
important part:

  srand(time(0));
  for(i = 0; i < 256; i++)
    a[i] = i;
  for(i = 0; i < 256 * 16; i++)
    swap(&a[rand() % 256], &a[rand() % 256]);

Hopefully, this should answer everyone's questions about random number
generators :)

> Does anyone know how to generate a random number without using the r
> register? (I wnat to generate several in a row and using the r register
they
> are almost identical.)






Follow-Ups: References: