Generate a random number with a non-uniform distribution - javascript

I have a formula that generates a random integer in a given range [x,y].
rand = Math.floor(x + Math.random()*(y-x+1));
And I would like the generated integer to have a higher chance of being close to the midrange.
Here is an interesting approach.
I am trying to adapt that solution to my problem (numbers skewed towards the midrange, not the extremities), but I am struggling with the formula.
beta = Math.sin(Math.random()*Math.PI)^2;
rand = Math.floor(x + beta*(y-x+1));
I do not understand how beta works. According to this graph wouldn't the numbers generated have a higher chance of being closer to 0.5? beta always returns the same number. Didn't I implement Math.random() properly? I swear javascript is messing with me right now.

You probably need a
beta = 4 * (rand - 0.5)^3 + 0.5
function or something with a similar shape
Graph
Distribution results: http://jsfiddle.net/4hBqz/

Related

Decimal rounding in JavaScript

I am trying to achieve what was asked in this question. In most scenarios, it is working fine by using the following code:
Math.round(num * 100) / 100
But I encountered a situation where it fails due to a very odd behavior. The number I'm trying to round is 1.275. I am expecting this to be rounded to 1.28, but it is being rounded to 1.27. The reason behind this is the fact that num * 100 is resulting in 127.49999999999999 instead of 127.5.
Why is this happening and is there a way around it?
EDIT
Yes, this question is probably the root cause of the issue I'm facing, but the suggested solutions of the selected answer are not working for me. The desired end result is a rounded number that is displayed correctly. So basically I'm trying to achieve what that question is instructing (check below) but I cannot figure out how.
If you just don’t want to see all those extra decimal places: simply
format your result rounded to a fixed number of decimal places when
displaying it.
You can use toFixed to ensure the precision that you're looking for:
var x = 1.275;
var y = x * 100;
y = y.toFixed(1); // y = 127.5

Generate random numbers with logarithmic distribution and custom slope

Im trying to generate random integers with logarithmic distribution. I use the following formula:
idx = Math.floor(Math.log((Math.random() * Math.pow(2.0, max)) + 1.0) / Math.log(2.0));
This works well and produces sequence like this for 1000 iterations (each number represents how many times that index was generated):
[525, 261, 119, 45, 29, 13, 5, 1, 1, 1]
Fiddle
I am now trying to adjust the slope of this distribution so it doesn't drop as quickly and produces something like:
[150, 120, 100, 80, 60, ...]
Blindly playing with coefficients didn't give me what I wanted. Any ideas how to achieve it?
You mention a logarithmic distribution, but it looks like your code is designed to generate a truncated geometric distribution instead, although it is flawed. There is more than one distribution called a logarithmic distribution and none of them are that common. Please clarify if you really do mean one of them.
You compute floor[log_2 U] where U is uniformly distributed from 1 to (2^max)+1. This has a 1/2^max chance to produce max, but you clamp that to max-1. So, you have a 1/2^max chance to produce 0, 2/2^max chance to produce 1, 4/2^max chance to produce 2, ... up to a 1/2 + 1/2^max chance to produce max-1.
Present in your code, but missing from the description in the question, is that you are flipping the computed index around with
idx = (max-idx) - 1
After this, your chance to produce 0 is 1/2 + 1/2^max, and your chance to produce a value of k is 1/2^(k+1).
I think it is a mistake to let U be uniform on [1,2^max+1]. Instead, I think you want U to be uniform on [1,2^max]. Then your chance to generate idx=k is 2^(max-k-1)/((2^max)-1).
idx = Math.floor(Math.log((Math.random()*(Math.pow(2.0, max)-1.0)) + 1.0) / Math.log(2.0));
zmii's comment that you could get a flatter distribution by replacing both 2.0s with a value closer to 1.0 is good. The reason it produced unsatisfactory results for small values of max was that you were sampling uniformly from [1,1.3^max+1] instead of [1,1.3^max]. The extra +1 made a larger difference when max was smaller and the base was smaller. Try the following:
var zmii = 1.3;
idx = Math.floor(Math.log((Math.random()*(Math.pow(zmii, max)-1.0))+1.0) / Math.log(zmii));

Custom linear congruential generator in JavaScript

I am trying to create a custom linear congruential generator (LCQ) in JavaScript (the one used in glibc).
Its properties as it's stated on Wikipedia are: m=2^31 , a=1103515245 , c=12345.
Now I am getting next seed value with
x = (1103515245 * x + 12345) % 0x80000000 ; // (The same as &0x7fffffff)
Although the generator seems to work, but when the numbers are tested on canvas:
cx = (x & 0x3fffffff) % canvasWidth; // Coordinate x (the same for cy)
They seem to be horribly biased: http://jsfiddle.net/7VmR9/3/show/
Why does this happen? By choosing a different modulo, the result of a visual test looks much better.
The testing JSFiddle is here: http://jsfiddle.net/7VmR9/3/
Update
At last I fixed the transformation to canvas coordinates as in this formula:
var cx = ((x & 0x3fffffff)/0x3fffffff*canvasWidth)|0
Now the pixel coordinates are not so much malformed as when used the modulo operation.
Updated fiddle: http://jsfiddle.net/7VmR9/14/
For the generator the formula is (you forgot a modulus in the first part):
current = (multiplier * current * modul + addend) % modulus) / modulus
I realize that you try to optimize it so I updated the fiddle with this so you can use it as a basis for the optimizations:
http://jsfiddle.net/AbdiasSoftware/7VmR9/12/
Yes, it looks like you solved it. I've done the same thing.
A linear congruential generator is in the form:
seed = (seed * factor + offset) % range;
But, most importantly, when obtaining an actual random number from it, the following does not work:
random = seed % random_maximum;
This won't work because the second modulus seems to counteract the effect of the generator. Instead, you need to use:
random = floor (seed / range * random_maximum);
(This would be a random integer; remove the floor call to obtain a random float.)
Lastly, I will warn you: In JavaScript, when working with numbers that exceed the dword limit, there is a loss of precision. Thus, the random results of your LCG may be random, but they most likely won't match the results of the same LCG implemented in C++ or another low-level language that actually supports dword math.
Also due to imprecision, the cycle of the LCG is highly liable to be greatly reduced. So, for instance, the cycle of the glibc LCG you reference is probably 4 billion (that is, it will generate over 4 billion random numbers before starting over and re-generating the exact same set of numbers). This JavaScript implementation may only get 1 billion, or perhaps far less, due to the fact that when multiplying by the factor, the number surpasses 4 billion, and loses precision in doing so.

JavaScript Random Number [duplicate]

This question already has answers here:
Generating random whole numbers in JavaScript in a specific range
(39 answers)
Closed 9 years ago.
This maybe more mathematical question than programming. In JS I wanted to a function that returns a random integer number in an interval lets say 1-6 and this is what I found:
// Returns a random integer between min and max
// Using Math.round() will give you a non-uniform distribution!
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
I feel guilty if I copy and paste this in my code. I don't understand this :
Why we subtract min from max, add 1, multiply the answer by Math.random() and then add the min. I tired with several numbers manually on paper and it work just fine ! But I don't understand why !
Assuming you already understand the behaviour of Math.floor and Math.random, here's the rest step by step:
Math.random() ↝ a random number between 0 (inclusive) and 1 (exclusive)
Math.random() * max ↝ a random number between 0 (inclusive) and max (exclusive)
Math.floor(Math.random() * max) ↝ a random integer between 0 (incl.) and max (excl.)
Math.floor(Math.random() * (max - min)) + min ↝ a random integer between min (incl.) and max (excl.)
Math.floor(Math.random() * ((max + 1) - min)) + min ↝ a random integer between min (incl.) and max+1 (excl.) (OR between min and max both inclusive)
Math.random() will give you a "real" number from 0 to 1 (not including 1.0).
That's cool and all, but what if I want a "real" number from 1 to 2?
The answer: "transform" your [0,1) into [1,2).
In practical terms, it means adding 1 to your result.
Try it out -- Math.random()+1 will give you a number from 1 to 2.
In mathematics this is known as a "mapping". That is -- for every possible real number in [0,1), find a way to "map" that real number to another real number in [1,2). That is, if I give you any real number between [0,1), you should be able to map that number -- apply that number to a function that will return a number between [1,2).
In our case, that function f(x) = x+1.
Do you see how this gives us random numbers between [1,2)? Visualize the two intervals next to each other and imagine a line going from every point in [0,1) to its corresponding map in [1,2). Now, pick a random point on [0,1) ... and follow the line. You'll follow the line to a random point in [1,2)!
Now, all complete one-to-one maps from [0,1) to [1,2) will turn a random number between [0,1) to a random number between [1,2)...but not all of them will give you an evenly distributed random number between [1,2). The mathematics behind what maps give you evenly distributed results is a bit complicated but in short, if your map only involves adding, subtracting, multiplying, and dividing by constants, it's "legal" in the sense that the results will also be evenly distributed.
So, now we know how to transform [0,1) into [1,2).
What if I want to map [0,1) onto [0,2)? I can't just add numbers anymore ...
How about I multiply everything by two?
This should work -- the function f(x) = x*2 does indeed map every point on [0,1) to a point on [0,2) --- and because it only involves multiplication by constants (2), it is a distribution-preserving map.
This works! Math.random()*2 will give you a random number between 0 and 2.
Okay, now something a bit more complicated ... transforming [0,1) into [1,3).
Multiplying by two doesn't work ... 0*2 = 0, and that's not in your target range.
Adding one doesn't work... even though 0+1 is in your target range and 1+1 is, as well, there is no way you can ever reach 3.
If we can't transform [0,1) into [1,3), let's try and see if we can transform something else into [1,3).
How about [0,2)? Yes, we can do this ... the function f(x) = x+1 perfectly maps [0,2) to [1,3). You can think of + as "shifting" the range up.
And so the solution here is clear -- first, turn [0,1) into [0,2), then turn [0,2) into [1,3).
We already know the first (f(x) = x*2), and we figured out the second (f(x) = x+1). So the "combined" transformation/map is f(x) = (x*2)+1.
That is, Math.random()*2 + 1 will give you a number from 0 to 3.
Now for the final trick...mapping [0,1) to an arbitrary range [min,max).
The secret here is to re-write this as [min,min+range), where range = max-min.
Here you can see that it's simple to transform the range [0,range) to [min,min+range) -- you just add "min" to it. So if I had the range [0,range), and I wanted to get [min,min+range), i would use f(x) = x+min.
So how do we get from [0,1) to [0,range) ?
Multiply by range!
f(x) = (x*range) + min
Now writing things back to original terms, using range = max-min
f(x) = (x*(max-min)) + min
will transform a real number from [0,1) to a real number from [min,max)
I'll leave the rest (turning it into a useful integer) to you
0 <= Math.random() < 1 =>
0 <= Math.random() * 6 < 6 =>
0 <= Math.floor( Math.random() * 6 ) <= 5
then you add 'min' so it would look like this:
min <= Math.floor( Math.random() * 6 ) <= 5 + min
in your exemple, for min = 1 you will have all the numbers in 1-6.
I hope now is much clear.
Here's an explanation of your code:
Math.random() generates a random number between 0 and 1 (not including 1).
You need to scale that value based on the range of numbers you want. Your range is how far from your min desired number to your max desired number which is max - min.
If you want to include the max value in the range of numbers generated, then use max - min + 1
You then need to make sure the random number starts at the right base rather than 0 so you add min to it.
Then, if you want it to be an integer you call Math.floor() to truncate it to the next lowest integer.
So, if you just had this:
Math.floor(Math.random())
You would always get zero. Because Math.floor() of a float value between 0 and 1 (not including one) will always truncate down to 0.
Then, if you expand the range with:
Math.floor(Math.random() * (max - min + 1))
You would now get a random number between 0 and max - min including the larger value.
So, to then get it to start at the right base, you add in min like this:
Math.floor(Math.random() * (max - min + 1)) + min

Influence Math.random()

I'm looking for a way to influence Math.random().
I have this function to generate a number from min to max:
var rand = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Is there a way to make it more likely to get a low and high number than a number in the middle?
For example; rand(0, 10) would return more of 0,1,9,10 than the rest.
Is there a way to make it more likely to get a low and high number than a number in the middle?
Yes. You want to change the distribution of the numbers generated.
http://en.wikipedia.org/wiki/Random_number_generation#Generation_from_a_probability_distribution
One simple solution would be to generate an array with say, 100 elements.
In those 100 elements represent the numbers you are interested in more frequently.
As a simple example, say you wanted number 1 and 10 to show up more frequently, you could overrepresent it in the array. ie. have number one in the array 20 times, number 10 in the array 20 times, and the rest of the numbers in there distributed evenly. Then use a random number between 0-100 as the array index. This will increase your probability of getting a 1 or a 10 versus the other numbers.
You need a distribution map. Mapping from random output [0,1] to your desired distribution outcome. like [0,.3] will yield 0, [.3,.5] will yield 1, and so on.
Sure. It's not entirely clear whether you want a smooth rolloff so (for example) 2 and 8 are returned more often than 5 or 6, but the general idea works either way.
The typical way to do this is to generate a larger range of numbers than you'll output. For example, lets start with 5 as the base line occurring with frequency N. Let's assume that you want 4 or 7 to occur at frequency 2N, 3 or 8 at frequency 3N, 2 or 9 and frequency 4N and 0 or 10 at frequency 5N.
Adding those up, we need values from 1 to 29 (or 0 to 28, or whatever) from the generator. Any of the first 5 gives an output of 0. Any of the next 4 gives and output of 1. Any of the next 3 gives an output of 2, and so on.
Of course, this doesn't change the values returned by the original generator -- it just lets us write a generator of our own that produces numbers following the distribution we've chosen.
Not really. There is a sequence of numbers that are generated based off the seed. Your random numbers come from the sequence. When you call random, you are grabbing the next element of the sequence.
Can you influence the output of Math.random in javascript (which runs client side)?
No. At least not in any feasible/practical manner.
But what you could do is to create your own random number generator that produces number in the distribution that you need.
There are probably an infinite number of ways of doing it, and you might want to think about the exact shape/curvature of the probability function.
It can be probably be done in one line, but here is a multi-line approach that uses your existing function definition (named rand, here):
var dd = rand(1,5) + rand(0,5);
var result;
if (dd > 5)
result = dd - 5;
else result = 6 - dd;
One basic result is that if U is a random variable with uniform distribution and F is the cumulative distribution you want to sample from, then Y = G(X) where G is the inverse of F has F as its cumulative distribution. This might not necessarily be the most efficient way of doing and generating random numbers from all sort of distributions is a research subfield in and of itself. But for a simple transformation it might just do the trick. Like in your case, F(x) could be 4*(x-.5)^3+.5, it seems to satisfy all constraints and is easy to invert and use as a transformation of the basic random number generator.

Categories

Resources