Open Jamesernator opened 8 years ago
Javascript needs to improve its random number support. I was going to propose Math.randomGaussian()
(return a normally distributed random number with mean 0 and standard deviation of 1). But I'd held back because I was queasy about having it hanging off Math
.
I'm even more queasy about adding random integer generation to Math
. However your algorithm never returns max (since Math.random()
always returns a number LESS than 1). That makes me reconsider. (Perhaps Math.random.uniformInteger()
, Math.random.gaussian()
etc...?)
I also I think you should remove the RangeError and sort the numbers. Something like this:
(Almost certainly I've made some silly mistake, too.)
I've written the above as inclusive because that's what I expect when I see min-max; anything else would be counterintuitive to me.
However, I expect the one argument form to be exclusive; I expect randomInteger(N)
to return an uniform integer in the range [0,N). One of my main uses cases for random integers is picking from lists, e.g. array[ randomInteger( array.length ) ]
, an inclusive range from would be a handicap in that case.
So maybe the half-open range you wrote is right. Or maybe it's a foot-gun. (Where are the smart people?)
Another justification for supporting random integers is that PRNGs work in terms of integers and I presume recovering it from a float is less efficient than transforming an integer to an integer.
Mixed behaviour would be the most un-intuitive, e.g.
array[Math.randomInteger(array.length)]
would correctly select a random element of the array but
array[Math.randomInteger(array.length/2, array.length)]
could give array[array.length]
which is bad
Also I was thinking about the probabilities (I know Javascript won't have any guarantees but its roughly true) for choosing a random number from a range e.g. if I use Math.randomInteger(N) with exclusive then each number has a 1/N chance of being selected, similarly Math.randomInteger(A, B) will give each number a 1/(B-A) chance of being selected which seems more intuitive e.g. take for example a coin flip
if (Math.randomInteger(2)) {
~50% chance
} ...
That could be solved by making the default value one instead of zero but then you'd need to do
if (Math.randomInteger(2) === 1) {
~50% chance
} ...
Python has both randint and randrange where randint(a, b) is just randrange(a, b+1) so adding two functions is another possible solution.
I do like the idea of having the functions hang off Math.random so we could have:
Math.random.range
Math.random.integer
// ...etc if other's are deemed useful enough
If this gets accepted please use the name randomInt
to be consistent with parseInt
, Int8Array
and other similar names.
@fuchsia I have asked es-discuss if they think Array.prototype.random would be a useful addition to the language as an easy way to pick from a list. So far the responses have been:
I believe it's too specialized to be a part of Array interface.
I feel in this particular case, a more general solution is much more useful than just a "pick some random item in an array".
I agree that having a random integer function would be more suitable. Picking a random element from an array is a fairly uncommon thing...
What are some other uses for a random integer?
What about something like:
Math.randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1) + min)
//Or
Math.randomInt = (min, max) => (Math.random() * (max - min + 1) + min) | 0;
Math.randomFloat = (min, max) => Math.random() * (max - min + 1) + min;
That way, it allows coders to easily generate random floats or integers while being able to specify the minimum and maximum number it can output.
Or maybe something closer to how Python, Matlab and Processing (Java) did with a function definition as such:
Math.randomInt = (max, min = 0) => ...
For functions that take two arguments, which together specify a half-open interval, I have found it useful to name the parameters inclusive
and exclusive
without implication that one is less than or greater than the other. This makes these functions easier to predict and makes it easier for calling code to avoid off-by-one errors. For example
Math.randomInt = function (inclusive, exclusive) {
// This implementation is only an example and not a suggested specification
var delta = exclusive - inclusive;
if (delta && Number.isInteger(inclusive) && Number.isInteger(exclusive)) {
return inclusive + Math.trunc(delta * Math.random());
}
throw new RangeError("Invalid arguments (" + inclusive + ", " + exclusive + ")");
};
Which enables (in pseudo-code)
0 <= Math.randomInt( 0, length ) < length; // the primary use case
-128 <= Math.randomInt( -128, +128 ) < +128; // a signed octet
+180 >= Math.randomInt( +180, -180 ) > -180; // a longitude in integer degrees
100 >= Math.randomInt( 100, 0 ) > 0; // a non-zero integer percent
13 >= Math.randomInt( 13, 0 ) > 0; // "pick a card, any card..."
10 >= Math.randomInt( 10, 0 ) > 0; // "think of a number between 1 and 10"
6 >= Math.randomInt( 6, 0 ) > 0; // "roll one 6-sided die"
This approach complements
Math.inInterval = function (number, inclusive, exclusive) {
if (inclusive < exclusive) {
return inclusive <= number && number < exclusive;
}
return exclusive < number && number <= inclusive;
};
Which ensures the aesthetically pleasing identity
Math.inInterval( Math.randomInt( inclusive, exclusive ), inclusive, exclusive ) === true
Generating random integers is a rather common procedure so it would be nice to have given that most people have probably used the suggested function from MDN.
Obviously this wouldn't be suitable for generating cryptographic-ally secure random integers as its limited by Math.random but it would be useful for non-secure use cases.
The proposed procedure would be as follows (based of the MDN code):
Math.randomInteger(max)
Math.randomInteger(min, max)
Math.MIN_SAFE_INTEGER
toMath.MAX_SAFE_INTEGER
throw a RangeErrormax
<=min
then throw a RangeErrorrandomInteger
beMath.floor(Math.random() * (max - min)) + min
randomInteger
Questions:
Math.MIN_SAFE_INTEGER
toMath.MAX_SAFE_INTEGER
be allowed?