Reputation: 28228
I have been trying to find a way to generate a number which is more likely to generate a number in the middle of a range. Given:
rnum = r.nextInt(5) + 1;//+ 1 to exclude 0
generates a completely random number between 1 and 5(0 and 4 if + 1
is removed). What I want to do, is generate less of 1 and 5, and generate a lot of 3's. I attempted this:
int[] classes = {1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5};
System.out.println("Class " + classes[r.nextInt(15)]);//logs the output
But it generates this:(print statement repeated 10 times)
Class 2
Class 3
Class 1
Class 4
Class 3
Class 4
Class 2
Class 3
Class 2
Class 5
However, this is neither efficient or a good way to do it. Further, because the random number generator used in retrieving the number is completely random instead of focusing on a center value, thus making the output above. 3 only appears 30% of the time, which is too low. 2 appears 30% of the time as well, which means (in this test) it has the same probability to be generated as 3.
So, how can I generate a number randomly with a higher probability to generate a number in the middle of a range?
Upvotes: 1
Views: 1846
Reputation: 2035
The easiest approach would be to start with an array which contains desired probabilities.
import java.util.Random;
class Main {
public static int getOneNumber(double[] probs, Random rnd) {
double r = rnd.nextDouble();
for (int j = 0; j < probs.length; j++) {
if (r < probs[j]) {
return j;
}
r -= probs[j];
}
throw new RuntimeException("probabilities should sum to 1");
}
public static void main(String[] args) {
// Desired probabilities
double[] probabilities = {
0.05, // 0
0.15, // 1
0.6, // 2
0.15, // 3
0.05 }; // 4
Random rnd = new Random();
for (int i = 0; i < 20; i++) {
System.out.println(getOneNumber(probabilities, rnd));
}
}
}
The idea here is as following. We generate random number between 0 and 1. Now we check: is it below 0.05? If it is, we return 0 -- and it will happen with the probability 0.05. If it is not, we check if our number is between 0.05 and 0.15 (by subtracting 0.05 from it and comparing with 0.1). If it is (this happens with probability 0.15-0.05 = 0.1) -- we got 1. If it is not, we check if the random number is between 0.15 and 0.75, etc.
Upvotes: 3
Reputation: 9
Start with a random number in [0, 1] and then raise it to the power of some positive number. Powers < 1 will bias upward, i.e. the numbers will be more likely to be higher than lower within [0, 1], and powers > 1 will bias downward. Then use multiplication and addition to shift the range of numbers from [0, 1] to your desired range.
double rnum,bias_factor;
int low,high;
//low, high and bias_factor are the inputs, then :
high = (low + high)/2;
for(int i = 0;i<=10;i++)
{
rnum = Math.random();
rnum = Math.pow(rnum,bias_factor);
rnum = (low + (high - low)*rnum)+1;
System.out.println((int)rnum);
}
The statement:
high=(low + high)/2;
makes the middle value of given range as the upper value of the range. Hence with a low bias factor the output gets biased towards the upper values of a range. For eg: Initially giving the inputs as high = 5, low = 1; and bias_factor = 0.4 generates more middle values(3's) in the range [1,5]. I think this might help: https://gamedev.stackexchange.com/questions/54551/using-random-numbers-with-a-bias
Upvotes: 0
Reputation: 2789
You can generate the random values using binomial distribution with an algorithm presented in this answer:
public static int getBinomial(int n, double p) {
int x = 0;
for(int i = 0; i < n; i++) {
if(Math.random() < p)
x++;
}
return x;
}
Call the function like this to get a peak of the value 3 in the middle:
getBinomial(4,0.5) +1;
The values will be approximately distributed like follows:
1 2 3 4 5
1/16 1/4 3/8 1/4 1/16
Upvotes: 0
Reputation: 6783
The easiest approach is that one:
Generate a floating point number between 0 and 1. Double the value and subtract 1 Take that number to any power, e.g. 10. Divide it by 2 and add 0.5
Multiply your result with 15
float fVal = Math.power(r.next()*2-1, 10) / 2 + 0.5;
int iVal = Math.floor(fVal * 15);
int[] classes = {1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5};
System.out.println("Class " + classes[iVal]);//logs the output
This should make your probability look more like a gaussian bell curve Maybe you want to read about Normal Distribution
Upvotes: 1