简体   繁体   中英

Random number generation in assembly NASM

I am working on a university project in assembly nasm. The only problem is i am not able to generate an even random number between 162 and 278. I have tried many algorithms but cant seem to restrict the number in range.

Is there a small trick or tweak to get the desired number in range? The aim is to display a fruit randomly on the screen(It is mainly a snake game).

I look a little bit in this post: nasm random number generator function . Maybe you can try (rdtsc % (278 - 162) / 2 + 162 / 2) * 2. I hope I helped you :)

generate an even random number between 162 and 278

Step 1

"generate random number"
The task of displaying a fruit in a snake game is not too demanding, so I think using the TimeStampCounter is quite ok. You'll continu with the low dword found in EAX .

-

rdtsc                        ; -> EDX:EAX

Step 2

"number between 162 and 278"
To restrict the number to your desired range of [162,278], you calculate
(RandomNumber mod NumbersInTheRange) + StartOfTheRange .

  • RandomNumber is the EAX register from the 1st step.
  • NumbersInTheRange is upper bound - lower bound + 1 . Don't forget that +1.
  • StartOfTheRange is its lower bound.

-

xor     edx, edx             ; Required because there's no division of EAX solely
mov     ecx, 278 - 162 + 1   ; 117 possible values
div     ecx                  ; EDX:EAX / ECX --> EAX quotient, EDX remainder
mov     eax, edx             ; -> EAX = [0,116]
add     eax, 162             ; -> EAX = [162,278]

Step 3

"even number"
Make the number even by dropping its lowest bit.

-

and     eax, -2              ; -> EAX = {162,164,...,276,278}

As posted in my comment, you could use

 ((random_number % 59) + 81) * 2

Brandon's next power of 2 - 1 is simple, but you'd need to choose and implement an random number generator. A lot of these random number generators are LCG's that use a multiply and add modulo some power of 2, however, one could be created specifically for modulo some power or multiple of 59, which would eliminate the need to discard numbers that are too big and repeat the random number sequence. Wiki article on this.

https://en.wikipedia.org/wiki/Linear_congruential_generator

Another alternative would be a non-binary Galois LFSR:

https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Non-binary_Galois_LFSR

However, LFSR's do not include 0, so they produce q^m - 1 values. A check could be made for when the LFSR cycled back to it's initial state and at that point return zero to end up with q^m values.

Since 59 is a prime number, an LFSR could use GF(59^2) based on primitive polynomial 1x^2 + 1x + 2 (there are many other primitive polynomials for GF(59^2), but any of them will work). This will cycle through all 3480 non-zero values. Each time the LFSR cycles back to it's initial state, a zero could be output to end up with 3481 (59^2) values. An Ascii art version for this LFSR would look like (all the math would be %59):

1x^2  1x     2

→    *1     *2 
↑     ↓      ↓
↑     +      +  
←   ┌━┐ ← ┌━┐
    └━┘   └━┘

I did this and used the low order term (the b value in ax+b) to produce a set of 3481 values as shown in this linked to text file:

http://rcgldr.net/misc/x3481.txt

If you want a longer cycle (12117360 cycles + 1 cycle for the zero), you could use GF(59^4) based on primitive polynomial 1x^4 + 1x + 14:

1x^4 0x^3  0x^2   1x     14

→    →      →     *1    *14 
↑                  ↓      ↓  
↑                  +      +  
←   ┌━┐ ← ┌━┐ ← ┌━┐ ← ┌━┐
    └━┘   └━┘   └━┘   └━┘

Note - I find primitive polynomials using a brute force search program to find a LFSR that takes q^m-1 cycles to repeat.

First; understand that (for random number generation) modulo by a "non-power of 2" causes bias and should be avoided. For example, if you want a number from 0 to 2 and start with a 4-bit random number (values 0, 1, 2, .., 14, 15) then after the "modulo 3" the values will become 0, 0, 0, 1, 1, 1, ..., 4, 4, 4, 5) and the value 5 will be a lot less likely than any other value.

The easiest way to fix "bias caused by modulo of non-power of 2" is to mask (with AND) to the next highest power of 2 minus 1, then discard the number and retry if the value is still out of range. For example, if you want a number from 0 to 2 and start with a 32-bit random number; you'd do "number = number & (4-1)" (because 4 is the next highest power of 2), and then if the number is larger than 2 you'd discard it and get a whole new random number again.

Now...

If you're retrying to avoid "bias caused by modulo of non-power of 2"; why not also retry if the number happens to be an even number in the range 162 to 278?

For example (in C):

#define MAX_VALUE         500
#define NEXT_POWER_OF_2   512

int getNumber(void) {
    int number;

    do {
        number = rand();
        number = number & (NEXT_POWER_OF_2 - 1);
    } while( (number > MAX_VALUE) || ((number >= 162) && (number <= 278) && (number & 1 == 0)) );
}

For assembly:

;Input
; none
;
;Output
; eax = random number in range from 0 to MAX_NUMBER that is not an even number from 168 to 278

%define MAX_VALUE         500
%define NEXT_POWER_OF_2   512

getNumber:
    call getRandomDword         ;eax = random 32-bit value
    and eax,NEXT_POWER_OF_2-1   ;eax = random N-bit value
    cmp eax,MAX_VALUE           ;Is it too large?
    ja getNumber                ; yes, retry
    cmp eax,278                 ;Is it larger than the "not even" range?
    ja .done                    ; yes, allow the number
    cmp eax,162                 ;Is it smaller than the "not even" range?
    jb .done                    ; yes, allow the number
    test al,1                   ;Is it even?
    je getNumber                ; yes, retry

.done:
    ret

Note: I have no idea if you want 16-bit, 32-bit or 64-bit code, or if the code has to work on which CPUs, or what the source of randomness will be. For example, recent CPUs support a rdrand instruction that is relatively slow (it's intended for cryptography not speed but can be used to regularly "re-seed" a pseudo-random number generator) but if you need to make sure the code works fine on an old 80386 then...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM