given a Math.random()
function which returns a number between [0,1) and min
max
values to specify the range, how can we generate numbers for the following cases:
Case we want integer:
A: (min,max) ?
B: [min,max) return Math.floor(Math.random() * (max - min)) + min;
C: (min,max] ?
D: [min,max] return Math.floor(Math.random() * (max - min + 1)) + min;
Case we want float:
A: (min,max) ?
B: [min,max) return Math.random() * (max - min) + min;
C: (min,max] ?
D: [min,max] ?
Integers
Your formula for B. is correct, everything else is obtained by trivial +1
-1
corrections:
- A.
(min, max) = [min + 1, max)
, therefore from B. we obtain
min + 1 + Math.floor(Math.random() * (max - min - 1))
- B.
min + Math.floor(Math.random() * (max - min))
- C. Since in the interval arithmetic
(min, max] = max - [0, max - min)
, one could also write max - Math.floor(Math.random() * (max - min))
- D.
[min, max] = [min, max + 1)
, therefore: min + Math.floor(Math.random() * (max + 1 - min))
Floats. As V13 already pointed out, the question is somewhat ill-posed: if we consider single points as measure-zero sets, there is almost (in measure-theoretical sense) no difference between the four sets... However, if you want to guarantee that the excluded interval boundaries are never (not merely "almost never") sampled, and if you assume that there are no rounding errors, you could do something like this:
A: var middle = (min + max) / 2; var sign = Math.random() > 0.5 ? 1 : -1; return middle + sign * (max - min) / 2 * Math.random();
This solution puts a tiny little bit more mass on 0
, but this should be negligible for all practical purposes.
B: min + Math.random() * (max - min)
, yes.
- C:
max - Math.random() * (max - min)
, symmetric to the above.
- D: It is not possible to guarantee that we ever hit the upper interval boundary, so we can just use
min + Math.random() * (max - min)
.
The difference between A and D is the following: if we tried to use the formula min + Math.random() * (max - min)
in A, we might occasionally get a 0
(because the range of possible numbers is actually finite). However, no reasonable statistic could ever complain that the upper bound is not hit in D.
Integer:
- A:
return Math.floor(Math.random() * (max - min - 1)) + min + 1;
- B: Correct
- C: This is the same as [min+1, max+1), so:
return Math.floor(Math.random() * (max - min)) + min + 1;
- D: Correct
For floats, you need to know what kind of floating point arithmetic you have. Unless you use a special library, equality in float is something that usually doesn't happen, so it makes no real sense to have closed ranges. As such there should be no difference between all four and you can just go with:
return Math.random() * (max-min) + min;
For the question to make sense, you'll need to define a minimum acceptable range for equality (E.g. r=0.00000000000000001
). After that you can transform your open range equations (i.e. (min, max)
) to [min+r, max-r]
.
I'll start by defining the next two helper functions and then use them to get the values. These methods are your definitions of the B cases.
int nextInt(int min, int max) {
return Math.floor(Math.random() * (max - min)) + min;
}
float nextFloat(float min, float max) {
return Math.random() * (max - min) + min;
}
Then for integers
- A: return nextInt(min+1, max);
- B: return nextInt(min, max);
- C: return nextInt(min+1, max+1);
- D: return nextInt(min, max+1);
Floats are a bit more complex case. Some might argue that there isn't much difference whether the end points are included or not - especially an open ended solution could be used instead of closed - since the end points get rarely selected. But since it is perfectly possible to implement all the scenarios, I think there is mathematical interest in how it can be done.
A: In this case we can simple make sure that the illegal value is rolled again:
float f;
do {
f = nextFloat(min, max);
} while (f == min);
return f;
B:
return nextFloat(min, max);
C: Here we just switch the endpoint
float f = nextFloat(min, max);
if (f == min) {
return max;
}
return f;
D: This is the most complex scenario of all, but can be achieved as follows:
float f = nextFloat(min, max);
if (f == min) {
return max;
}
return nextFloat(min, max);
The cases A and D are a bit dirty in the sense that they may require generating more than one random number, which may be an issue in some specific scenarios. Solving this would require digging deep into the specification of the floating point to find alternative implementations. Further it should be noted that in case D the propability of max-value has very slightly higher propability than any other number, if the referenced function is completely uniform (usually not), but usually this is only a theoretical matter. (To be precise, if there are n possible values within the range, the propability of max value pmax = 1/(n-1) and the propability of any other value is (1-pmax)/(n-1)).
A little problem that should be taken care of in precise implementations of floating point case A, should be noticed. There is a possibility that the caller of the function will call it with adjacent floating points. This is not easy to see by any dummy check of the parameters, so to be safe there should be a limit on how many times the loop may be executed.