Say for example I am given the number 3
. I then have to choose a random number from 0 to 3, but where 0 has a bigger chance of being chosen than 1, 1 has a bigger chance of being chosen than 2, and 2 has a bigger chance of being chosen than 3.
I already know that a percentage chance of choosing a specific number from 0 to 3 can kind of be achieved by doing the following:
double r = Math.random();
int n = 0;
if (r < 0.5) {
n = 0;
// 50% chance of being 0
} else if (r < 0.8) {
n = 1;
// 30% chance of being 1
} else if (r < 0.95) {
n = 2;
// 15% chance of being 2
} else {
n = 3;
// 5% chance of being 3
}
The problem is that the 3
can be anything. How can I do this?
Note: The numbers 0.5, 0.8 and 0.95 were arbitrarily chosen by me. I would expect those numbers to decrease so that the sum of all of them equals 1, and so that none of them are the same, if that is possible in some way.
This seems like you would want to work with a generic probability distribution whose domain can be scaled to your liking. You could chose any function such that f(0) = 0
and f(1) = 1
. For this example I will take f(x) = x^2
.
To get a random numbers from here - with more values concentrated closer to 0 - we can do the following:
numbers = ceil(max * f(rand()))
where ceil
is the ceiling function, max
is the highest output you would like, f()
is the function you chose, and rand()
gives a random number between zero and one. Do note that the outputs of this function would be in a range from 1
to max
and not 0
to max
.
The following graph should give you some idea of why this actually works:
Notice there is a diminishing chance of an integer being chosen as the integers grow larger - i.e. the ceil(max*f(x)) is equal to one the "longest" and 10 the "shortest".
If you would like a direct relationship between the number chosen and its magnitude you would simply need to chose a different f(x)
. However, at this point this is turning into more of a mathematics question than anything else. I will look for a proper f(x)
- if i understand what you are looking for at least and get back to you. I am guessing as of now f(x)
will be e^x
but I will double check.
I hope this helps!
A quick code example:
public int weightedRandom(int max, Random rand) {
return Math.ceil(((double) max) * Math.pow(rand.nextDouble(), 2));
}
I also printed a couple out in a java program and got the following list where max == 10
:
2.0, 6.0, 8.0, 3.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0, 7.0, 1.0, 4.0, 1.0, 1.0, 6.0, 8.0, 9.0, 7.0, 5.0
I would propose to use : public double nextGaussian()
method from java.util.Random
This allow to have a distribution with more elements around the average
I won't explain again what it is written there Javamex nextGaussian (if you want more details)
So in fact you want values between 0
and n
:
the method will give values like this :
with deviation of 1 with nothing
Random r = new Random();
int n = 10;
int res = (int) Math.min(n, Math.abs(r.nextGaussian()) * n / 3);
So :
n
: deviation becomes n
n
)Math.abs
because it's symetric with 0 for middleMath.min
as a final check in case a value is higher than n
Test on 10 000 iterations :
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With