4

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).

  • @rcgldr This expression could never generate the upper bound 278. The biggest result would be 276. – Sep Roland Dec 02 '18 at 21:37
  • 1
    @SepRoland - I wasn't sure if 278 was included. In that case ((random_number % 59) + 81) * 2. I deleted my prior comment. – rcgldr Dec 02 '18 at 21:41

4 Answers4

6

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}
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
5

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 :)

2

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.

rcgldr
  • 27,407
  • 3
  • 36
  • 61
0

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...

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • 1
    I'm a bit puzzled as to why you would *retry* on even numbers when it's precisely the even numbers that the OP wants. – Sep Roland Dec 02 '18 at 22:32
  • The assembly snippet will return every number below or equal to 500 except 278. You didn't use the lower bound (162). – Sep Roland Dec 02 '18 at 22:35
  • Is this *"bias caused by modulo of non-power of 2"* related to **all** RNG or just a problem with the **pseudo** RNG? – Sep Roland Dec 02 '18 at 22:41
  • @SepRoland: OP wants "not able to generate an even random number between 162 and 278" so even numbers (that are "not able to generate") need to be retried. I fixed the lower bound bug in the assembly :-) – Brendan Dec 02 '18 at 22:43
  • 1
    @SepRoland: More correctly; to reduce the range of a random number without causing bias the divisor of the modulo has to divide the number of possibilities evenly; so if you have (e.g.) a 20 sided dice (values 1 to 20) then modulo would have to be a factor of 20 (e.g. 2, 4, 5 or 10) and other values will cause bias. For RNGs on computers the original value is typically (always?) a power of 2, so "modulo of (smaller) power of 2" is always going to be fine. – Brendan Dec 02 '18 at 22:52
  • Hrm. In hindsight; I'm not sure what OP wants - there's 2 ways to interpret it ("wants code that is not able to generate even numbers from 162 to 278" vs. "wants code that generates even numbers from 162 to 278 but wasn't able to write it"). At first reading I didn't consider the second interpretation to be plausible (too easy for someone to need to ask as its little more than "v = v*2 + k") so I assumed the first interpretation. – Brendan Dec 02 '18 at 23:37