简体   繁体   中英

Bell curve with left or right bias

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

在此处输入图像描述

  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.

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.

    三角指数

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

    压扁高斯

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM