Can the "random" function be limited to a range of numbers?

OLDmarty

Senior Member
Hi All,

I'm just starting to tinker with the Random command to light up 4 LEDS in any combination from $0h to $Fh.

As i'm only using 4 bits to drive the LEDs, the random word seems to spend a lot of time generating big numbers larger than $Fh, so my LEDs are often in the OFF state.

Can the random command be told to only generate numbers within a given range? or would i check if the number is greater than $Fh and then divide it down until it is equal (or less than) $Fh ?

I recently thought about using a lookup/lookdown array for my 16 values, but of course the large random word values prevent looking for the correct number in my lookup/lookdown selection.

I'm not sure what my options are now for something that seemed like an easy idea when i first began trying some code with the random command, now i've stuck myself in a hole lol.

with thanks in advance...
 

papaof2

Senior Member
Does your project include a real time clock? If so, you could read the seconds and do a little math (divide seconds by X, shift them left/right X columns, multiply by 2 and then divide by 3, etc.) to get a semi-random number. Save the number used and ensure the "new" number is different by some value - that "random" number might be something else to save to ensure you don't have adjacent number repeats in your pseudo-random numbers (unless that works well for the pattern(s) you want).

You may find other ideas for how you'd want to process a 8-bit "random" number: use the first 4 bits, the last 4 bits, the "middle" 4 bits, the odd bits, the even bits and as much "and so forth" as you want to play with ;-)
 

inglewoodpete

Senior Member
The easiest way to create a random number within limits is to use the // opertor (modulus divide). The Random function (command) must always have exclusive use of a word variable. For the 16-value (4-bit) result, use a byte variable.

Try the following piece of code in the simulator:
Rich (BB code):
#PICAXE 08M2
Do
   Random w12
   b5 = w12 // 16
   SerTxd (#w12, " ", #b5, CR, LF)
Loop
 

AllyCat

Senior Member
Hi,

If you want exactly 16 combinations, then the easiest is to simply AND the Word value with 15 (i.e. %1111). Note that you MUST use a Word variable and it will always generate exactly the same sequence from any given starting seed (e.g. 0). More than you'll probably ever require is HERE. ;)

Cheers, Alan.
 

bpowell

Senior Member
The easiest way to create a random number within limits is to use the // opertor (modulus divide). The Random function (command) must always have exclusive use of a word variable. For the 16-value (4-bit) result, use a byte variable.

Try the following piece of code in the simulator:
Rich (BB code):
#PICAXE 08M2
Do
   Random w12
   b5 = w12 // 16
   SerTxd (#w12, " ", #b5, CR, LF)
Loop
I'd change the following line:

Rich (BB code):
b5 = w12 // 0xF + 1
That way reads better to me, as it says the random number will be UP TO (and including) 0xF

That could just be a personal preference though ...
 

AllyCat

Senior Member
Hi,
Rich (BB code):
b5 = w12 // 0xF + 1
Yes that will generate a maximum of 15, but can never generate 0, so gives only 15 combinations.

Sometimes a range of (say) 1 to 16 will be desired, but here the OP has specifically requested 0 to $F so either // 16 or AND 15 , etc. is correct (or the equivalent Hex or Binary formats).

Because of the nature of the FeedBack Shift Register algorithm, it's best to use the LSBs (if starting from a small seed value), but for a "better" 4-bit random sequence, the RANDOM should be executed at least 4 times (however it's a surpringly "fast" executing instruction).

Cheers, Alan.
 

inglewoodpete

Senior Member
I'd change the following line:

Rich (BB code):
b5 = w12 // 0xF + 1
That way reads better to me, as it says the random number will be UP TO (and including) 0xF
OLDMarty specified that he wanted "4 LEDS in any combination from $0h to $Fh". The above formula does not meet the spec.
 

Aries

New Member
The best way of getting random values in a range is to use the ** operator. This gives the "upper" word of the product of two word values.

This code:
Code:
w0 = 42857
for b10 = 0 to 99
random w0
w1 = w0 ** 16
sertxd(13,10,#bit19,#bit18,#bit17,#bit16)
next b10
generates and displays random numbers (shown as binary for clarity) in the range 0-15. The starting value for w0 is arbitrary (I usually use 142857 or bits of it - you could use any reasonably-sized non-zero value to start).

The explanation goes like this:

When two word variables Wx and Wy are multiplied together, the result is potentially greater than the maximum value of a word, and so technically the result goes into two word variables (Wa,Wb). Think of multiplying two numbers in the range 0-99 - the result is in the range 0-9999, so you need twice as much space to store it.

Wa is actually (Wx*Wy) / 65536 (again think of decimal multiplication if it helps), and can be rewritten as Wx * (Wy/65536). If Wy is random in the range 0-65535, then Wa is random in the range 0-(Wx-1)
 

AllyCat

Senior Member
Hi,
Code:
w0 = 42857
for W5 = 0 to 999
random w0
w1 = w0 ** 16
; sertxd(13,10,#bit19,#bit18,#bit17,#bit16)
B6 = w1 and 15
LOOKUP B6,("0123456789ABCDEF"),B6    ; Convert to Hex
SERTXD(" ",B6)
next W5

#rem   Output for 0 to 99
4 9 3 7 E D B 6 D A 4 9 3 6 D B 6 C 8 0 1 3 6 C 8 1 3 6 D B 7 E C 8 1 3 6 D B 6 D A 5 A 4 9 3 6 C 9 2 5 B 6 C 9 2 4 8 0 1 3 6 C 9 2 4 8 1 3 7 F E D A 4 9 3 6 C 8 1 3 7 F F E C 8 0 1 2 4 8 0 1 2 4 8 1
continues with ....  2 4 9 2 4 9 3 6 C 9 3 7 E C 8 0 1 2 4 9 2 5 A 5 A 4 9 3 6 C 9 3 7 E C 9 3 7 E D A 4 9 3 6 C 8 0 0 0 0 1 3 6 C 8 1 3 6 D B 6 D B 6 D B 7 E C 8 1 2 4 9 3 7 E D B 6 C 8 0 1 2 5 A 5 A 4 9 3 6 D A 4 8 0 1 3 7 F E C 8 1 2 4 9 3 6 C 9 3 7 F F E D A 4 9 2 4 8 1 3 7 F F E C 9 3 7 E C 9 3 6 C 8 0 0 1 3 7 E D B 7 F E C 9 3 6 C 9 3 7 F F F E C 9 2 4 9 3 7 E C 9 2 5 A 4 9 2 4 8 0 1 3 6 D B 6 C 9 2 4 9 3 6 C 9 3 7 F E C 9 3 6 D B 6 C 8 1 3 6 D A 5 A 5 B 6 D A 5 A 5 A 4 9 2 5 B 6 D A 5 A 4 8 1 3 6 C 9 2 4 8 1 3 6 C 8 1 3 7 F F F F E D A 4 9 3 6 C 9 2 5 A 4 9 3 7 E D A 4 9 3 6 C 8 1 2 5 B 6 C 8 1 2 5 B 6 C 9 3 6 D B 7 E C 8 1 2 5 A 4 8 0 0 1 2 4 8 1 3 7 E D A 5 A 5 A 5 A 4 8 0 1 3 6 D B 7 F F F E D B 6 D B 6 C 8 1 2 4 8 1 2 4 9 3 7 E D B 6 D B 7 F E D B 6 D A 5 B 7 E D A 5 B 6 C 9 2 5 B 6 D A 5 B 7 E C 9 2 4 9 3 6 D A 4 9 2 5 A 4 8 0 0 0 0 1 3 7 F E D B 6 C 9 3 6 C 9 3 6 D A 5 B 7 E D A 4 9 3 6 C 8 0 1 3 7 F F E D B 6 D A 5 A 5 A 5 B 6 C 8 0 1 3 6 C 8 1 2 5 A 5 B 6 D B 6 D A 5 A 5 A 4 8 1 3 6 D B 7 E C 9 3 7 E D A 4 8 1 2 5 A 4 9 2 4 8 1 2 5 A 5 A
Sorry, but that's a terrible random generator. :(

I've only changed the SERTXD to print Hex characters so that many more can fit on the screen/page and increased the capabilty to more than 255 output values (although the problems are apparent at even 100 iterations). Notice how 0 is always followed by 0 or 1 , 1 by 2 or 3 , 2 by 4 or 5 , 3 by 6 or 7 , 4 by 8 or 9 , 5 by A or B , 6 by C or D , 7 by E or F , 8 by 0 or 1 , 9 by 2 or 3 , A by 4 or 5 , B by 6 or 7 , C by 8 or 9 , D by A or B , E by C or D and F by E or F .

For a "genuine" random number generator, repetitions should occur, for example here, on average every 16th iteration should repeat the previous (nibble) value. Above, only 0 and F repeat, but more frequently than they should, e.g. 0 0 0 0 and F F F F both appear above, which should occur only about once in every 65k nibbles (but I suspect that 5x will never occur). Also the sequence above will always be repeated after a restart unless the program itself is changed. Sometimes repetitions are not desirable, for example when creating a "Random" Playlist, but that can be fixed in a program if necessary.

Here is my attempt. The simulator doesn't appear to obey the #no_data instruction (i.e. NOT clearing the EPROM) so I've added another do : loop to test the "randomising" between runs of the same program. The program keeps a running total of repeated nibbles and the number of occurrences of each nibble to check for reasonable "Randomness". This might be quite important because I've noticed some unexpected "patterns" appearing, perhaps because the 4-bit nibbles are an integer divisor of the working 16-bit words.
Code:
#no_data                            ; Keep seed from previous run(s)
w1 = 0                              ; Repeats counter
do
  read 0 , b0 , b1                  ; w0 = Random seed
  inc w0 : write 0 , b0 , b1
  sertxd("  seed= ",#w0,cr,lf)
for w2 = 0 to 15
    b7 = w0 and 15
    random w0 : random w0 : random w0 : random w0
    b6 = w0 and 15                  ; Random nibble
    b8 = b6 / 10 * 7 + b6 + "0"
    sertxd(b8," ")                  ; Display the random nibble
    bptr = b6 + 30
    @bptr = @bptr + 1 max 255       ; Accumulate the incidence of the nibble
    if b6 = b7 then
       inc w1
    endif
next w2
for bptr = 30 to 45                 ; Display the incidence list
  b8 = bptr - 30   
  sertxd(" ",#b8,"=",#@bptr)
next bptr
sertxd(cr,lf,"Repeats= ",#w1)
loop
; Sample run:
  seed= 1
7 A 1 6 B 4 3 F 7 F C A C 1 9 0  0=1 1=2 2=0 3=1 4=1 5=0 6=1 7=2 8=0 9=1 10=2 11=1 12=2 13=0 14=0 15=2
Repeats= 0  seed= 2
F 4 2 D 6 8 7 E F F 9 5 8 3 2 0  0=2 1=2 2=2 3=2 4=2 5=1 6=2 7=3 8=2 9=2 10=2 11=1 12=2 13=1 14=1 15=5
Repeats= 1  seed= 3
8 E 3 B D C 4 1 8 0 5 F 4 2 B 0  0=4 1=3 2=3 3=3 4=4 5=2 6=2 7=3 8=4 9=2 10=2 11=3 12=3 13=2 14=2 15=6
Repeats= 1  seed= 4
9 2 4 C 6 4 C 2 8 0 E 1 C 7 D 1  0=5 1=5 2=5 3=3 4=6 5=2 6=3 7=4 8=5 9=3 10=2 11=3 12=6 13=3 14=3 15=6
Repeats= 1  seed= 5
E 8 5 A D 0 F D F F 2 B 0 6 4 1  0=7 1=6 2=6 3=3 4=7 5=3 6=4 7=4 8=6 9=3 10=3 11=4 12=6 13=5 14=4 15=9
Repeats= 2  seed= 6
6 6 6 1 0 C B C 7 F 7 4 4 4 F 1  0=8 1=8 2=6 3=3 4=10 5=3 6=7 7=6 8=6 9=3 10=3 11=5 12=8 13=5 14=4 15=11
Repeats= 7  seed= 7
1 C 7 7 B 8 8 3 0 0 B E 8 5 6 1  0=10 1=10 2=6 3=4 4=10 5=4 6=8 7=8 8=9 9=3 10=3 11=7 12=9 13=5 14=5 15=11
Repeats= 10  seed= 8
5 E 8 E 7 D B A 7 E 0 9 4 E 3 2  0=11 1=10 2=7 3=5 4=11 5=5 6=8 7=10 8=10 9=4 10=4 11=8 12=9 13=6 14=9 15=11
Repeats= 10  seed= 9
2 4 9 8 C 9 8 5 0 1 C 3 8 F A 2  0=12 1=11 2=9 3=6 4=12 5=6 6=8 7=10 8=13 9=6 10=5 11=8 12=11 13=6 14=9 15=12
Repeats= 10  seed= 10
A A A 3 1 5 C 4 8 1 9 C C D 1 2  0=12 1=14 2=10 3=7 4=13 5=7 6=8 7=10 8=14 9=7 10=8 11=8 12=14 13=7 14=9 15=12
Repeats= 14
BTW, if you want to see what a "real" Random sequence should look like, run the program in PE5, because it doesn't emulate the RANDOM command (as PE6 does) but pulls a "genuine" Random variable from the (Windows) Operating System on the Editor's Host.

Cheers, Alan.
 

micrometal

New Member
As i'm only using 4 bits to drive the LEDs, the random word seems to spend a lot of time generating big numbers larger than $Fh, so my LEDs are often in the OFF state.
I must be misunderstanding something here - all bits of a random number are high and low for fifty percent of the time, surely. Numbers larger than 0xF still have random bits set in the bottom four bits. I wanted to randomly flash three LEDs and I used this code . . .
Code:
    #picaxe    08M2
    #no_data
    
    symbol        L1 = C.1
    symbol        L2 = C.2
    symbol        L3 = C.4
    
    try1:    random    w0
            if bit1 = 1 then goto try2
            toggle    L1
            pause    250
    try2:    if bit2 = 1 then goto try3
            toggle L2
            pause    250
    try3:    if bit3 = 1 then goto try1
            toggle L3
            pause    250
            goto try1
It seems to work perfectly - each of the LEDs is on for around half the time, so what am I missing?
 

AllyCat

Senior Member
Hi,

Yes indeed, most of the answers above have proposed selecting the low 4 bits of the random word (which is not quite truly random). However:
It seems to work perfectly - each of the LEDs is on for around half the time, so what am I missing?
The binary sequence of 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 has each of its three bits set for (exactly) half the time, but in no way can it be called a Random Sequence !

I believe the code that you have posted (always) generates the sequence:
Code:
2 6 7 3 1 5 4 6 7 3 2 0 1 3 7 6 4 0 2 6 7 3 1 5 4 6 7 3 1 5 4 6 7 3 2 0 1 3 7 6 4 0 2 6 2 3 1 5 4 5 7 5 1 0 4 6 7 3 1 5 4 5 7 6 4 0 1 3 7 6 4 0 2 6 2 3 2 .....
which looks "moderately" random, but the sequence 6 7 3 1 5 4 seems to appear rather frequently and as above, it NEVER contains a repeated digit. One of the requirements of a truly "Random" sequence is that every value should be independent of the previous value(s). In many cases such a restriction is not very important, but it can be in creating (for example) a "Fair" Gambling Game.

Cheers, Alan.
 
Last edited:

AllyCat

Senior Member
Hi,
.... Random command to light up 4 LEDS in any combination from $0h to $Fh.
....my LEDs are often in the OFF state.
....now i've stuck myself in a hole lol.
Yes, there is a slight "hole" in the original specification. It's clear that the OP requires all 16 combinations (noting that one is "No LEDs Lit"), but it's not stated how a change from one combination to the next is to be identified. Most times it will be obvious and a "blank" (all OFF) gap is implied, but what if one (or two or more) of the random states is "All Off"? Maybe detecting a "repeated digit" is not important (or even desirable) for the intended application, but it skews the odds for most forms of probability/gaming**. For example, the absurdity of a "Random Coin-Tossing" machine where the result of a throw is never to be a repeat of the previous throw. :)

A solution to Kill Two Birds With One Stone is to emulate the "rolling" of the Randomiser, i.e. a loop which executes the RANDOM command and displays the result for (say) 100 ms, repeating a number of times. That makes the change obvious and solves the issue that RANDOM does NOT generate a random Word, but only one Random BIT (which it adds to the seed value doubled, as explaned in my link in #4). That is why several of the posts above give poor results. The question arises how many "rolls" need to be executed? Obviously at least 4 to guarantee that four new bits are created, but the Word could contain 15 consecutive zeros so 12 rolls would be needed to ensure that at least one LED lights during some part of the "throw". But 8 iterations would be sufficient to ensure that at least one LED is illuminated at some time before, during or after the throw.

** One "missing" pattern in 8 or 16 possibilities may not be obvious and might be considered trivial, but for example a Roulette Wheel has 37 numbers (maybe 38 in America), but the Winning Odds are based on 36 numbers (0 is ignored) which gives the Bank a healthy margin. ;)

The program keeps a running total of repeated nibbles and the number of occurrences of each nibble to check for reasonable "Randomness". This might be quite important because I've noticed some unexpected "patterns" appearing,
An example of how careful one needs to be in designing Random generators: I believe the code that I posted in #9 is sufficiently good, but a previous version used a RANDOM w0 instead of the INC w0 in line 5. Ideally (in the absence of a "Power Fail" interrupt capabilty), one would write every updated value of w0 to the "seed store", but this might soon "wear out" the EEPROM. Therefore I chose to KISS and save the value just once at the start of the program, which does at least ensure that subsequent runs don't begin with the same sequence. This seemed to work well, but the incidence of the value F after some hundreds of iterations appeared too high: :(

To cut a long story short; the READ / WRITE effectively "winds back" the random sequence to an earlier state. Using an INC or a single RANDOM doesn't generally restart at a previously displayed value, but after four "Restarts" the four RANDOMs do restore a previously used value and the sequence repeats. It happens that the first hundred or so "Random" values had a predominance of Fs (which is quite possible in a genuine random sequence), but this was being repeatedly accumulated instead of being "watered down" by other parts of the sequence. However, this issue was revealed by the very artificial situation of frequent (and equally spaced) restarts, so is unlikely to be an issue in practice.

Cheers, Alan.
 

Solar Mike

New Member
If you want a truly random result, then perhaps an external source has to be applied, eg white noise applied to an ADC input pin, using that as the seed value.

Cheers
Mike
 

OLDmarty

Senior Member
Hi All,

To clarify some questions about my "needs"....
I would like the numbers $0h to $Fh to be randomly generated at a speed interval set by an external pot for the speed.
Setting the pot slow, would generate one of the 16 required values at say once per second, while turning the pot to a faster speed would generate the numbers at say 1/10th of a second intervals, in fact, even this speed "value" could also be a random duration, but that's for step 2 of this project ;-)

So, the 16 available (4 bit/nibble) values would be 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, but of course in *any* random order:

At any given point in time, the numbers might be: 7,E,9,A,2,1,F,C,0,4,B,5,D,3,8,F or also could simply be 7,7,7,7,9,A,0,3,3,4,4,B,B,B,B,9 for all i know...

The values will be generated indefinitely until the unit is switched off.

I hope this helps?
 

Buzby

Senior Member
Rich (BB code):
#picaxe 14m2

symbol randW = w0
symbol potval = w1
symbol countr = w2
symbol rrslt  = b6

do ' Main loop
      
      readadc c.4, potval
      countr = potval * 2 + 10 ' Adjust these numbers to set overall speed
      
      do ' Timing loop
            random randW ' Update random word   
            dec countr   ' Time counter
      loop until countr = 0   

      rrslt = randW & $0F
      
      sertxd (#rrslt,",")
            
loop
 
Last edited:

AllyCat

Senior Member
Hi,
I would like the numbers $0h to $Fh to be randomly generated at a speed interval set by an external pot for the speed.
Yes indeed, that is very do-able, probably even with the humblest 08M2 {LE} PICaxe and up to at least 100 times per second. :)

It must be remembered that the RANDOM command (and any FSR based system) is only "Pseudo Random", but it is only necessary to understand its "Limitations", and Work Around them, when they are significant. IMHO the most important is that because it introduces only one random bit**, it should be executed at least four times (for a nibble output), usually on the same Word, but it could use more Words if a higher degree of "Randomness" were required. Easiest is to use the four low bits (with AND or // operators); other bits may give a "different" result/sequence, but I don't believe that will be any more Random (and maybe less).

The next restriction is that it does use only 16 bits, so certain "patterns" cannot occur, and the sequence will eventually repeat (after about 18 hours at one nibble per second). So, "7 7 7 7" and "A A A A" will occur (exactly once every cycle), but "7 7 7 7 7" etc., and uniquely "0 0 0 0" will NOT. If either of these restrictions is an issue then 32 bits could create up to 8 nibble-repetitions (eventually !) and the cycle time rises to about 13 years at 10 nibbles/sec. Because the cycle is exactly 65535 iterations (not 2^16 = 65536), then using 4x (or even 16x) repeated RANDOMs for each iteration will NOT reduce the cycle period, but e.g. 5x RANDOMs will. If you do decide that more than 16 bits are required, then the method of combining the Words can be an interesting further discussion. ;)

Probably not an issue for this application, but I do feel that I should warn (again) that a "fixed" Program (with no active inputs) will always generate the same "Random" sequence when it starts. Thus a simple "MP3 player" with a RANDOM playlist might play exactly the same sequence of Songs each morning after it is switched On. But almost any "Human (or external) Intervention" (including the use of a Pot and/or ADC) will very probably solve the issue.

Buzby's solution is quite ingenious and will probably meet all the requirements*. But to be "nit picking", there are two issues I would suggest. Firstly, I prefer a READADC10 which may introduce just enough (internal) noise to create a further random element, whilst READADC may give a continuously stable (8 bit) value (until the pot is adjusted). Secondly, if we are particularly "unlucky", I believe that if countr takes a value to cause 255 or 257 loops, then the "Random" sequence will enter a repeated loop of only 257 or 255 iterations. Similarly, there are a total of 14 values (including 3 , 5 , 15 , 17 , 51 , 85 , etc.) which will shorten the loop. :(

*EDIT: I don't know if it's by accident or design, but Busby's code, exactly as written, looks almost Spot On. (y) The timing loop appears to execute in around 2 ms, so countr needs to take a value between about 50 and 500. Therefore, multiplying the READADC byte value by 2 should give a range of around 0 to 1022 (ms) and countr will always be EVEN. The added "minimising" value needs to be about 50 (for 100 ms) and provided it (and the loop exit value) are also Even, will avoid the "nasty" sub-division factors (that are all Odd). Personally, I'd like to introduce a little extra random "noise" and/or a NV stored Seed, and am intrigued that a 64-bit version loop shouldn't repeat until a time longer than the age of the Universe! But there's much more merit in a Keep It SimpleS approach. :)

** It could be argued that the RANDOM instruction should execute the FSR algorithm 16 times to generate a completely "new" Word, since it would need only a very "simple" additional loop. But PICaxe Basic needed to be a very "compact" language and the single Bit-update is sufficient, e.g. to emulate a Coin Toss (or just 4 loops for our current nibble requirement). Actually, I'm very pleased that it is so simple, because I have found several "unexpected" and useful applications of the RANDOM command (because it is so "fast"), which I might formally document sometime. :)

Cheers, Alan.
 
Last edited:

Buzby

Senior Member
I don't know if it's by accident or design, but Busby's code, exactly as written, looks almost Spot On. (y)
Definitely not by design, it was just the first two numbers that came to mind. I was only running it the simulator, with no idea how fast a real chip would behave.

Regarding storing a NV seed, this useful with code that has no variability. However, it's highly likely the user in this case will twiddle the pot at each switch on, thus adding a proper randomness.

Although I've not studied the exact details of the generator, I do know that by recording the last 16 values, it's fairly easy to predict the next value. This is why I never use consecutive results, and always call 'random' an odd multiple of times.

However, in most applications there is usually some randomness, such as a pot, pushbutton or keyboard, so just repeating 'random' while waiting for an event is good enough to scramble it beyond predictability.
 

AllyCat

Senior Member
Hi,
..... always call 'random' an odd multiple of times.
Actually it's Odd numbers that (might) need to be avoided, because all the Factors of 65535 (iterations per cycle) are Odd (14 of them). But of course two odds make an Even so it won't often be an issue.

Probably not relevant to this particular thread, but I've recently updated my RANDOM Function ... thread in the Code Snippets section of the Forum, and linked above.

Cheers, Alan.
 
Top