how to randomly change a probability? - java

I have a collection of double values, each representing a probability (i.e. [0-1] range).
I want to add an uniformly distributed noise to these values. The values, after the modification, must still represent probabilities ([0-1] range).
Additionally, I want to be able to specify a desired range for the perturbation independently for each value: for some probabilities i would like to modify them by a small percentage and for others I would like to change them by a large percentage.
I'm unsure about how to proceed: I can only imagine swapping probabilities between different items.
I have considered to use java.Util.Random but I dont see any methods I can use.
Can anyone illustrate a possible algorithm to achieve this?

If I understand you correctly, you are trying to add a random, uniformly distributed noise to a set of values (which are probabilities, and therefore must remain between 0 and 1).
Suppose your original value is:
double myProbability;
and you want to add an uniform noise with max range:
double desiredDelta;
You can, at most, vary it such that it doesn't become > 1 or < 0:
double maxDelta = Math.min(myProbability, 1-myProbability);
At this point, you need to define what the actual range of your noise will be:
double actualDelta = Math.min(desiredDelta, maxDelta);
Now you need to either add or remove a random value between 0 and actualDelta:
boolean noiseIsAdded = Math.random() > 0.5; //50% prob add, 50% prob subtract
double noise = Math.random() * actualDelta; //scales down the 0-1 range to 0-actualDelta
double newProbability;
if (noiseIsAdded) newProbability = myProbability + noise;
else newProbability = myProbability - noise;
And you should have it.

Related

Smart algorithm to randomize a Double in range but with odds

I use the following function to generate a random double in a specific range :
nextDouble(1.50, 7.00)
However, I've been trying to come up with an algorithm to make the randomization have higher probability to generate a double that is close to the 1.50 than it is to 7.00. Yet I don't even know where it starts. Anything come to mind ?
Java is also welcome.
You should start by discovering what probability distribution you need. Based on your requirements, and assuming that random number generations are independent, perhaps Poisson distribution is what you are looking for:
a call center receives an average of 180 calls per hour, 24 hours a day. The calls are independent; receiving one does not change the probability of when the next one will arrive. The number of calls received during any minute has a Poisson probability distribution with mean 3: the most likely numbers are 2 and 3 but 1 and 4 are also likely and there is a small probability of it being as low as zero and a very small probability it could be 10.
The usual probability distributions are already implemented in libraries e.g. org.apache.commons.math3.distribution.PoissonDistribution in Apache Commons Math3.
I suggest to not think about this problem in terms of generating a random number with irregular probability. Instead, think about generating a random number normally in a some range, but then map this range into another one in non-linear way.
Let's split our algorithm into 3 steps:
Generate a random number in [0, 1) range linearly (so using a standard random generator).
Map it into another [0, 1) range in non-linear way.
Map the resulting [0, 1) into [1.5, 7) linearly.
Steps 1. and 3. are easy, the core of our algorithm is 2. We need a way to map [0, 1) into another [0, 1), but non-linearly, so e.g. 0.7 does not have to produce 0.7. Classic math helps here, we just need to look at visual representations of algebraic functions.
In your case you expect that while the input number increases from 0 to 1, the result first grows very slowly (to stay near 1.5 for a longer time), but then it speeds up. This is exactly how e.g. y = x ^ 2 function looks like. Your resulting code could be something like:
fun generateDouble(): Double {
val step1 = Random.nextDouble()
val step2 = step1.pow(2.0)
val step3 = step2 * 5.5 + 1.5
return step3
}
or just:
fun generateDouble() = Random.nextDouble().pow(2.0) * 5.5 + 1.5
By changing the exponent to bigger numbers, the curve will be more aggressive, so it will favor 1.5 more. By making the exponent closer to 1 (e.g. 1.4), the result will be more close to linear, but still it will favor 1.5. Making the exponent smaller than 1 will start to favor 7.
You can also look at other algebraic functions with this shape, e.g. y = 2 ^ x - 1.
What you could do is to 'correct' the random with a factor in the direction of 1.5. You would create some sort of bias factor. Like this:
#Test
void DoubleTest() {
double origin = 1.50;
final double fiarRandom = new Random().nextDouble(origin, 7);
System.out.println(fiarRandom);
double biasFactor = 0.9;
final double biasedDiff = (fiarRandom - origin) * biasFactor;
double biasedRandom = origin + biasedDiff;
System.out.println(biasedRandom);
}
The lower you set the bias factor (must be >0 & <= 1), the stronger your bias towards 1.50.
You can take a straightforward approach. As you said you want a higher probability of getting the value closer to 1.5 than 7.00, you can even set the probability. So, here their average is (1.5+7)/2 = 4.25.
So let's say I want a 70% probability that the random value will be closer to 1.5 and a 30% probability closer to 7.
double finalResult;
double mid = (1.5+7)/2;
double p = nextDouble(0,100);
if(p<=70) finalResult = nextDouble(1.5,mid);
else finalResult = nextDouble(mid,7);
Here, the final result has 70% chance of being closer to 1.5 than 7.
As you did not specify the 70% probability you can even make it random.
you just have to generate nextDouble(50,100) which will give you a value more than or equal 50% and less than 100% which you can use later to apply this probability for your next calculation. Thanks
I missed that I am using the same solution strategy as in the reply by Nafiul Alam Fuji. But since I have already formulated my answer, I post it anyway.
One way is to split the range into two subranges, say nextDouble(1.50, 4.25) and nextDouble(4.25, 7.0). You select one of the subranges by generating a random number between 0.0 and 1.0 using nextDouble() and comparing it to a threshold K. If the random number is less than K, you do nextDouble(1.50, 4.25). Otherwise nextDouble(4.25, 7.0).
Now if K=0.5, it is like doing nextDouble(1.50, 7). But by increasing K, you will do nextDouble(1.50, 4.25) more often and favor it over nextDouble(4.25, 7.0). It is like flipping an unfair coin where K determines the extent of the cheating.

Do an action with some probability in java

In Java, I am trying to do an action with a probability p. p is a float variable in my code. I came up with this way of doing it:
if( new Random().nextFloat() < p)
do action
I wanted to confirm if this is the correct way of doing it.
There is a TL;DR at the end.
From javadocs for nextFloat() (emphasis by me):
public float nextFloat()
Returns the next pseudorandom, uniformly distributed float value
between 0.0 and 1.0 from this random number generator's sequence.
If you understand what uniform distribution is, knowing this about nextFloat() is going to be enough for you. Yet I am going to explain a little about uniform distribution.
In uniform distribution, U(a,b) each number in the interval [a,b], and also all sub-intervals of the same length within [a,b] are equally probable, i.e. they have equal probability.
In the figure, on the left is the PDF, and on the right the CDF for uniform distribution.
For uniform distribution, the probability of getting a number less than or equal to n, P(x <= n) from the distribution is equal to the number itself (look at the right graph, which is cumulative distribution function for uniform distribution). That is, P(x <= 0.5) = 0.5, P(x <= 0.9) = 0.9. You can learn more about uniform distribution from any good statistics book, or some googling.
Fitting to your situation:
Now, probability of getting a number less than or equal to p generated using nextFloat() is equal to p, as nextFloat() returns uniformly distributed number. So, to make an action happen with a probability equal to p all you have to do is:
if (condition that is true with a probability p) {
do action
}
From what is discussed about nextFloat() and uniform distribution, it turns out to be:
if(randObj.nextFloat() <= p) {
do action
}
Conclusion:
What you did is almost the right way to do what you intended. Just adding the equal sign after < is all that's needed, and it doesn't hurt much to leave out the equal sign either!
P.S.: You don't need to create a new Random object each time in your conditional, you can create one, say randObj before your loop, and then invoke its nextFloat() method whenever you want to generate a random number, as I have done in my code.
Comment by pjs:
Take a look at the comment on the question by pjs, which is very important and well said. I quote:
Do not create a new Random object each time, that's not how PRNGs are
meant to be used! A single Random object provides a sequence of values
with good distributional properties. Multiple Random objects created
in rapid succession are 1) computationally expensive, and 2) may have
highly correlated initial states, thus producing highly correlated
outcomes. Random actually works best when you create a single instance
per program and keep drawing from it, unless you really really know
what you're doing and have specific reasons for using correlation
induction strategies.
TL;DR
What you did is almost the right way to do it. Just adding the equal sign after < (to make it <=) is all that's needed, and it doesn't hurt much to leave out the equal sign either!
Yes. That is correct (from a pure probability perspective). Random().nextFloat() will generate a number between 0.0 and 1.0 exclusive. So as long as your probability is as a float in the range 0.0 and 1.0, this is the correct way of doing it.
You can read more of the exact nextFloat() documentation here.

Why the random function in java is always generating high values?

I am implementing a test data generator in java that uses to generate random values for java primitive types.
The range of possible parameters values is not limited. For example, if I want to generate a random integer or float I will consider all possible values (MAX_INT-MIN_INT). To do so, I am using stuff like :
Random().nextInt()
Random().nextLong()
Random().nextFloat()*Float.MAX_VALUE
Random().nextDouble()*Double.MAX_VALUE
And so on...
However, doing like this, I note that the generated values are always high (close to the max and low value of the parameter type). After 100000 iteration for example, the random operator didn't generate a value in the range [-1000 - 1000]. The same thing for floats, longs. etc,...
Can you give me an explanation of how the random operator is performing in Java? Why the generated values are always high when we consider all possible values of the Java type?
Thanks in advance.
Your preception of "high" and "low" is wrong.
The probability of a single value (assuming uniform distribution) to be in [-1000,1000] is 2001/(MAX_INT-MIN_INT), which is around 0.00000046.
This probability is extremely small, and thus also the expected number of "small" variables will be small.
In fact, in uniform distribution over [MIN_INT,MAX_INT], approximately half of the element will be positive - and half negative.
Similarly, only quarter of them will be between 0 to MAX_INT/2 (which is much higher than 1000 as you know).
If you want more "low" values, narrow yourself to smaller range of elements, or use non uniform distribution that is expected to generate more values closer to 0 (gaussian for exmaple).
Have a look at this code snippest:
int count1 = 0, count2=0;
for (int i = 0; i < 10000; i++) {
float x = genFloat(null);
if (x < 1E38 && x > 0) count1++;
if (x > Float.MAX_VALUE - 1E38) count2++;
}
System.out.println(count1);
System.out.println(count2);
It generates 10000 random floats, and checks how much are in [0,1E38], and how much are in [MAX-1E38,MAX]
Note that when talking about floats, the theoretical probability of each is ~1/(2*MAX) ~= 14.7%.
And as you can see, both "close to 0" and "close to MAX" in the same range has similar empirical number of variables produced in their ranges.

Generate even numbers 1-4 using Math.random()?

I would like to generate the numbers 1-4 (whole integers) using Math.random. I have only succeeded in getting doubles or large doubles, and cannot figure out how to set a limit on the minimum and maximum.
Math.random(); = something between 0-1 as a double?
I have seen some people suggest something like this: num = Math.random() * 60 + 25; but have no idea what that does, or how it works.
I am not sure if this is a true question, and feel free to let me know if I should delete it.
Edit: Is there a way to not get the numbers to repeat, yet still be random every time the program is run?
int rand = (Math.random() * 4) + 1;
Math.Random is redundant here, use the Random class.
Random rand = new Random();
rand.nextInt(4)+1; //starts at 0, so add 1
Import this class by:
import java.util.*; or import java.util.Random;
the random number in math gives you a decimal number between zero and one.
you need to tell it to be within a certain range.
something like:
(4*Math.random())+1 should give you between 1-4 I think. correct me if I am wrong anyone.
Random rand = new Random();
System.out.println(rand.nextInt(4) + 1); // we add 1 because it starts with 0
If you really have to use Math.random you need to multiply (and add).
It's quite basic math, Math.random()
Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0.
So multiplying it with X will give a number greater than or equal to 0.0 and less than X. Cast that to an int to get rid of decimals and if you only want even numbers you can do a few things, the easiest probably being int even = (notSureIfEven >> 1) << 1;. [I'm kind of assuming that with 'even' numbers you actually meant 'whole' numbers though, in which case you can ignore that.] Then if you don't want the range to be 0->X but Y->X you just add Y to your outcome (make sure to subtract Y from X before the multiplication or your range will be Y->X+Y).
To not generate the same number twice you can do different things. One way is to store all the numbers you generated so far in a List and then when you generate a new number, check if the list contains that number already, if so generate a new one until you got one that isn't in the list (and then when you do obviously add that to the list). Another way could be to preload all numbers it could generate into a list and then remove a random number out of that list.
Both ways probably won't scale very well to really large ranges of numbers though. The first one since it might get in a very long loop trying to find a number it hadn't generated yet, the second one because you'll have to create a really large list at the start.
I'm not sure if there's something you could do in the case of a really large range.

What is the randomness of Java.nextFloat()

Specifically, if used in the form of:
Random.nextFloat() * N;
can I expect a highly randomized distribution of values from 0 to N?
Would it be better to do something like this?
Random.nextInt(N) * Random.nextFloat();
A single random number from a good generator--and java.util.Random is a good one--will be evenly distributed across the range... it will have a mean and median value of 0.5*N. 1/4 of the numbers will be less than 0.25*N and 1/4 of the numbers will be larger than 0.75*N, etc.
If you then multiply this by another random number generator (whose mean value is 0.5), you will end up with a random number with a mean value of 0.25*N and a median value of 0.187*N... So half your numbers are less than 0.187*N! 1/4 of the numbers will be under .0677*N! And only 1/4 of the numbers will be over 0.382*N. (Numbers obtained experimentally by looking at 1,000,000 random numbers generated as the product of two other random numbers, and analyzing them.)
This is probably not what you want.
At first, Random in Java doesn't contain rand() method. See docs. I think you thought about Random.next() method.
Due to your question, documentation says that nextFloat() is implemented like this:
public float nextFloat() {
return next(24) / ((float)(1 << 24));
}
So you don't need to use anything else.
Random#nextFloat() will give you an evenly distributed number between 0 and 1.
If you take an even distribution and multiply it by N, you scale the distribution up evenly. So you get a random number between 0 and N evenly distributed.
If you multiply this by a random number between 0 and N, then you'll get an uneven distribution. If multiplying by N gives you an even distribution between 0 and N, then multiplying by a number between 0 and N, must give you an answer that is less or equal to if you just multiplied by N. So your numbers on average are smaller.

Categories