I don't get how adding a value to "m" (max) works
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…
Try doing the srand only once (when the app first starts) and just do the rand with you want a new random number
And calling srand each time doesn't hurt, right?
Did you read the link I posted about why calling srand() multiple times is a bad idea? Suppose you use Time.Now.value() (number of…
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);
}
Math.srand(Sys.getTimer());
r = Mt.rand() % 900 + 100; //Random number between 100 and 1000 (900+100)
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]
Returns a pseudo-random Number. Use the srand() function to seed the random number generator.
Returns:
Non-negative random number
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:
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, assumeRAND_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:
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:
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:
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 . 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 FlowState - but thanks to Jim and FlowState for a much better implementation.