生成具有非均匀分布的随机整数

时间:2017-02-25 12:34:44

标签: java random

我一直试图找到一种方法来生成一个更有可能在范围中间生成数字的数字。给出:

rnum = r.nextInt(5) + 1;//+ 1 to exclude 0

生成1到5之间的完全随机数(如果删除+ 1则为0和4)。我想要做的是产生少于1和5,并产生很多3#s。我试过这个:

int[] classes = {1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5};
System.out.println("Class " +  classes[r.nextInt(15)]);//logs the output

但它产生了这个:(打印语句重复10次)

Class 2
Class 3
Class 1
Class 4
Class 3
Class 4
Class 2
Class 3
Class 2
Class 5

然而,这既不是有效的,也不是一种好方法。此外,因为用于检索数字的随机数发生器是完全随机的而不是关注中心值,因此使输出高于上。 3只出现30%的时间,这太低了。 2也出现了30%的时间,这意味着(在此测试中)它与3生成的概率相同。

那么,如何以更高的概率随机生成一个数字来生成一个范围中间的数字呢?

4 个答案:

答案 0 :(得分:3)

最简单的方法是从包含所需概率的数组开始。

import java.util.Random;

class Main {

    public static int getOneNumber(double[] probs, Random rnd) {
        double r = rnd.nextDouble();
        for (int j = 0; j < probs.length; j++) {
            if (r < probs[j]) {
                return j;
            }
            r -= probs[j];
        }
        throw new RuntimeException("probabilities should sum to 1");
    }


    public static void main(String[] args) {

        // Desired probabilities
        double[] probabilities = {
            0.05, // 0
            0.15, // 1
            0.6, // 2
            0.15, // 3
            0.05 }; // 4

        Random rnd = new Random();
        for (int i = 0; i < 20; i++) {
            System.out.println(getOneNumber(probabilities, rnd));
        }
    }
}

这里的想法如下。我们生成0到1之间的随机数。现在我们检查:它是否低于0.05?如果是,我们返回0 - 并且它将以0.05的概率发生。如果不是,我们检查我们的数字是否介于0.05和0.15之间(从中减去0.05并与0.1比较)。如果是(这种情况发生的概率为0.15-0.05 = 0.1) - 我们得到1.如果不是,我们检查随机数是否介于0.15和0.75之间等。

答案 1 :(得分:1)

最简单的方法是:

生成0到1之间的浮点数。 将值加倍并减去1 把这个数字取为任何权力,例如10。 除以2并加0.5

将结果乘以15

float fVal = Math.power(r.next()*2-1, 10) / 2 + 0.5;
int iVal = Math.floor(fVal * 15);

int[] classes = {1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5};
System.out.println("Class " +  classes[iVal]);//logs the output

这应该让你的概率看起来更像高斯钟形曲线也许你想阅读Normal Distribution

答案 2 :(得分:0)

您可以使用二分法分布生成随机值,并使用this答案中提供的算法:

public static int getBinomial(int n, double p) {
  int x = 0;
  for(int i = 0; i < n; i++) {
    if(Math.random() < p)
      x++;
  }
  return x;
}

像这样调用函数以获得中间值3的峰值:

getBinomial(4,0.5) +1;

这些值大致分布如下:

 1     2    3    4    5
1/16  1/4  3/8  1/4  1/16

答案 3 :(得分:0)

从[0,1]中的随机数开始,然后将其提升到某个正数的幂。权力&lt; 1将向上偏置,即数字将更可能高于[0,1]内的较低值,并且功率> 1。 1会向下偏向。然后使用乘法和加法将数字范围从[0,1]移到所需的范围。

double rnum,bias_factor;
int low,high;
//low, high and bias_factor are the inputs, then :
high = (low + high)/2;
for(int i = 0;i<=10;i++)
    {
        rnum = Math.random();
        rnum = Math.pow(rnum,bias_factor);
        rnum = (low + (high - low)*rnum)+1;
        System.out.println((int)rnum);
    }

声明:

high=(low + high)/2; 

将给定范围的中间值作为范围的上限值。因此,在低偏置因子的情况下,输出偏向于范围的上限值。例如:最初输入为高= 5,低= 1; and bias_factor = 0.4在[1,5]范围内生成更多中间值(3)。我认为这可能会有所帮助: https://gamedev.stackexchange.com/questions/54551/using-random-numbers-with-a-bias