【问题标题】:Generating a random integer with non-uniform distribution生成具有非均匀分布的随机整数
【发布时间】:2017-07-16 07:36:34
【问题描述】:

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

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

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

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 具有相同的概率。

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

【问题讨论】:

  • 10 次是一个太小的样本,无法推断出准确的概率并获得所需的分布。你的做法很好。
  • 补充@JBNizet 说的。在 for 循环中添加打印并以编程方式计算输出以计算百分比以获得更好的站点。我建议 1000 次循环迭代。
  • 您可能需要考虑将binomial distribution 作为随机变量。
  • 具有更具代表性的样本:ideone.com/uFhzKA
  • 你这里有些误会。 一个特定样本中的概率与频率无关。与样本中有多少条目无关(即使可能性极小),样本仍有可能仅包含 1 个。

标签: java random


【解决方案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

【讨论】:

  • 相当肯定 Java Random 有 nextGaussian(),它返回一个“双值,从(大约)平均为 0.0 和标准差为 1.0 的通常正态分布中选择”
  • 可能,但这不会导致人们理解他试图完成的事情。
  • r.next() 如此处所述需要输入。 Math.power 是一种无法识别的方法,建议使用 Math.pow,它需要双输入
【解决方案2】:

您可以使用二项分布和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】:

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

    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 之间,等等。

    【讨论】:

      【解决方案4】:

      从 [0, 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; 
      

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

      【讨论】:

        猜你喜欢
        • 2014-02-04
        • 2011-08-23
        • 2011-05-31
        • 1970-01-01
        • 1970-01-01
        • 2013-12-09
        • 2013-07-22
        • 2015-04-04
        • 1970-01-01
        相关资源
        最近更新 更多