Creating a Random Number

Former Member
Former Member
I was wondering if there is a way to get a random number between two numbers. I see that there is a Math.rand(), but this just grabs a very random number. I am trying to get a random number to draw a bitmap at random points on the screen.

Thanks for any and all the help.

Top Replies

All Replies

  • Try this:

    using Toybox.Math as Mt;
    using Toybox.System as Sys;

    .......
    ..
    var r;
    r = Mt.rand() % 900 + 100; //Random number between 100 and 1000 (900+100)
    Sys.println(r); //To check the result
  • I'm not sure of the implementation of Math.rand() provided with the ConnectIQ SDK, but if it is anything like most other languages the above will work, but it may produce biased values (poor randomness). You can read about this with the C language here. If you want to reduce this, you can write something like...

    const RAND_MAX = 0x7FFFFFF;

    // return a random value on the range [n, m]
    function random(n, m) {
    return m + Math.rand() / (RAND_MAX / (n - m + 1) + 1);
    }


    Travis
  • I use
    Math.srand(Sys.getTimer());
    to randomize initial seed number generator values.

    r = Mt.rand() % 900 + 100; //Random number between 100 and 1000 (900+100)

    From 100 to 999, as x % 900 never gets 900.
  • In other languages I believe it is a random number from 0 to 1.

    Could someone explain the return range of numbers from Math.rand() ?

  • In other languages I believe it is a random number from 0 to 1.

    Could someone explain the return range of numbers from Math.rand() ?

  • Like Travis implied above, the range is from 0 to (2^31)-1 (0x7FFFFFFF or 2147483647), and you can use something like provided function to get a number between n and m. (I think you may have to need to add a cast to float somewhere in that expression)

    As per the docs:

    [https://developer.garmin.com/connect-iq/api-docs/Toybox/Math.html#rand-instance_function]

    rand()Toybox.Lang.Number

    Returns a pseudo-random Number. Use the srand() function to seed the random number generator.

    Returns:


    It's def not well-documented -- you sort of have to infer that by "non-negative" and "Toybox.Lang.Number", it means that the range is the entire set of non-negative Numbers. (A Number is a signed 32-bit integer). That's the most reasonable interpretation of the docs, anyway, I guess.

    I guess it also helps to know that GCC/Linux uses the same range (although other implementations range from 0 to 0x7FFF.)

    It's hard to know absolutely for sure, though. (Although I'm reasonably certain what Travis wrote is correct.)

    Might want to post in the bug reports section to ask for the docs to be clarified.

    EDIT: Now that Travis works for Garmin, maybe he can confirm this?

  • I just created this in my barrel that has a bunch of helper functions. Seems to work. But if I set lo/hi to a small range, like say: 5 to 8... at least in the simulator, it seems to camp on a value like "7" for many iterations. Is this a simulator bug or is my implementation bad? I looked at your code example, but I don't get how adding a value to "m" (max) works, or why the RAND_MAX helps.

  • Try doing the srand only once (when the app first starts)  and just do the rand with you want a new random number

  • TL;DR you should only call srand() once and both naive implementations (modulus by max - min + 1 or float division by RAND_MAX) are biased. The best solutions involve calling rand() multiple times: stackoverflow.com/.../11758872

    --

    As Jim said, you should only call srand() once, but there's a deeper problem here.

    For an explanation of why your implementation (Math.rand() % (hi-lo+1) + lo) is biased, see:

    https://stackoverflow.com/questions/11758809/what-is-the-optimal-algorithm-for-generating-an-unbiased-random-integer-within-a

    The top answer: stackoverflow.com/.../11758872

    The problem is that you're doing a modulo operation. This would be no problem if RAND_MAX would be evenly divisible by your modulus, but usually that is not the case. As a very contrived example, assume RAND_MAX to be 11 and your modulus to be 3. You'll get the following possible random numbers and the following resulting remainders:

    0 1 2 3 4 5 6 7 8 9 10
    0 1 2 0 1 2 0 1 2 0 1
    

    As you can see, 0 and 1 are slightly more probable than 2.

    Here's one source for an answer similar to Travis's. Note the caveat about large ranges and 32768 integers doesn't apply to monkey C, since Monkey C's Math.rand() returns a 32-bit number. It's only marginally better than the original algorithm (see below).

    https://stackoverflow.com/a/5009307

    Here's a source for the same answer as Travis's, but with M as the min and N as the max:

    c-faq.com/.../randrange.html

    To address your questions:

    I don't get how adding a value to "m" (max) works

    That might be a typo (Travis's answer actually applies to a range between M and N, not N and M as stated). See here:

    stackoverflow.com/.../2254517

    c-faq.com/.../randrange.html

    why the RAND_MAX helps

    Dividing by RAND_MAX scales the result of rand() to a number between 0 and 1 (inclusive).

    As others pointed out, even this option is not uniformly random:

    Using division gives only a minute improvement by itself -- the results may be more random, but they're still usually skewed. Assuming rand() produces all numbers up to RAND_MAX with equal probability, this will NOT produce numbers from 1 to 12 with equal probability, except in the (vanishingly rare) case that RAND_MAX happens to be a multiple of 12.

    As many people pointed out, the best solution is to call rand() multiple times:

    c-faq.com/.../randrange.html

    When N is close to RAND_MAX, and if the range of the random number generator is not a multiple of N (i.e. if (RAND_MAX+1) % N != 0), all of these methods break down: some outputs occur more often than others. (Using floating point does not help; the problem is that rand returns RAND_MAX+1 distinct values, which cannot always be evenly divvied up into N buckets.) If this is a problem, about the only thing you can do is to call rand multiple times, discarding certain values:

    ...

    https://stackoverflow.com/a/11758872

     

    Sorry for posting images of code, but the forum rejected my post multiple times when I posted code as text Rage. I also hate the way I have to manually edit image dimensions to avoid tiny inline images. The UX here is just awful.

  • I can't seem to reply to - but thanks to Jim and FlowState for a much better implementation.