Shop OBEX P1 Docs P2 Docs Learn Events
How do you generate a random (even psudo-random) number from say -20 to 20? — Parallax Forums

How do you generate a random (even psudo-random) number from say -20 to 20?

photomankcphotomankc Posts: 943
edited 2012-03-14 22:07 in Propeller 1
I find the ? operator but that says it's any number that can be represented by a signed 32 bit number. I need to add a little random to some things in the range of maybe -20 to +20 just not sure how I go about that.

Comments

  • RiJoRiRiJoRi Posts: 157
    edited 2012-03-14 13:42
    photomankc wrote: »
    I find the ? operator but that says it's any number that can be represented by a signed 32 bit number. I need to add a little random to some things in the range of maybe -20 to +20 just not sure how I go about that.

    I'd first get the 32-bit number, then bitwise AND it with 64 (the nearest power of 2 which is greater than 40). I'd then subtract 24 (64-40) to get a value of 0..40. Finally, I'd subtract 20 to get a range of -20..20.

    Of course, you could combine the two subtractions (subtracting 44) but I did it this way to make the explanation easier.

    Note that if I was doing this, I'd run tests with positive and negative numbers, and test the limit values before I tried it with the random number generator. I'd also seed the variable I was using with something like a timer to get a kind-of-random number.

    --Rich
  • pedwardpedward Posts: 1,642
    edited 2012-03-14 13:53
    The modulus operator is the conventional way to get a range constraint.
    { call with 40, -20 to get from -20 to +20, you can also do 40, -10 to get from -10 to +30 }
    PUB getRandom(range, offset)
      ?result  'get a random number
      result := result // range + offset  'get from 0-to-range then add offset, result is a magic variable in SPIN that is returned as the output of a function
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-03-14 13:54
    RiJoRi wrote: »
    I'd first get the 32-bit number, then bitwise AND it with 64 (the nearest power of 2 which is greater than 40). I'd then subtract 24 (64-40) to get a value of 0..40. Finally, I'd subtract 20 to get a range of -20..20.

    Of course, you could combine the two subtractions (subtracting 44) but I did it this way to make the explanation easier.

    Note that if I was doing this, I'd run tests with positive and negative numbers, and test the limit values before I tried it with the random number generator. I'd also seed the variable I was using with something like a timer to get a kind-of-random number.

    --Rich

    When you subtract 24, you'll get a number between -24 and 40.

    If you make any negative number zero then you're skewing the results. You either need to just resample until you get a number in the correct range 0 and 40 or use some more complicated algorithm.

    Edit: After seeing pedward's post it looks like the algorithm isn't very complicated after all.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-03-14 13:58
    pedward,

    No, no, no. The ? operator requires a persistent VAR or DAT variable to use as a seed for the next evaluation. You can't use result, since it starts at 0 every time.

    Do it this way:
    VAR
    
      long seed
    
    PUB random(first, last)
    
      return first + ||?seed // (last - first + 1)
    

    BTW, the modulus operator introduces a slight bias for ranges that are not a power of two. But for small numbers, it's safe to ignore this.

    -Phil
  • JonnyMacJonnyMac Posts: 9,197
    edited 2012-03-14 13:59
    I would tend to want to remove negative values before using modulus.
    randval := ||(?seed) // 41 - 20
    


    An easy way to remember the output from modulus is that it will be between 0 and the divisor minus one, hence 41 in the code gives an output between 0 and 40. Finally, subtracting 20 takes care of the offset that you desire. Per Phil's note, 'seed' is a persistent (global) long variable.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-03-14 14:05
    Jon,

    You're too fast for me! I caught the || before I saw your post, but your 41 clued me that I needed the +1.

    -Phil
  • pedwardpedward Posts: 1,642
    edited 2012-03-14 14:22
    Ahh yes, in my haste to make a subroutine I violated the persistent variable clause. If SPIN had static variables, I'd use that to avoid cluttering the global scope. The point about negative is very well taken.

    See, just goes to show that simple questions sometimes have complex answers, especially when dealing in SPIN.

    The one thing that kinda bugs me about SPIN is the insistence that every variable be a signed long. I tire of that because PASM treats everything as unsigned unless you specifically want signed, the way it should be!

    The traditional random number generators generate a number between 0 and 1, then you just scale it. The issue of negative or positive is obviously because the sign bit is part of the random data in the LFSR, which effectively halves the number space because you throw the sign away to get a number that is predictably positive in the signed space. So instead of 32 bits you have 31 bits.
  • photomankcphotomankc Posts: 943
    edited 2012-03-14 22:07
    pedward wrote: »
    The modulus operator is the conventional way to get a range constraint.
    { call with 40, -20 to get from -20 to +20, you can also do 40, -10 to get from -10 to +30 }
    PUB getRandom(range, offset)
      ?result  'get a random number
      result := result // range + offset  'get from 0-to-range then add offset, result is a magic variable in SPIN that is returned as the output of a function
    

    Thank you, very cool. I appreciate the example.
Sign In or Register to comment.