22 June 2010, 18:53 | #1 |
son of 68k
Join Date: Nov 2007
Location: Lyon / France
Age: 51
Posts: 5,323
|
random number generation (in asm)
I've had a discussion with Thorham on that subject. My way is time counter reading (date/time, cia registers, and beam counter), with some computation on this (including the previous emitted random value).
What, according to people here, is the best method for that ? |
22 June 2010, 19:27 | #2 |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
The perfect PRNG that doesn't stall nor generates identical series on successive runs is probably a lot more complicated (and IMO unnecessary for anything in an Amiga context) than you'd think.
I would just settle for a simple algorithm with a fixed initial state in order to get a predetermined series which I know is good, and use some input such as mouse position counters or TOD clock to determine a starting offset in the series. Code:
Seed move.l #$9876fedc, d0 move.l #$abcd1234, d1 move.w JOY0DAT, d2 .loop swap d0 add.l d1, d0 add.l d0, d1 dbf d2, .loop movem.l d0-d1, PRNG.state rts PRNG movem.l .state(pc), d0-d1 swap d0 add.l d1, d0 add.l d0, d1 movem.l d0-d1, .state rts .state ds.l 2 |
23 June 2010, 09:57 | #3 |
Registered User
Join Date: Sep 2004
Location: Brasil
Age: 49
Posts: 181
|
The Leffmann's solution is pretty, simple and fast and how he said set the seed in function of the player input or any random action.
Other option is using a Xorshift PRNG, they are very easy to convert to 68000 (a few EORs and ASL/R) and if you only need numbers of 8 or 16 bits, take a look here (i'm using it actually in my z80 code and changing the coeficients between games to get a few more pseudorandom ). |
23 June 2010, 11:56 | #4 |
Registered User
Join Date: May 2010
Location: Tintern/UK
Posts: 35
|
If its any use, this is the routine I used in all my asm programs.
Its computationally expensive, so I used to cheat and pre-generate my random numbers into a list and then use that! Get_Random_Number * D0 - Lower Bound, D1 - Upper Bound, number returned in d0 mult equ 34564 inc equ 7682 seed equ 12032 mod equ 65535 move.l d2,-(sp) sub.w d0,d1 addq.w #1,d1 move.w old_seed,d2 mulu.w #mult,d2 add.l #inc,d2 divu.w #mod,d2 swap d2 move.w d2,old_seed mulu.w d1,d2 divu.w #mod,d2 add.w d2,d0 move.l (sp)+,d2 rts old_seed dc.w see |
23 June 2010, 17:23 | #5 | |
Registered User
Join Date: Mar 2009
Location: N/A
Posts: 23
|
Hi !
I think the best method is to use the cpu for programming a random function. Here i have found some information from the amycoders page. so long victim ------------------------- How are random numbers generated? True randomness is impossible to do on a computer since any number obtained will depend on the algorithm used to generate them and thus cannot possibly be random. Generally, it is sufficient to produce pseudorandom numbers, which are numbers that appear to be random. When we say they appear to be random, we mean that pseudorandom numbers should satisfy many of the properties that random numbers do. This is much easier said than done though. Suppose we only need to simulate a coin flip. One way to do this is to examine the system clock. Presumably the clock maintains the number of seconds in the current time; if the number is even, we can return 0(for heads), and if it's odd, we can return 1 (for tails). The problem is that this strategy does not work well if we need a sequence of random numbers. One second is a long time, and the clock might not change at all while the program is running. We would expect to generate all 0s or all 1s, which is hardly a random-looking sequence. Even if the time was recorded in units of microseconds, and the program was running by itself, the sequence of numbers that would be generated would be far from random because the time between the calls to the generator would be essentially indentical on every program invocation. What we really need is a sequence of pseudorandom numbers, a sequence with the same properties as a random sequence. Suppose we want random numbers between 0 and 999, uniformly distributed. In a uniform distribution, all numbers in the specified range are equally likely to occur. Other distributions are also widely used. We will see that most distributions can be derived from the uniform distribution, so that is the one we consider first. The following properties hold if the sequence 0...999 is a true uniform distribution:
Here we describe the simplest uniform generator that passes a reasonable number of statistical tests. By no means is it the best generator, but it is suitable for use as a reasonable generator in applications where a good approximation to a random sequence is acceptable. The method we use is the linear congruential generator, which was first described in 1951. Numbers X1,X2, ... are generated satisfying Xi + 1 = AXi(mod M) This equation state that we can get the (i+1)th number by multiplying the ith number by some constant A and computing the remainder when the result is divided by M. In C/C++ we would do something like: X[ i+1 ] = A * X[ i ] % M; The constants A and M are specified below. Notice that all generated numbers will be smaller than M. Some value X0 must be given to start the sequence. This value is known as the seed. If X0=0, then the sequence is not random because it generates all zeroes, but if A and M are carefully chosen, then any other seed satisfying 1 =< X0 < M is equally valid. If M is a prime, then Xi is never 0. For example, if M = 11,A=7, and the seed X0=1, then the numbers generated are 7,5,3,10,4,6,9,8,1,7,5,2, ... When we generate a number a second time, we have a repeating sequence. In our case the sequence repeats after M-1 = 10 numbers; this is known as the period of the sequence. The period obtained with this choice of A is clearly as good as possible, since all nonzero numbers smaller than M are generated. (We must have a repeated number generated on the 11th iteration). I believe this is a fairly detailed and extensive introduction to pseudorandom number generation which can be applicable in both assembler and C with ease. If you have made your own generator, and think it's better than this one, test it against all of the above properties, and if it holds, you can consider yourself lucky. ;-) Example 1: Code:
moveq #1,d7 move.l #9999,d0 loop: bsr Random dbf d0,loop rts *********************************************************** * Input * d7 : seed * * Output * d7 : pseudo-random number * * Uses * d2-d7 Random: move.l d7,d6 ; copy Rand(i) move.l #127773,d2 ; sythetic modulus bsr.b Div ; divide d6 by 127773 move.l d4,d5 ; copy to K1 muls #-2836,d5 ; d5 = -2836 * K1 mulu #42591,d4 ; multiply d4 by 127773 move.l d4,d6 add.l d4,d4 add.l d6,d4 sub.l d4,d7 ; d7 = Rand(i) - K1 * 127773 moveq #4,d4 ; loop counter .loop: move.l d7,d6 ; multiply d7 by 16807 lsl.l #3,d7 sub.l d6,d7 dbf d4,.loop add.l d5,d7 ; d7 = Rand(i + 1) bpl.b .exit addi.l #$7fffffff,d7 ; normalize negative result .exit: rts *********************************************************** Div: add.l d6,d6 ; shift out unused bit moveq #0,d4 ; quotient moveq #14,d3 ; counter move.w d6,d5 ; save low word of Rand(i) swap d6 andi.l #$ffff,d6 ; d6 = Rand(i) DIV 2^15 .loop: add.w d4,d4 ; line up quotient and.. add.w d5,d5 ; dividend addx.l d6,d6 ; shift in bit of low word cmp.l d2,d6 ; trial subtraction bmi.b .exit sub.l d2,d6 ; real subtraction addq.w #1,d4 ; put 1 in quotient .exit: dbf d3,.loop ; loop rts Code:
;============================================================================= ; NAME ; RandomSeed - seed random number generator, call once at beginning of ; your program. CurrentTime provides useful values for this ; ; SYSNOPSIS RandomSeed( SeedValue ) D0 ; ; FUNCTION Seeds the random number generator ; ; INPUTS SeedValue - a longword containing any value you like ; ; RESULT Random number generator is initialised ; ;============================================================================ _RandomSeed MOVE.L 4(SP),D0 ;entry point for C functions RandomSeed ADD.L D0,D1 ;user seed in d0 (d1 too) MOVEM.L D0/D1,RND ; drops through to the main random function (not user callable) LongRnd MOVEM.L D2-D3,-(SP) MOVEM.L RND,D0/D1 ;D0=LSB's, D1=MSB's of random number ANDI.B #$0E,D0 ;ensure upper 59 bits are an... ORI.B #$20,D0 ;...odd binary number MOVE.L D0,D2 MOVE.L D1,D3 ADD.L D2,D2 ;accounts for 1 of 17 left shifts ADDX.L D3,D3 ;[D2/D3] = RND*2 ADD.L D2,D0 ADDX.L D3,D1 ;[D0/D1] = RND*3 SWAP D3 ;shift [D2/D3] additional 16 times SWAP D2 MOVE.W D2,D3 CLR.W D2 ADD.L D2,D0 ;add to [D0/D1] ADDX.L D3,D1 MOVEM.L D0/D1,RND ;save for next time through MOVE.L D1,D0 ;most random part to D0 MOVEM.L (SP)+,D2-D3 RTS ;============================================================================= ; NAME ; Random - returns a random integer in the specified range ; ; SYSNOPSIS RndNum = Random( UpperLimit ) D0 D0 ; ; FUNCTION returns a random integer in the range 0 to UpperLimit-1 ; ; INPUTS UpperLimit - a long(or short will do) in the range 1-65535 ; ; RESULT a random integer is returned to you, real quick! ; ; LIMITATIONS ; range was limited to 1-65535 to avoid problems with the DIVU instruction ; which can return real wierd values if the result is larger than 16 bits. ; ;============================================================================ _Random MOVE.L 4(SP),D0 ;C entry point Random MOVE.W D2,-(SP) MOVE.W D0,D2 ;save upper limit BEQ.S 10$ ;range of 0 returns 0 always BSR.S LongRnd ;get a longword random number CLR.W D0 ;use upper word (it's most random) SWAP D0 DIVU.W D2,D0 ;divide by range... CLR.W D0 ;...and use remainder for the value SWAP D0 ;result in D0.W 10$ MOVE.W (SP)+,D2 RTS BSS RND DS.L 2 ;random number END Quote:
|
|
23 June 2010, 18:55 | #6 |
Moderator
Join Date: Nov 2004
Location: Eksjö / Sweden
Posts: 5,604
|
I did some research on random number generation and made a short one modified from a paper by some Uni guys decennia ago, with improvements for minimum attractors.
It's really tricky to come up with some bit-massaging and math combo that doesn't result in patterning or biasing, especially if it is to deliver say, the full range of an axis. Best way to test it is to plot two axes scaled to fit in view for n x the axis length, then you can see patterns, range coverage and how quickly it covers the range. Or hear it, if you use it as noise generator. The 'whiter' the noise is, the better the algorithm. I think I used it in MonsterBobs, if so I'll grab that and share, um, brag :P EDIT: Nah, it wasn't, I needed something even smaller. So here's that standard tiny one for now Code:
RndW: bsr.s RndB rol.w #8,d0 RndB: move.b $dff007,d0 ;Hpos move.b $bfd800,d5 ;event counter eor.b d5,d0 RTS Also, if it's not allowed to repeat, it's not random. But for random playlists and stuff it's useful, even if you can use elimination of last n numbers to not mess up the random number generator routine itself. Last edited by Photon; 23 June 2010 at 20:05. |
24 June 2010, 06:16 | #7 |
Banned
Join Date: Jan 2010
Location: Kansas
Posts: 1,284
|
There is some random number code on Aminet as well. This one in particular may be interesting for assembly coders...
http://aminet.net/dev/asm/Random250Lib.lha |
24 June 2010, 13:22 | #8 | ||||||
son of 68k
Join Date: Nov 2007
Location: Lyon / France
Age: 51
Posts: 5,323
|
Well, so it appears that things are very similar to my actual method.
Here's my code. It's not very fast, but it needn't be in typical applications. And it seems to give statistically significative results ; better than real dice anyway. Code:
; random routine - takes rnd from 0 to D1-1 -> D0 (D1=0 means full 32-bit rnd) rnd move.l d2,-(a7) move.l seed,d0 move.w $dff006,d2 swap d2 move.b $bfe801,d2 lsl.w #8,d2 move.b $bfd800,d2 add.l d2,d0 mulu.l #$59fa769f,d2:d0 add.l d2,d0 addi.l #$4a5be021,d0 ror.l #6,d0 move.l d0,seed tst.l d1 ; 0 -> d0=full rnd32 beq.s .nul moveq #0,d2 divu.l d1,d2:d0 move.l d2,d0 ; modulo .nul movem.l (a7)+,d2 rts ; init the seed randomize lea $dff000,a2 ; custom chips lea $dc0000,a3 ; internal clock moveq #15,d7 ; (16 regs) move.w (a2),d0 swap d0 move.w d1,d0 ; d1 is bptr handle when we start ; depend on mem allocations .loop ; (so it's somewhat random too) rol.l #5,d0 add.w (a3),d0 addq.l #4,a3 dbf d7,.loop add.l $a(a2),d0 rol.l #7,d0 add.l $12(a2),d0 move.l d0,seed rts This discussion has started elsewhere, but i felt that a dedicated thread could be much better - and would allow other people to come in. So here are the quotes. 1. Time taken by the user can be used as rnd seed. Quote:
Quote:
Of course this isn't the generator by itself, but it certainly can be included in the seed. Btw i don't see why radio static could be a good source. 2. About using a table... Quote:
Quote:
Quote:
Quote:
Hmm. I hope everything is clear enough. But i really HAD to move that into another thread. Btw i still say that my actual random generator performs better than a real dice, because dice often (always) give some results more often than others, a problem which the generator haven't. Even though it's supposed to be pseudo random, what really is random and pseudo is a philosophical question, isn't it ? Do true random really exist ? Perhaps the answer comes from quantum physics. Then, as i've read that neurons appear to show quantic effects, they become unpredictable and the user is ultimately the best source of rnd for a computer. Hence the time the user takes to do something is a good source ! And so time is best source of random numbers, whether Mr Thorham wants it or not |
||||||
24 June 2010, 14:20 | #9 |
Registered User
Join Date: Mar 2009
Location: N/A
Posts: 23
|
Hi !
Good results you get through the usage of cia timers so long... victim Code:
loop: cmp.b #99,$dff006 bne.s loop move.w #$f0f,$dff180 move.w #50-1,d7 rands: moveq #5,d0 bsr GetRnd020 dbra d7,rands move.w #$bbc,$dff180 btst #6,$bfe001 bne.s loop rts Randomizer: lea maxrand(pc),a0 move.l d0,d2 st.b seeded-maxrand(a0) move.b $bfea01.l,d0 ;ciatodhi lsl.l #8,d0 move.b $bfe901.l,d0 ;ciatodmid lsl.l #8,d0 move.b $bfe801.l,d0 ;ciatodlow move.l d0,random_seed-maxrand(a0) tst.b seeded-maxrand(a0) bne.b valid_seed st.b seeded-maxrand(a0) move.b $bfea01.l,d0 ;ciatodhi lsl.l #8,d0 move.b $bfe901.l,d0 ;ciatodmid lsl.l #8,d0 move.b $bfe801.l,d0 ;ciatodlow move.l d0,random_seed-maxrand(a0) move.l d2,d0 valid_seed: move.l d0,(a0) ;maxrand moveq #0,d1 move.b $bfe801.l,d1 ;ciatodlow add.l random_seed(pc),d1 move.l #$19660D,d0 mulu.l d1,d1:d0 move.l d0,random_seed-maxrand(a0) moveq #0,d0 swap d0 move.l (a0),d3 ;maxrand mulu d3,d0 moveq #0,d0 swap d0 rts maxrand: dc.l 0 random_seed: dc.l 0 seeded: dc.w 0 |
24 June 2010, 19:36 | #10 | |||||||
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 47
Posts: 3,764
|
Quote:
Quote:
Quote:
Quote:
Quote:
Again, it all depends on what you need, and that's exactly why you 'can't' say that your PRGN is better than dice throws. Quote:
Quote:
It's not about what I want, meynaf. If only you could make a true RNG like this, that would be great. But it's because you can't, that hardware RNGs exist. Time is not unpredictable, and even the speed of time can be measured! Time is not the best source for random numbers, although you can still use those methods to get effective results (although they're still not truly random, sadly). |
|||||||
24 June 2010, 21:41 | #11 | |
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 47
Posts: 3,764
|
Quote:
Code:
;------------------------------------------------------------------------------------ ;XOR shift RND generator. ;------------------------------------------------------------------------------------ start bsr xor128.init lea dat,a0 move.l #1024,d7 .loop bsr xor128.gen move.l d2,(a0)+ subq.l #1,d7 bne .loop rts ;------------------------------------------------------------------------------------ xor128.init ;Call once before calling xor128.gen. ;------------------------------------------------------------------------------------ move.l a0,-(sp) lea xor128_data,a0 move.l #123456789,(a0)+ move.l #362436069,(a0)+ move.l #521288629,(a0)+ move.l #88675123,(a0)+ move.l (sp)+,a0 rts ;------------------------------------------------------------------------------------ xor128.gen ;Gets a random number. ;------------------------------------------------------------------------------------ ;out: d2=random number. ;------------------------------------------------------------------------------------ movem.l d0-d1/a0-a1,-(sp) lea xor128_data,a0 move.l a0,a1 move.l (a0)+,d0 move.l (a0)+,(a1)+ move.l d0,d1 lsl.l #8,d1 lsl.l #3,d1 move.l (a0)+,(a1)+ eor.l d1,d0 move.l d0,d2 lsr.l #8,d2 eor.l d0,d2 move.l (a0)+,d1 move.l d1,(a1)+ eor.l d1,d2 moveq #19,d0 lsr.l d0,d1 eor.l d1,d2 move.l d2,(a1) movem.l (sp)+,d0-d1/a0-a1 rts ;------------------------------------------------------------------------------------ section data,data_f xor128_data dcb.l 4 dat dcb.b 1024*1024 ;------------------------------------------------------------------------------------ |
|
25 June 2010, 00:24 | #12 |
Registered User
Join Date: Jun 2010
Location: PL?
Posts: 2,771
|
|
25 June 2010, 21:10 | #13 |
Moderator
Join Date: Nov 2004
Location: Eksjö / Sweden
Posts: 5,604
|
Apart from first tries etc, which are usually shit (just look at my first try - it used the realtime clock...!), it's quite interesting to experiment with it. It's a good test of your algorithm design skills. The plot method gives more clues than the 'noise whiteness' method, of course, but they're both good.
I'd avoid constants as much as possible though, unless there's a researched (ie. not experimented) reason for them. |
25 June 2010, 22:04 | #14 |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
If you want to put your numbers up to real, rigid tests then look up The Diehard Battery of Tests of Randomness. The Xorshift family of algorithms that Lionheart linked to pass all of these tests, and the algorithms are simple, fast and scalable and IMO the best choice.
Though I dare say that for most purposes on the Amiga, being games I guess, sticking a few hundred values in a list, scrambling them around a bit and cycling through them, will be sufficient. |
25 June 2010, 22:27 | #15 | |
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 47
Posts: 3,764
|
Quote:
Code:
start lea $bfd800,a0 ;Counter from time of day clock. lea tab,a1 moveq #0,d0 moveq #0,d1 moveq #0,d2 move.l #255,d4 move.l #$11111111,d5 move.l #1023,d6 .loop_x move.l #1023,d7 rol.l #1,d1 move.b (a0),d0 add.l d0,d1 .loop_y rol.l #4,d1 add.l d2,d1 add.l d5,d2 move.l d1,d3 and.l d4,d3 move.b d3,(a1)+ dbra d7,.loop_y dbra d6,.loop_x rts section data,data_f tab dcb.b 1024*1024 Last edited by Thorham; 27 June 2010 at 04:16. Reason: rol.l #2,d1 -> rol.l #4,d1 |
|
26 June 2010, 00:03 | #16 | |
Registered User
Join Date: Sep 2004
Location: Brasil
Age: 49
Posts: 181
|
Quote:
|
|
26 June 2010, 00:27 | #17 |
Registered User
Join Date: Jun 2010
Location: PL?
Posts: 2,771
|
Write POTGO, read values from POTxDAT - they can be really random
|
27 June 2010, 17:07 | #18 |
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 47
Posts: 3,764
|
Thought this might be amusing:
Last edited by Thorham; 31 August 2017 at 22:26. |
28 June 2010, 12:01 | #19 | ||||||||||
son of 68k
Join Date: Nov 2007
Location: Lyon / France
Age: 51
Posts: 5,323
|
Quote:
And this is because they can't be perfectly physically balanced, and they also get worse as they get used. Quote:
Come on, stay real. Quote:
Quote:
Quote:
Quote:
Quote:
On the other hand, my random generator doesn't have these problems. This is why i see it as better than the dice. Quote:
Quote:
Quote:
As we don't know this for sure, we can't tell that something is true random and something else isn't. Time doesn't exist by itself if you ask me. What's used isn't just "time", but time it takes to perform something whose timing isn't predictable. At the end, i'll say that my rnd generator never failed me, where dice did. If you can find a specific application where it's not good enough, then tell me (and test it before speaking ). Yeah, you never know. |
||||||||||
28 June 2010, 17:09 | #20 | |||||||||||||||
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 47
Posts: 3,764
|
Quote:
Quote:
Quote:
Applications for which you need true random data, such as for gambling games where money is involved, for example. Hence the existence of hardware RNGs. Quote:
Quote:
Quote:
Unpredictable PRGNs are used in cryptography, and they're more complex than yours, and even those can be broken, although it's extremely difficult. Actually, there are better PRNGs than yours that aren't secure. Quote:
Quote:
What I want to know is why you think your PRNG is so good? Quote:
Quote:
Quote:
Quote:
Quote:
Quote:
Quote:
Here's some further reading on the subject: computers are lousy random number generators. Last edited by Thorham; 28 June 2010 at 17:21. |
|||||||||||||||
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Random question | 251Mario | project.EAB | 1 | 16 May 2013 02:19 |
HELP! A600 number 2 down! :( | Snowy | support.Other | 5 | 04 December 2011 22:12 |
Help needed!!Random octal numbers generator(asm) | sheryn88 | Coders. General | 6 | 01 August 2010 07:19 |
Random crashes | ami_stuff | support.WinUAE | 8 | 06 February 2009 16:51 |
D/Generation | IanMac | support.Games | 2 | 04 November 2002 16:47 |
|
|