Generate random numbers with different probabilities within given ranges - java

Is there a simple algorithm that will print results of rolling a die such that the probability of getting 1,2,3,4,5 is 1/9 and the probability of getting a 6 is 3/9.
I would like to implement this in Java and intentionally only use Math.random(), if statements, for/ while loops.

As others suggested to make the sum of all events equal to 1, then number 9 will have a probability of 4/9 to be chosen.
Generate a random number between 1 and 9, inclusive on both ends. If the number be 1 to 5, you rolled that number, otherwise, you rolled 6. Note that there are 4 chances in this scheme to roll a 6, and 5 total chances to roll 1 through 5.
Random random = new Random();
int roll = random.nextInt(9) + 1; // create a random number
if (roll > 5) {
System.out.println("You rolled a 6");
}
else {
System.out.println("You rolled a " + roll);
}
To simulate more dice rolls you can add the above logic inside a for loop that runs for as many loops as you want.

Generating values for random variables with a certain distribution usually works like this:
You have a function which generates a random 0 <= q < 1,
You apply the quantile function and you obtain the value of your variable.
In your case you have a discrete random variable. You need an instance of Random:
private static final Random random = new Random();
the values assumed by the variable:
private static final double[] values = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
Compute the cumulative distribution function (sum of probabilities of the values up to the specific value) for these values:
private static final double[] cdf = {1.0 / 9, 2.0 / 9, 3.0 / 9, 4.0 / 9, 5.0 / 9, 1.0};
You random generating function will return the last value for which the cdf is not greater than q:
public static double randomValue() {
double q = random.nextDouble();
for (int i = 0; i < values.length; i++) {
if (q > cdf[i]) continue;
return values[i];
}
throw new IllegalStateException();
}

Seems pretty straightforward:
// Pass in an instance of class Random
public static int gen(Random r) {
int i = r.nextInt(9); // uniformly generate 0,...,8 inclusive
if (i < 5) {
return i + 1; // returns 1,...,5 w/ probability 1/9
} else {
return 6; // returns 6 w/ probability 4/9
}
}
Warning, I no longer have a Java compiler on my machine, so I haven't compiled this. However, the algorithm is valid as confirmed in another language.

Related

Java Random() rounding off

I'm using Java's Random to generate random numbers: 1.0, 1.1 - 10
Random random = new Random();
return (double) ((random.nextInt(91) + 10) / 10.0);
When I printed a lot of these numbers (2000), I noticed 1.0 and 10 are significant less printed than all others (repeated 20 times, happened every time). Most likely because 0.95-0.99 and 10.01-10.04 aren't generated.
Now I have read a lot of threads about this, but it still leaves me to the following question:
If these numbers would represent grades for example, you can't get lower than a 1 and higher than a 10 here, would it be legit to extend the range from 0.95 up to 10.04?
Random random = new Random();
return Double.valueOf((1005-95) / 100);
To generate a random value between 1.1 and 10 use the following code:
double min = 1.1d;
double max = 10d;
Random r = new Random();
double value = min + (max - min) * r.nextDouble();
Afterwarsds you can use Math.floor(value) too round your result
This premise
Most likely because 0.95-0.99 and 10.01-10.04 aren't
generated.
is wrong. You generate random ints from 10 inclusive to 100 inclusive. Lower fractions and rounding of values does not play into it. Random nextInt is random in the interval; the end cases is not discriminated against.
I think your method
Random random = new Random();
return (double) ((random.nextInt(91) + 10) / 10.0);
Looks correct. I would suggest measuring the anomaly you are experiencing, maybe it is a human bias from when you are merely looking at the output.
Here is some code that measures the actual random generation of the 91 values. It is before the conversion to double which is not ideal.(but I do not see how dividing by 10 does anything else than map values as 10 -> 1.0, 11 -> 1.1 ... 99 -> 9.9 and 100 -> 10.0. A measure of the final result would of course be more desirable)
Random random = new Random();
int[] measure = new int[101];
for (int i = 0; i < 10000; i++) {
int number = (random.nextInt(91) + 10);
measure[number]++;
}
for (int i = 0; i < 101; i++) {
System.out.println(i + " count: " + measure[i]);
}
Looking at the results from that code the 10 and 100 values seem to come up as often as any other.

JAVA : generate integer numbers randomly with probabilities

How can I generate integer numbers randomly from (1 to 100) with probability for example 30% if the numbers range from (1 to 50), and with probability 70% if the numbers range from (50 to 100)?
int integer = new Random().nextInt(100) + 1;
// Probabilities
..... code here ....
How would I do that?
Here is a method getRandom() which returns a single random number meeting the criteria you specified. It actually uses a random number between 0 and 9 to determine which of the two ranges to use.
public int getRandom() {
Random random = new Random();
int val = random.nextInt(10);
if (val < 3) {
return random.nextInt(50) + 1; // random range 1 to 50
}
else {
return random.nextInt(51) + 50; // random range 50 to 100
}
}
Here's a general solution that will return one of any number of events, where you specify the relative weights of the events. The weights could be probabilities, but they don't have to; they don't have to add up to 1. For example, if you have three events, and you want the first one to have probability 20%, the second 30%, and the third 50%, you could call addEvent on each event with 2, 3, and 5 as the second parameter, or 20, 30, and 50, or 0.2, 0.3, and 0.5, or any other combination of numbers that has those ratios. For your case, you could make the generic parameter an interval and add two events with weights 3 and 7 (or 30 and 70, or whatever); then, when you call randomEvent, and it returns an interval with endpoints m and n inclusive, you then generate another random number in that interval:
value = m + random.nextInt(n - m + 1);
where random is your own instance of Random.
class RandomDistribution<T> {
private class Event {
public final T event;
public final double relativeWeight;
public Event(T event, double relativeWeight) {
this.event = event;
this.relativeWeight = relativeWeight;
}
}
private double totalWeight = 0D;
private ArrayList<Event> events = new ArrayList<>();
private Random generator = new Random();
public void addEvent(T event, double relativeWeight) {
events.add(new Event(event, relativeWeight));
totalWeight += relativeWeight;
}
public T randomEvent() {
double random = generator.nextDouble() * totalWeight;
for (Event event : events) {
random -= event.relativeWeight;
if (random < 0D) {
return event.event;
}
}
// It's possible to get here due to rounding errors
return events.get(events.size() - 1).event;
}
}
You can use MockNeat probabilities() method.
String s = mockNeat.probabilites(String.class)
.add(0.1, "A")
.add(0.2, "B")
.add(0.5, "C")
.add(0.2, "D")
.val();
The above example will generate "A" with 10% probability, B with 20% probability and so on.
Integer x = m.probabilites(Integer.class)
.add(0.2, m.ints().range(0, 100))
.add(0.5, m.ints().range(100, 200))
.add(0.3, m.ints().range(200, 300))
.val();
The above example will generate a number in the range [0, 100) with 20% probability, a number in the range [100, 200) with 50% probability and a number in the range [200, 300) with 30% probability.
Disclaimer: I am the author of the library, so I might be biased when I am recommending it.

Problems Generating A Math.random Number, Either 0 or 1

I want a random number, either 0 or 1 and then that will be returned to main() as in my code below.
import java.util.Scanner;
public class Exercise8Lab7 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int numFlips = 0;
int heads = 0;
int tails = 0;
String answer;
System.out.print("Please Enter The Number Of Coin Tosses You Want: ");
numFlips = input.nextInt();
for(int x = 1;x <= numFlips; x++){
if(coinToss() == 1){
answer = "Tails";
tails++;
}
else{
answer = "Heads";
heads++;
}
System.out.print("\nCoin Toss " + x + ": " + answer);
}
System.out.println("\n\n====== Overall Results ======" +
"\nPercentage Of Heads: " + (heads/numFlips)*100 + "\nPercentage Of Tails: " + (tails/numFlips)*100);
}
public static int coinToss(){
double rAsFloat = 1 * (2 + Math.random( ) );
int r = (int)rAsFloat;
return r;
}
}
Many solutions had been suggested to use the util.Random option which I have done and works perfectly but I want to sort out why I can't get this to work. Obviously I want the number to be an int myself so I convert it to an int after the random number has been generated. But no matter what I add or multiply the Math.random() by, it will always all either be Heads or all either be Tails. Never mixed.
Try this) It will generate number 0 or 1
Math.round( Math.random() ) ;
You could use boolean values of 0 or 1 based on value of Math.random() as a double between 0.0 and 1.0 and make the random generator much simpler. And you can get rid completely of the coinToss() method.
if(Math.random() < 0.5) {
answer = "Tails";
tails++;
}
Remove the coin toss method and replace the first conditional with the code above.
Math.random(); by itself will return a value between 0.0 and less than 1.0. If the value is in the lower half, [0.0, 0.5), then it has the same probability of being in the upper half, [0.5, 1.0). Therefore you can set any value in the lower half as true and upper as false.
Wierd that no one is using a modulo division for the random number.
This is the simplest implementation you can get:
Random rand = new Random();
int randomValue = rand.nextInt() % 2;
Math.round(Math.random()) will return either 0.0 and 1.0. Since both these values are well within the limits of int range they can be casted to int.
public static int coinToss(){
return (int)Math.round(Math.random());
}
(int)(Math.random()*2) also works fine in this case
its not working because of the integer math you are using, the call to 2+ Math.Random is pretty much always giving you a answer between 0.0 and 1.0.
so assuming that you recieve 0.25 as your result your maths is as follows
double d = 1* (2 + 0.25); // (result = 2
Then you are checking to see if your result == 1 ( which it never will. )
A better result would be to declare java.util.Random as a class variable and call random.nextBoolean() and simply perform your heads/tails calculation on that.
If you were to continue to use Math.random() and lets say
return Math.random() < 0.5
Your results would be ever so slightly skewed due to the fact that Math.random() cannot return 1.0, due to the fact that the java API specification states:
"Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0."
Math.random() returns a random float in the range [0.0,1.0)--that means the result can be anything from 0 up to but not including 1.0.
Your code
double rAsFloat = 1 * (2 + Math.random( ) );
will take this number in the [0.0,1.0) range; adding 2 to it gives you a number in the [2.0,3.0) range; multiplying it by 1 does nothing useful; then, when you truncate it to an integer, the result is always 2.
To get integers from this kind of random function, you need to figure out how many different integers you could return, then multiply your random number by that. If you want a "0 or 1" answer, your range is 2 different integers, so multiply Math.random() by 2:
double rAsFloat = 2 * Math.random();
This gives you a random number in the range [0.0,2.0), which can then be 0 or 1 when you truncate to an integer with (int). If, instead, you wanted something that returns 1 or 2, for example, you'd just add 1 to it:
double rAsFloat = 1 + 2 * Math.random();
I think you've already figured out that the Random class gives you what you want a lot more easily. I've decided to explain all this anyway, because someday you might work on a legacy system in some old language where you really do need to work with a [0.0,1.0) random value. (OK, maybe that's not too likely any more, but who knows.)
The problem can be translated to boolean generation as follow :
public static byte get0Or1 {
Random random = new Random();
boolean res= random.nextBoolean();
if(res)return 1;
else return 0;
}
Here it the easiest way I found without using java.util.Random.
Blockquote
Scanner input = new Scanner (System.in);
System.out.println("Please enter 0 for heads or 1 for tails");
int integer = input.nextInt();
input.close();
int random = (int) (Math.random() + 0.5);
if (random == integer) {
System.out.println("correct");
}
else {
System.out.println("incorrect");
}
System.out.println(random);
This will take a random double from (0 to .99) and add .5 to make it (.5 to 1.49). It will also cast it to an int, which will make it (0 to 1). The last line is for testing.
for(int i=0;i<100;i++){
System.out.println(((int)(i*Math.random())%2));
}
use mod it will help you!
One more variant
rand.nextInt(2);
As it described in docs it will return random int value between 0 (inclusive) and the specified value (exclusive)

Greater chance to roll 6 with dice in Java

I was wondering how can I program a dice which has a greater chance to roll a 6?
I've tried everything but I just cant figure the algorithm.
The probability of six is given by the user.
I just don't now how to program random which uses the given probability to roll a 6 or 1-5.
I would suggest using a percentage based scheme. let the user pick the probability of a six, in this example lets say 30%. Then choose a random number between 0-1.0 (which is what java's Random#nextFloat does). If its below or equal to .3, then make it a six, otherwise make it a 1-5.
Random r = new Random();
float probability = r.nextFloat(); // get a value between 0 and 1
if (probability < probabilityOfSix){
return 6;
} else {
return r.nextInt(4) +1;
}
Let's say a 6 has twice the probability to appear. Get a random number from 1 through 7, if your result is either a 6 or a 7, then you have a 6.
Same thing for three times the probability. Fetch a random number from 1-8: 6, 7, and 8 become a 6.
Divide what remains of the interval (0,1) when you take out a region the size of the probability of six into 5 equal pieces and assign these regions to 1 - 5:
/**
* #param pSix probability that a six is returned
* #param rnd Random instance
* #return a random integer between 1 and 6 with 1-5 equiprobable and P(6) = pSix
*/
public int loadedDice(final double pSix, final Random rnd) {
final double pOther = (1d - pSix) / 5d;
final float val = rnd.nextFloat();
if (val < pSix) {
return 6;
}
return (int) Math.ceil((val - pSix) / pOther);
}
}

Java Biasing Random Numbers in a Triangular Array

This question is an extension of Java- Math.random(): Selecting an element of a 13 by 13 triangular array. I am selecting two numbers at random (0-12 inclusive) and I wanted the values to be equal.
But now, since this is a multiplication game, I want a way to bias the results so certain combinations come up more frequently (like if the Player does worse for 12x8, I want it to come up more frequently). Eventually, I would like to bias towards any of the 91 combinations, but once I get this down, that should not be hard.
My Thoughts: Add some int n to the triangular number and Random.nextInt(91 + n) to bias the results toward a combination.
private int[] triLessThan(int x, int[] bias) { // I'm thinking a 91 element array, 0 for no bias, positive for bias towards
int i = 0;
int last = 0;
while (true) {
int sum = 0;
for (int a = 0; a < i * (i + 2)/2; a++){
sum += bias[a]
}
int triangle = i * (i + 1) / 2;
if (triangle + sum > x){
int[] toReturn = {last,i};
return toReturn;
}
last = triangle;
i++;
}
}
At the random number roll:
int sum = sumOfArray(bias); // bias is the array;
int roll = random.nextInt(91 + sum);
int[] triNum = triLessThan(roll);
int num1 = triNum[1];
int num2 = roll - triNum[0]; //now split into parts and make bias[] add chances to one number.
where sumOfArray just finds the sum (that formula is easy). Will this work?
Edit: Using Floris's idea:
At random number roll:
int[] bias = {1,1,1,...,1,1,1} // 91 elements
int roll = random.nextInt(sumOfBias());
int num1 = roll;
int num2 = 0;
while (roll > 0){
roll -= bias[num2];
num2++;
}
num1 = (int) (Math.sqrt(8 * num2 + 1) - 1)/2;
num2 -= num1 * (num1 + 1) / 2;
You already know how to convert a number between 0 and 91 and turn it into a roll (from the answer to your previous question). I would suggest that you create an array of N elements, where N >> 91. Fill the first 91 elements with 0...90, and set a counter A to 91. Now choose a number between 0 and A, pick the corresponding element from the array, and convert to a multiplication problem. If the answer is wrong, append the number of the problem to the end of the array, and increment A by one.
This will create an array in which the frequencies of sampling will represent the number of times a problem was solved incorrectly - but it doesn't ever lower the frequency again if the problem is solved correctly the next time it is asked.
An alternative and better solution, and one that is a little closer to yours (but distinct) creates an array of 91 frequencies - each initially set to 1 - and keeps track of the sum (initially 91). But now, when you choose a random number (between 0 and sum) you traverse the array until the cumulative sum is greater then your random number - the number of the bin is the roll you choose, and you convert that with the formula derived earlier. If the answer is wrong you increment the bin and update the sum; if it is right, you decrement the sum but never to a value less than one, and update the sum. Repeat.
This should give you exactly what you are asking: given an array of 91 numbers ("bins"), randomly select a bin in such a way that the probability of that bin is proportional to the value in it. Return the index of the bin (which can be turned into the combination of numbers using the method you had before). This function is called with the bin (frequency) array as the first parameter, and the cumulative sum as the second. You look up where the cumulative sum of the first n elements first exceeds a random number scaled by the sum of the frequencies:
private int chooseBin(float[] freq, float fsum) {
// given an array of frequencies (probabilities) freq
// and the sum of this array, fsum
// choose a random number between 0 and 90
// such that if this function is called many times
// the frequency with which each value is observed converges
// on the frequencies in freq
float x, cs=0; // x stores random value, cs is cumulative sum
int ii=-1; // variable that increments until random value is found
x = Math.rand();
while(cs < x*fsum && ii<90) {
// increment cumulative sum until it's bigger than fraction x of sum
ii++;
cs += freq[ii];
}
return ii;
}
I confirmed that it gives me a histogram (blue bars) that looks exactly like the probability distribution that I fed it (red line):
(note - this was plotted with matlab so X goes from 1 to 91, not from 0 to 90).
Here is another idea (this is not really answering the question, but it's potentially even more interesting):
You can skew your probability of choosing a particular problem by sampling something other than a uniform distribution. For example, the square of a uniformly sampled random variate will favor smaller numbers. This gives us an interesting possibility:
First, shuffle your 91 numbers into a random order
Next, pick a number from a non-uniform distribution (one that favors smaller numbers). Since the numbers were randomly shuffled, they are in fact equally likely to be chosen. But now here's the trick: if the problem (represented by the number picked) is solved correctly, you move the problem number "to the top of the stack", where it is least likely to be chosen again. If the player gets it wrong, it is moved to the bottom of the stack, where it is most likely to be chosen again. Over time, difficult problems move to the bottom of the stack.
You can create random distributions with different degrees of skew using a variation of
roll = (int)(91*(asin(Math.rand()*a)/asin(a)))
As you make a closer to 1, the function tends to favor lower numbers with almost zero probability of higher numbers:
I believe the following code sections do what I described:
private int[] chooseProblem(float bias, int[] currentShuffle) {
// if bias == 0, we choose from uniform distribution
// for 0 < bias <= 1, we choose from increasingly biased distribution
// for bias > 1, we choose from uniform distribution
// array currentShuffle contains the numbers 0..90, initially in shuffled order
// when a problem is solved correctly it is moved to the top of the pile
// when it is wrong, it is moved to the bottom.
// return value contains number1, number2, and the current position of the problem in the list
int problem, problemIndex;
if(bias < 0 || bias > 1) bias = 0;
if(bias == 0) {
problem = random.nextInt(91);
problemIndex = problem;
}
else {
float x = asin(Math.random()*bias)/asin(bias);
problemIndex = Math.floor(91*x);
problem = currentShuffle[problemIndex];
}
// now convert "problem number" into two numbers:
int first, last;
first = (int)((Math.sqrt(8*problem + 1)-1)/2);
last = problem - first * (first+1) / 2;
// and return the result:
return {first, last, problemIndex};
}
private void shuffleProblems(int[] currentShuffle, int upDown) {
// when upDown==0, return a randomly shuffled array
// when upDown < 0, (wrong answer) move element[-upDown] to zero
// when upDown > 0, (correct answer) move element[upDown] to last position
// note - if problem 0 is answered incorrectly, don't call this routine!
int ii, temp, swap;
if(upDown == 0) {
// first an ordered list:
for(ii=0;ii<91;ii++) {
currentShuffle[ii]=ii;
}
// now shuffle it:
for(ii=0;ii<91;ii++) {
temp = currentShuffle[ii];
swap = ii + random.nextInt(91-ii);
currentShuffle[ii]=currentShuffle[swap];
currentShuffle[swap]=temp;
}
return;
}
if(upDown < 0) {
temp = currentShuffle[-upDown];
for(ii = -upDown; ii>0; ii--) {
currentShuffle[ii]=currentShuffle[ii-1];
}
currentShuffle[0] = temp;
}
else {
temp = currentShuffle[upDown];
for(ii = upDown; ii<90; ii++) {
currentShuffle[ii]=currentShuffle[ii+1];
}
currentShuffle[90] = temp;
}
return;
}
// main problem posing loop:
int[] currentShuffle = new int[91];
int[] newProblem;
int keepGoing = 1;
// initial shuffle:
shuffleProblems( currentShuffle, 0); // initial shuffle
while(keepGoing) {
newProblem = chooseProblem(bias, currentShuffle);
// pose the problem, get the answer
if(wrong) {
if(newProblem > 0) shuffleProblems( currentShuffle, -newProblem[2]);
}
else shuffleProblems( currentShuffle, newProblem[2]);
// decide if you keep going...
}

Categories