This question already has answers here:
Java: random integer with non-uniform distribution
(10 answers)
Closed 2 years ago.
Can I do the following in java without using external libraries? Maybe with if-else statements?
thanks
import random
print(random.choices(['A', 'B', 'C'], [0.5, 0.3, 0.2]))
Sure. The tools required are:
an instance of java.util.Random
call its nextDouble method.
The algorithm is something like:
First calculate, once-off, the incremental weighting. In your example that would be [0.5, 0.8, 1.0].
Multiply the output of nextDouble with the final weight (here the final weight is 1.0, so not needed. Multiplying by 1.0 doesn't hurt, of course).
loop through the incremental weights and check if the random number you have is less than it. If yes, that's your choice.
Example:
public class WeightedList {
private final char[] choices;
private final double[] weights;
private final Random rnd = new Random();
public WeightedList(char[] choices, double[] weights) {
if (choices.length != weights.length) throw new IllegalArgumentException();
this.choices = Arrays.copyOf(choices);
this.weights = new double[weights.length];
double s = 0.0;
for (int i = 0; i < weights.length; i++) {
this.weights[i] = (s += weights[i]);
}
}
public char get() {
double v = rnd.nextDouble() * weights[weights.length - 1];
for (int i = 0; i < weights.length - 1; i++) {
if (v < weights[i]) return choices[i];
}
return weights[weights.length - 1];
}
}
Related
This question already has answers here:
How to pick an item by its probability?
(13 answers)
Closed 1 year ago.
I have a mathematical/programming question about a problem I am trying to solve. Given this simple array of integers:
int[] goals = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Do you know how could I implement a function to pick an element from the array given a distributed probability like the one from this example?:
The probability of picking goals[0] is 50%
The probability of picking goals[1] is 30%
The probability of picking goals[2] is 12%
The probability of picking goals[3] is 2.5%
The probability of picking goals[4] is 0.85%
etcetera
The probability distribution is of my choice, hardcoded to keep things simple.
Thank you very much for your inputs!
Let's say you specify you probabilities in an array:
double[] probs = {50, 30, 12, 2.5, 0.85 /*, ...*/};
You can calculate the total of the probabilities:
double totalProbs = DoubleStream.of(probs).sum();
And declare a random object:
Random random = new Random();
You can then write a method that returns a random goal:
public int randomGoal() {
double x = random.nextDouble()*totalProbs;
for (int i = 0; i < probs.length; ++i) {
x -= probs[i];
if (x <= 0) {
return goals[i];
}
}
return goals[goals.length-1];
}
You can create an array of 100 elements, then for each number i in the array goals, put i into the new array n times where n equals the probability of i times the array size (100). Then randomly pick an element from the new array.
You can increase the array size (100) to get a more precise result. The most precise result will be when you pick the array size that makes every n a whole number.
Example:
https://jsfiddle.net/o5hmjxd2/28/
const x = [0,1,2,3,4,5,6,7,8,9];
const p = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
const l = 100;
let y = [];
for (const i = 0; i < x.length; i++) {
y = y.concat(new Array(l*p[i]).fill(x[i]));
}
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
// result
console.log(y);
console.log(y[getRandomInt(y.length - 1)])
Try this.
public class DistributedProbability {
final NavigableMap<Double, Integer> distribution = new TreeMap<>();
final Random random = new Random();
public final int size;
public final double max;
public DistributedProbability(double... rates) {
double sum = 0;
int index = 0;
for (double rate : rates) {
sum += rate;
this.distribution.put(sum, index++);
}
size = index;
max = sum;
}
int next() {
double d = random.nextDouble(max);
Entry<Double, Integer> entry = distribution.higherEntry(d);
return entry.getValue();
}
}
and
public static void main(String[] args) {
DistributedProbability d = new DistributedProbability(
50, 30, 12, 2.5, 0.85, 4.65);
int[] counts = new int[d.size];
for (int i = 0; i < 100000; ++i)
++counts[d.next()];
System.out.println(Arrays.toString(counts));
}
output:
[49844, 30101, 12023, 2512, 845, 4675]
This question already has answers here:
Coding pattern for random percentage branching?
(7 answers)
Closed 4 years ago.
I want to get an integer random number between 0 and 100 with different probabilities in different ranges.
For example I want the probability of values between 0 and 20 to be 0.5 and the probability of values between 21 and 80 to be 0.4 and the probability of values between 81 and 100 to be 0.1.
Is there any method or class in Java or any library for Java to do it? If not, how can I do it myself?
You just need to have an extra random number determining the range it should generate:
int getRandomNumberWithRangeProbability() {
double range = Math.random();
if (range < 0.5) {
return randomWithRange(0, 20);
} else if (range < 0.9) {
return randomWithRange(21, 80);
} else {
return randomWithRange(81, 100);
}
}
int randomWithRange(int min, int max) {
int range = (max - min) + 1;
return (int) (Math.random() * range) + min;
}
A small test can be found here.
Credits to AusCBloke for the randomWithRange() method.
You should get random in each range and then get another random between 0 and 1 and treat in your interest
Good Luck
ETG
I can think of this way don't know if there is any inbuilt function for doing this or not
So Make a function that will return random between two integers.
make a variable probable having random value of 1-10
Satisfy these condition
if(probable>=0 && probable<=5){
random = getUniqueRandom(0, 20);
}
else if(probable>=6 && probable<=9) {
random = getUniqueRandom(21, 80);
}
else if (probable == 10) {
random = getUniqueRandom(81, 100);
}
Here is the working implementation
import java.util.Random;
public class Solution {
private static Random r = new Random();
public static void main(String[] args) {
int pro1 = 0, pro2 =0, pro3 =0;
for(int i=0; i<10000; i++) {
int probable = getUniqueRandom(0, 10);
int random = 0;
if(probable>=0 && probable<=5){
random = getUniqueRandom(0, 20);
pro1++;
}
else if(probable>=6 && probable<=9) {
random = getUniqueRandom(21, 80);
pro2++;
}
else if (probable == 10) {
random = getUniqueRandom(81, 100);
pro3++;
}
//System.out.println(random);
}
System.out.println("Checked 10000 Times.\n0-20 Found: "+pro1);
System.out.println("21-80 Found: "+pro2);
System.out.println("81-100 Found: "+pro3);
}
static int getUniqueRandom(int min, int max){
int num = r.nextInt(max-min+1) + min;
return num;
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
This program is supposed to go through the number of 2 to 50 people and get the probability out of 100,000 trials whether 2 people will have the same birthday or not.
import java.util.Random;
public class birthday {
public static void main(String[] args) {
int N = 2;
while (N != 51) {
double probability = one_probability(N);
System.out.println(N + " " + probability);
N++;
}
}
public static boolean one_group(int N) {
int A[] = new int[N];
Random random = new Random();
boolean have_match = false;
for (int i = 0; i < N; i++) { //assigns each person a birthday
int k = random.nextInt(365);
A[i] = k;
}
for (int i = 0; i < N; i++) { //testing to see if birthdays match up
for (int j = i+1; j < N; j++) {
if (A[i] == A[j]) { have_match = true; break; }
}
}
return have_match;
}
public static double one_probability(int N) {
int x = 0;
for (int i = 0; i < 100000; i++) { //repeating 100,000 times to get average probability
boolean have_match = one_group(N);
if (have_match == true) { x++; }
}
double probability = x / 100000; //getting the average probability
return probability;
}
}
Here's the result (it goes from 2-50), it keeps giving me zeros so I know something is wrong. Please help :)
Output
Try with
int probability = x / 1000; // 1/100 of the value to get an probability in percent (integer)
or
float probably = x / 100000F; //F for a `float`
double probability = x / 100000.0; //a decimal without a F is a `double`
Without that, this can't work :
float probably = x / 100000;
First, the division of two integer will be stored in memory like an integer then store in a float/double. That storage truncate the value. This is the operator logic to return the biggest type in an operation so :
int * int -> int
int * float -> float
int * double -> double
short * short -> int //that's a trick, int is the minimal value an operation can return
int probability=x/100000; always return 0.
convert it to double probability=x/100000.0; the value of probability will always less than or equal one. because x will never be greater than 100000. And also change the return type of one_probability method to double.
I have a list of 10 probabilities (assume these are sorted in descending order): <p1, p2, ..., p10>. I want to sample (without replacement) 10 elements such that the probability of selecting i-th index is p_i.
Is there a ready to use Java method in common libraries like Random, etc that I could use to do that?
Example: 5-element list: <0.4,0.3,0.2,0.1,0.0>
Select 5 indexes (no duplicates) such that their probability of selection is given by the probability at that index in the list above. So index 0 would be selected with probability 0.4, index 1 selected with prob 0.3 and so on.
I have written my own method to do that but feel that an existing method would be better to use. If you are aware of such a method, please let me know.
This is how this is typically done:
static int sample(double[] pdf) {
// Transform your probabilities into a cumulative distribution
double[] cdf = new double[pdf.length];
cdf[0] = pdf[0];
for(int i = 1; i < pdf.length; i++)
cdf[i] += pdf[i] + cdf[i-1];
// Let r be a probability [0,1]
double r = Math.random();
// Search the bin corresponding to that quantile
int k = Arrays.binarySearch(cdf, random.nextDouble());
k = k >= 0 ? k : (-k-1);
return k;
}
If you want to return a probability do:
return pdf[k];
EDIT: I just noticed you say in the title sampling without replacement. This is not so trivial to do fast (I can give you some code I have for that). Anyhow, your question does not make any sense in that case. You cannot sample without replacement from a probability distribution. You need absolute frequencies.
i.e. If I tell you that I have a box filled with two balls: orange and blue with the proportions 20% and 80%. If you do not tell me how many balls you have of each (in absolute terms), I cannot tell you how many balls you will have in a few turns.
EDIT2: A faster version. This is not how it is typically but I have found this suggestion on the web, and I have used it in projects of mine as well.
static int sample(double[] pdf) {
double r = random.nextDouble();
for(int i = 0; i < pdf.length; i++) {
if(r < pdf[i])
return i;
r -= pdf[i];
}
return pdf.length-1; // should not happen
}
To test this:
// javac Test.java && java Test
import java.util.Arrays;
import java.util.Random;
class Test
{
static Random random = new Random();
public static void sample(double[] pdf) {
...
}
public static void main(String[] args) {
double[] pdf = new double[] { 0.3, 0.4, 0.2, 0.1 };
int[] counts = new int[pdf.length];
final int tests = 1000000;
for(int i = 0; i < tests; i++)
counts[sample(pdf)]++;
for(int i = 0; i < counts.length; i++)
System.out.println(counts[i] / (double)tests);
}
}
You can see we get output very similar to the PDF that was used:
0.3001356
0.399643
0.2001143
0.1001071
This are the times I get when running each version:
1st version: 0m0.680s
2nd version: 0m0.296s
Use sample[i] as index of your values array.
public static int[] WithoutReplacement(int m, int n) {
int[] perm = new int[n];
for (int i = 0; i < n; i++) {
perm[i] = i;
}
//take sample
for (int i = 0; i < m; i++) {
int r = i + (int) (Math.random() * (n - 1));
int tmp = perm[i];
perm[i] = perm[r];
perm[r] = tmp;
}
int[] sample = new int[m];
for (int i = 0; i < m; i++) {
sample[i] = perm[i];
}
return sample;
}
So what i need is basically described in the subject.
Like if i would put in number 12 and amount of parts it should devide into, i would like it to return something like (with 4 parts) 8, 2, 1, 1. But not doubles because i need the values as int.
I had found one answer earlier but it only worked using doubles. not ints.
(this is the one i found)
public double[] divideUniformlyRandomly(double number, int part) {
double uniformRandoms[] = new double[part];
Random random = new Random();
double mean = number / part;
double sum = 0.0;
for (int i=0; i<part / 2; i++) {
uniformRandoms[i] = random.nextDouble() * mean;
uniformRandoms[part - i - 1] = mean + random.nextDouble() * mean;
sum += uniformRandoms[i] + uniformRandoms[part - i -1];
}
uniformRandoms[(int)Math.ceil(part/2)] = uniformRandoms[(int)Math.ceil(part/2)] + number - sum;
return uniformRandoms;
I had tried changing this code to work using Ints by doing this:
public int[] divide(int number) {
int part = getDivider(number);
int uniformRandoms[] = new int[part];
Random random = new Random();
int mean = number / part;
int sum = 0;
for (int i=0; i<part / 2; i++) {
uniformRandoms[i] = random.nextInt() * mean;
uniformRandoms[part - i - 1] = mean + random.nextInt() * mean;
sum += uniformRandoms[i] + uniformRandoms[part - i -1];
}
uniformRandoms[(int)Math.round(part/2)] = uniformRandoms[(int)Math.round(part/2)] + number - sum;
for(int i : uniformRandoms)
System.out.println(i);
return uniformRandoms;
}
But when running that using number: 512 and using 10 parts (getDivider() will return 10) itll output this:
-1058809647, -2102647561, 469849949, 1627965716, -290084223, -33347991
And alot more of this kind of numbers.
Thanks.
Assuming every term should at least be 1.
public int[] divide(int number, int parts) {
int[] randoms = new int[parts];
Arrays.fill(randoms, 1); // At least one
int remainder = number - parts;
Random random = new Random();
for (int i = 0; i < parts - 1 && remainder > 0; ++i) {
int diff = random.nextInt(remainder);
randoms[i] += diff;
remainder -= diff;
}
randoms[parts - 1] += remainder;
Arrays.sort(randowms);
// Reverse (for getting a descending array):
for (int i = 0, j = parts - 1; i < j; ++i, --j) {h
int temp = randoms[i];
randoms[i] = randoms[j];
randoms[j] = temp;
}
return randoms;
}
This is not uniformly distributed. For that one could iterate till remainder becomes 0, everytime randomly picking an index to increase. Or so. Have fun.
Was this homework?
Use Random#nextInt(int)
public int[] divideUniformlyRandomly(int number, int parts) {
Random random = new Random();
int[] randoms = new int[];
for(int i = 0; i < parts; i++) {
randoms[randoms.length] = random.nextInt(number);
}
return randoms;
}
Here's an algorithm which will do the job:
Create an array of length parts+1.
Add the values 0 and number to the array, then fill the remainder of
it with unique random values using random.nextInt(number-1) + 1
to get values between 0 and number exclusive of the range limits.
Sort the array.
Iterate through the sorted array starting at index 1. The successive
differences array[i] - array[i-1] will be a set of positive integers that
sum to number.
If zeros are allowed, then you don't need the uniqueness criterion in filling the array.
If you need uniqueness, you might consider adding random values to a HashSet (which only .add()'s unique entries) until the size meets your requirement, then convert it with .toArray().
Here's an actual implementation:
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
public class SumToTotal {
public static Random r = new Random();
public static int[] divide(int number, int number_of_parts) {
HashSet<Integer> uniqueInts = new HashSet<Integer>();
uniqueInts.add(0);
uniqueInts.add(number);
int array_size = number_of_parts + 1;
while (uniqueInts.size() < array_size) {
uniqueInts.add(1 + r.nextInt(number - 1));
}
Integer[] dividers = uniqueInts.toArray(new Integer[array_size]);
Arrays.sort(dividers);
int[] results = new int[number_of_parts];
for(int i = 1, j = 0; i < dividers.length; ++i, ++j) {
results[j] = dividers[i] - dividers[j];
}
return results;
}
public static void main(String[] args) {
System.out.println(Arrays.toString(divide(12, 5)));
}
}
This produces results such as [3, 2, 1, 2, 4] or [1, 5, 2, 3, 1].
A inefficient but very easy way would be to loop n times and increment one of the indices by one.
void divider(int number, int divisions)
{
Random rand = new Random();
int[] container = new int[divisions];
System.out.print(number + "->");
while (number > 0)
{
container[rand.nextInt(divisions)]++;
number--;
}
for (int i : container)
{
System.out.print("[" + i + "]");
}
}
divider(1000, 20) could output:
1000->[57][43][60][35][39][47][45][59][51][71][52][54][58][48][33][49][49][46][49][55]
1000->[60][50][49][53][42][52][52][45][40][51][52][51][53][47][51][46][53][56][45][52]
1000->[52][43][49][53][57][45][42][43][61][61][58][44][46][49][52][39][63][45][54][44]
On my way to old PC it takes only 11ms to divide 100.000 in 20 different "containers". So if you are not using this very often and/or on very big numbers this is a perfectly valid way to do it.