In Java I want to generate a random number from 1 to 100 that can be made to be based to either end of the scale whilst still having a chance of a number from the opposite end still being 'rolled'.
To clarify, in a normal situation over a number of rolls of 1 - 100 the average will be 50 , but in some situations I want the average to shift down to say, 25%. But with there still being chance for 100% to be generated.
I have looked at Random.nextGaussian, which does shift the bell curve, but for a low end value this removes high end posibilities.
What formula or combination of formulas should I be using?
Work out the exact form of the distribution that you want.
Calculate the quantile function for that distribution.
Draw a uniform random number in [0, 1) using an appropriate generator.
That is the input to the quantile function.
The resulting numbers will be distributed as you need.
Figured out some easy ways to at least approach a result like this. I sort of failed grasping the math behind this all so I mostly arrived at this with trial and error and when I got something that looked good enough I called it a day.
Found two approaches:
Basically creating the triangular distribution and then adjusting its values with simple exponentiation. It creates a shape that is similar to the bell curve but it's got a sharp spike at the peak rather than a dull one. By changing the exponent the curve approaching the peak can either be concave or convex.
/** * @param min lower bound * @param max upper bound * @param avg mode <min, max> * @param exp convex (0, 0.5> or concave <0.5, 1> or linear (0.5) */ public static double triangleExponential(double min, double max, double avg, double exp) { if (min >= max || avg < min || avg > max) { throw new IllegalArgumentException(); } double pivot = MathUtils.range(avg, min, max, 0, 1); double x = generator.nextUniform(0, 1); double nx; if (x < pivot) { nx = MathUtils.range(x, 0, pivot, 0, 1); nx = Math.pow(nx, exp); nx = MathUtils.range(nx, 0, 1, 0, pivot); } else { nx = MathUtils.range(x, pivot, 1, 0, 1); nx = 1 - Math.pow(nx, exp); nx = MathUtils.range(nx, 0, 1, pivot, 1); } return MathUtils.range(nx, 0, 1, min, max); }
Second approach it to just take the gaussian distribution and squish/stretch it as needed.
/** * @param min lower bound * @param max upper bound * @param avg mode <min, max> * @param exp 'peak sharpness' (0, 2> */ public static double squishedGaussian(double min, double max, double avg, double exp) { if (min >= max || avg < min || avg > max) { throw new IllegalArgumentException(); } double pivot = MathUtils.range(avg, min, max, 0, 1); double uniform = generator.nextUniform(0, 1); double x = generateGaussian(); if (uniform < pivot) { x = Math.pow(x, exp); x = MathUtils.range(x, 0, 1, 0, pivot); } else { x = 1 - (Math.pow(x, exp)); x = MathUtils.range(x, 0, 1, pivot, 1); } return MathUtils.range(x, 0, 1, min, max); } static double generateGaussian() { double gaussianMax = 5; //Its more like 5.4 double rand; do { rand = Math.abs(generator.nextGaussian(0, 1)); } while (rand > gaussianMax); return 1 - MathUtils.range(rand, 0, gaussianMax, 0, 1); }
MathUtils.range
is just a value range remapping function
public static double range(double OldValue, double OldMin, double OldMax, double NewMin, double NewMax) {
return (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin;
}
and the generator
is the RandomDataGenerator
class from org.apache.commons.math3.random
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.