3

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?

Bell_curve_with_shift

enter image description here

Frakcool
  • 10,915
  • 9
  • 50
  • 89
gavin
  • 305
  • 1
  • 2
  • 12

2 Answers2

4
  1. Work out the exact form of the distribution that you want.

  2. Calculate the quantile function for that distribution.

  3. Draw a uniform random number in [0, 1) using an appropriate generator.

  4. That is the input to the quantile function.

The resulting numbers will be distributed as you need.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • I've only skimmed [this article](https://www.av8n.com/physics/arbitrary-probability.htm), but it looks like it describes a process very similar to this answer in greater detail. – Andy Turner Jan 05 '16 at 08:08
1

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:

  1. 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.

    triangleExp

    /**
     * @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);
    }
    
  2. Second approach it to just take the gaussian distribution and squish/stretch it as needed.

    squishedGaussian

    /**
     * @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

DUDSS
  • 110
  • 1
  • 9