I need to generate random numbers with following properties.
Min should be 200
Max should be 20000
Average(mean) is 500.
Optional: 75th percentile to be 5000
Definitely it is not uniform distribution, nor gaussian. I need to give some left skewness.
Java Random probably won't work because it only gives you normal(gaussian) distributions.
What you're probably looking for is an f distribution (see below). You can probably use the distlib library here and choose the f distribution. You can use the random method to get your random number.
Say X is your target variable, lets normalize the range by doing Y=(X-200)/(20000-200). So now you want some Y random variable that takes values in [0,1] with mean (500-200)/(20000-200)=1/66.
You have many options, the most natural one seems to me a Beta distribution, Y ~ Beta(a,b) with a/(a+b) = 1/66 - you have an extra degree of freedom, which you can choose either to fit the last quartile requirement.
After that, you simply return X as Y*(20000-200)+200
To generate a Beta random variable, you can use Apache Commons or see here.
This may not be the answer you're looking for, but the specific case with 3 uniform distributions:
(Ignore the numbers on the left, but it is to scale!)
public int generate() {
if(random(0, 65) == 0) {
// 50-100 percentile
if(random(1, 13) > 3) {
// 50-75 percentile
return random(500, 5000);
} else {
// 75-100 percentile
return random(5000, 20000);
}
} else {
// 0-50 percentile
return random(200, 500);
}
}
How I got the numbers
First, the area under the curve is equal between 200-500 and 500-20000. This means that the height relationship is 300 * leftHeight == 19500 * rightHeight making leftHeight == 65 * rightHeight
This gives us a 1/66 chance to choose right, and a 65/66 chance to choose left.
I then made the same calculation for the 75th percentile, except the ratio was 500-5000 chance == 5000-20000 chance * 10 / 3. Again, this means we have a 10/13 chance to be in 50-75 percentile, and a 3/13 chance to be in 75-100.
Kudos to #Stas - I am using his 'inclusive random' function.
And yes, I realise my numbers are wrong as this method works with discrete numbers, and my calculations were continuous. It would be good if someone could correct my border cases.
You can have a function f working on [0;1] such as
Integral(f(x)dx) on [0;1] = 500
f(0) = 200
f(0.75) = 5000
f(1) = 20000
I guess a function of the form
f(x) = a*exp(x) + b*x + c
could be a solution, you just have to solve the related system.
Then, you do f(uniform_random(0,1)) and there you are !
Your question is vague as there are numerous random distributions with a given minimum, maximum, and mean.
Indeed, one solution among many is to choose max with probability (mean-min)/(max-min) and min otherwise. That is, this solution generates one of only two numbers — the minimum and the maximum.
The following is another solution.
The PERT distribution (or beta-PERT distribution) is designed to take a minimum and maximum and estimated mode. It's a "smoothed-out" version of the triangular distribution, and generating a random variate from that distribution can be implemented as follows:
startpt + (endpt - startpt) *
BetaDist(1.0 + (midpt - startpt) * shape / (endpt - startpt),
1.0 + (endpt - midpt) * shape / (endpt - startpt))
where—
startpt is the minimum,
midpt is the mode (not necessarily average or mean),
endpt is the maximum,
shape is a number 0 or greater, but usually 4, and
BetaDist(X, Y) returns a random variate from the beta distribution with parameters X and Y.
Given a known mean (mean), midpt can be calculated by:
3 * mean / 2 - (startpt + endpt) / 4
Related
I have a plugin that i made for Minecraft. It is used for massive ender dragon fights to divide the EXP fairly among everybody, based on the damage dealt.
It works but there's a hiccup that makes the equation miss experience.
Here's the code:
private void divideExpAmongstPlayersBasedOnDamageDealt(int exp, Map<Player, Double> damageMap) {
double totalDamageDealt = 0.0d;
for (Double value : damageMap.values()) {
totalDamageDealt += value;
}
while (exp > 0 && !damageMap.isEmpty()) {
Player maxHitterInList = Collections.max(damageMap.entrySet(),
Comparator.comparingDouble(Map.Entry::getValue)).getKey();
double damageShare = damageMap.get(maxHitterInList) / totalDamageDealt;
int expShare = (int) Math.round(exp * damageShare);
if (expShare == 0)
break;
if (maxHitterInList.isOnline()) {
sendPlayerMessage(maxHitterInList, damageShare, expShare);
maxHitterInList.giveExp(expShare);
}
exp -= expShare;
damageMap.remove(maxHitterInList);
}
}
It finds the max hitter, awards them their share of EXP and removes them from the list, until there is no more EXP left to give.
The problem is that it misses points, forcing me to add && !damageMap.isEmpty() to the while condition to avoid exceptions.. It prints the percentages accurately always (the sum adds up to 100).
Im thinking it has to do with the Math.round function, although i am in the dark as how to debug this. When i do the calculations by hand they work out.
Example (this literally happened): A mob grants 10 exp. Player1 did 57% of damage, Player2 did 43%. The percentage values get printed correctly, yet Player1 receives 6 EXP and Player2 receives 2?? Last time i checked 0.57 * 10 equals 5.7 = 6 and 0.43 * 10 equals 4.3 = 4.
What am i missing? Does it have to do with the round function or the way doubles work?
Thanks
This is not a rounding issue but you are using the wrong value in your calculation.
You wrote "Last time i checked 0.57 * 10 equals 5.7 = 6 and 0.43 * 10 equals 4.3 = 4" but the formula you use is
int expShare = (int) Math.round(exp * damageShare);
and you update exp for each iteration of the loop
exp -= expShare;
which means for your second player exp is for 4 and not 10 as you assumed in your example so the real calculation is 0.43 * 2 which rounded is 2
So you need to separate between the variable used in the condition for the while and the one used in the calculation.
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 :
70% at 1 deviation from average
95% at 2 deviation from average
99% at 3 deviation from average
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 :
multiply by n : deviation becomes n
divide by 4 : use the fact that you can values further than the deviation (99% at 3 deviation), with that about 99% values will be under the deviation (your n)
use Math.abs because it's symetric with 0 for middle
use Math.min as a final check in case a value is higher than n
Test on 10 000 iterations :
You can apply a function to your random number in order to decrease chance for numbers close to one to appear.
Then you multiply by your (unreachable) maximum number: 4 in this example
int n = 4 * (1 - Math.sqrt(Math.random()))
"Evenly" could mean either "the probability of each successive number decreases by a fixed amount" or "the probability of each successive number decreases by a fixed percentage." For example, if you use a fixed percentage of 50% to randomly choose between 4 numbers:
50% of 100% is 50%, so the probability of the first number is 50%.
50% of 50% is 25%, so the probability of the second number is 25%.'
50% of 25% is 12.5%, so the probability of the third number is 12.5%.
You need the probabilities to add up to 100%, so the probability of the last number (#4) is equal to the probability of the second-to-last number (#3) - i.e. 12.5%.
If you want to decrease by a random (but decreasing) percent each time, you can just generate a random number for the probability that's less than the probability of the previous one - i.e. if the probability of the the first one is 0.5, the probability of the second is 0.0 < p < 0.5. You'd probably want to be a little more sophisticated than this, though, or you risk having tiny percentages for the last couple of items. For example, if you randomly select 0.1 for the second item, then the probability of the third item is a random number on the range of 0.0 < p < 0.1, which is quite small, and it only gets worse from there. You may want to make the probability of consecutive items have both a min and a max (e.g. the probability of the second item is 0.3 < p < 0.5).
Note that the fact that I used < rather than <= is very important. For example, you don't want to have 0.0 <= p <= 0.5, because that would mean that it's possible that the second item would have the same probability as the third item (which you don't want) and it's also possible that the probability of all subsequent items would equal 0.0 (i.e. there'd be a 100% probability of the first number and a 0% chance of any other number, which isn't at all what you want).
The weakness of the latter strategy is that you'd have to adjust one of the probabilities to make them add up to 1.0.
I were solving:
We know the content of the evaporator (content in ml), the percentage of foam or gas lost every day (evap_per_day) and the threshold (threshold) in percentage beyond which the evaporator is no longer useful. All numbers are strictly positive. The program reports the nth day (as an integer) on which the evaporator will be out of use.
My solution with recursion:
if (content > (initialContent / 100) * threshold) {
double postContent = content - (content / 100) * evap_per_day;
iterations++;
return recursiveEvaporator(postContent, evap_per_day, threshold, initialContent, iterations);
}
But then I found more sophisticated solution:
return (int)Math.ceil(Math.log(threshold / 100.0) / Math.log(1.0 - evap_per_day / 100.0));
Could you please explain me how does logarithms work here and why we choose natural logarithm?
First of all you have to obtain a clear image of e, that is the base of the natural logarithm.
e - is constant that represents approximation of (1 + 1/n)^n that we call for when speaking about constant growth
We see that newly appeared "addition" participated in further exponentiation.Roughly speaking: e^x is our income after x, where x is t*r (t-time; r-rate)
ln(y) is a reverse operation, we are aimed to know the time over rate we have to spend waiting for y income.
Bringing back the subject of your question
ln(threshold) - is t*r(time * rate)
ln(1 - evap_per_day) - is a t*r to evoparate 90% !but not initial, again we need ln because 90% is constantly decreasing and we chould include it into account.
We divide a product of ln(threshold) by ln(1 - evap_per_day) to get know the time.
So the correct solution is: (int)Math.ceil(Math.log(threshold / 100.0) / (ln(1.0 - evap_per_day / 100.0))
This is a case of using exponential decay and solving for time
The Exponential decay formula is A = A_o(1 - r)^t where A is the final quantity, A_o is the inital quantity, r is the rate of decay and t is the time.For this question we want to know the number of days until the intial amount is at or below a threshold percentage of the initail amount, evaperating at a cetain percentage per day. We can rewrite the equation as so:
(using the percent values for threshold and evapPerDay to make explaination easier)
A_o(threshold) = A_o( 1 - evapPerDay)^t
simplifies to:
threshold = (1 - evapPerDay)^t
now we use logs to solve for t
log(threshold) = log((1- evapPerDay)^t)
use one of the laws of logs to move the t
log(threshold) = t(log(1-evapPerDay))
solve for t
log(threshold)/log(1-evapPerDay) = t
Use ceiling to round up.
Example 1:
Shop selling beer, available packages are 6 and 10 units per package. Customer inputs 26 and algorithm replies 26, because 26 = 10 + 10 + 6.
Example 2:
Selling spices, available packages are 0.6, 1.5 and 3. Target value = 5. Algorithm returns value 5.1, because it is the nearest greater number than target possible to achieve with packages (3, 1.5, 0.6).
I need a Java method that will suggest that number.
Simmilar algorithm is described in Bin packing problem, but it doesn't suit me.
I tried it and when it returned me the number smaller than target I was runnig it once again with increased target number. But it is not efficient when number of packages is huge.
I need almost the same algorithm, but with the equal or greater nearest number.
Similar question: Find if a number is a possible sum of two or more numbers in a given set - python.
First let's reduce this problem to integers rather than real numbers, otherwise we won't get a fast optimal algorithm out of this. For example, let's multiply all numbers by 100 and then just round it to the next integer. So say we have item sizes x1, ..., xn and target size Y. We want to minimize the value
k1 x1 + ... + kn xn - Y
under the conditions
(1) ki is a non-positive integer for all n ≥ i ≥ 1
(2) k1 x1 + ... + kn xn - Y ≥ 0
One simple algorithm for this would be to ask a series of questions like
Can we achieve k1 x1 + ... + kn xn = Y + 0?
Can we achieve k1 x1 + ... + kn xn = Y + 1?
Can we achieve k1 x1 + ... + kn xn = Y + z?
etc. with increasing z
until we get the answer "Yes". All of these problems are instances of the Knapsack problem with the weights set equal to the values of the items. The good news is that we can solve all those at once, if we can establish an upper bound for z. It's easy to show that there is a solution with z ≤ Y, unless all the xi are larger than Y, in which case the solution is just to pick the smallest xi.
So let's use the pseudopolynomial dynamic programming approach to solve Knapsack: Let f(i,j) be 1 iif we can reach total item size j with the first i items (x1, ..., xi). We have the recurrence
f(0,0) = 1
f(0,j) = 0 for all j > 0
f(i,j) = f(i - 1, j) or f(i - 1, j - x_i) or f(i - 1, j - 2 * x_i) ...
We can solve this DP array in O(n * Y) time and O(Y) space. The result will be the first j ≥ Y with f(n, j) = 1.
There are a few technical details that are left as an exercise to the reader:
How to implement this in Java
How to reconstruct the solution if needed. This can be done in O(n) time using the DP array (but then we need O(n * Y) space to remember the whole thing).
You want to solve the integer programming problem min(ct) s.t. ct >= T, c >= 0 where T is your target weight, and c is a non-negative integer vector specifying how much of each package to purchase, and t is the vector specifying the weight of each package. You can either solve this with dynamic programming as pointed out by another answer, or, if your weights and target weight are too large then you can use general integer programming solvers, which have been highly optimized over the years to give good speed performance in practice.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Generate random number with non-uniform density
I try to identify/create a function ( in Java ) that give me a nonuniform distributed sequence of number.
if I has a function that say function f(x), and x>0 it will give me a random number
from 0 to x.
The function most work with any given x and this below is only a example how I want to have.
But if we say x=100 the function f(x) will return s nonunifrom distributed.
And I want for example say
0 to 20 be approximately 20% of all case.
21 to 50 be approximately 50% of all case.
51 to 70 be approximately 20% of all case.
71 to 100be approximately 10 of all case.
In short somting that give me a number like normal distribution and it peek at 30-40 in this case x is 100.
http://en.wikipedia.org/wiki/Normal_distribution
( I can use a uniform random gen as score if need, and only a function that will transfrom the uniform result to a non-uniform result. )
EDIT
My final solution for this problem is:
/**
* Return a value from [0,1] and mean as 0.3, It give 10% of it is lower
* then 0.1. 5% is higher then 0.8 and 30% is in rang 0.25 to 0.45
*
* #return
*/
public double nextMyGaussian() {
double d = -1000;
while (d < -1.5) {
// RANDOMis Java's normal Random() class.
// The nextGaussian is normal give a value from -5 to +5?
d = RANDOM.nextGaussian() * 1.5;
}
if (d > 3.5d) {
return 1;
}
return ((d + 1.5) / 5);
}
A simple solution would be to generate a first random number between 0 and 9.
0 means the 10 first percents, 1 the ten following percents, etc.
So if you get 0 or 1, you generate a second random number between 0 and 20. If you get 2, 3, 4, 5 or 6, you generate a second random number between 21 and 50, etc.
Could you just write a function that sums a number of random numbers it the 1-X range and takes an average? this will tend to the normal distribution as n increases
See:
Generate random numbers following a normal distribution in C/C++
I hacked something like the below:
class CrudeDistribution {
final int TRIALS = 20;
public int getAverageFromDistribution(int upperLimit) {
return getAverageOfRandomTrials(TRIALS, upperLimit);
}
private int getAverageOfRandomTrials(int trials, int upperLimit) {
double d = 0.0;
for (int i=0; i<trials; i++) {
d +=getRandom(upperLimit);
}
return (int) (d /= trials);
}
private int getRandom(int upperLimit) {
return (int) (Math.random()*upperLimit)+1;
}
}
There are libraries in Commons-Math that can generate distributions based on means and standard deviations (that measure the spread). and in the link some algorithms that do this.
Probably a fun hour of so of hunting to find the relevant 2 liner:
https://commons.apache.org/math/userguide/distribution.html
One solution would be to do a random number between 1-100 and based on the result do another random number in the appropriate range.
1-20 -> 0-20
21-70 -> 21-50
71-90 -> 51-70
91-100 -> 71-100
Hope that makes sense.
You need to create the f(x) first.
Assuming values x are equiprobable, your f(x) is
double f(x){
if(x<=20){
return x;
}else if (x>20 && x<=70){
return (x-20)/50*30+20;
} else if(...
etc
Just generate a bunch, say at least 30, uniform random numbers between 0 and x. Then take the mean of those. The mean will, following the central limit theorem, be a random number from a normal distribution centered around x/2.