java随机百分比

时间:2010-06-09 16:51:45

标签: java algorithm math random

我需要生成 n 百分比(0到100之间的整数),以便所有 n 数字的总和加起来为100.

如果我只做nextInt() n 次,每次确保参数为100减去先前累计的总和,那么我的百分比是有偏差的(即第一个生成的数字通常是最大等)。我如何以无偏见的方式做到这一点?

12 个答案:

答案 0 :(得分:12)

一些答案​​建议选择随机百分比并考虑它们之间的差异。正如Nikita Ryback所指出的那样,这不会给所有可能性带来均匀分布;特别是,零比预期的频率低。

要解决此问题,请考虑从100'百分比开始并插入分隔符。我将以10:

显示一个示例
 % % % % % % % % % % 

我们可以在11个地方插入分隔符:在任意两个百分点之间或在开头或结尾处。所以插入一个:

 % % % % / % % % % % % 

这表示选择四个和六个。现在插入另一个分隔符。这一次,有十二个地方,因为已经插入的分隔符创建了额外的一个。特别是,有两种方法可以获得

 % % % % / / % % % % % % 

在前一个分隔符之前或之后插入。你可以继续这个过程,直到你需要尽可能多的分频器(比百分数少一个。)

 % % / % / % / / % % % / % % % / 

这相当于2,1,1,0,3,3,0。

我们可以证明这给出了均匀分布。 100到k部分的组成数是二项式系数100 + k-1选择k-1。那是 (100 + k-1)(100 + k-2) ... 101 /(k-1)(k-2)* ... * 2 * 1 因此,选择任何特定成分的概率是这个的倒数。当我们一次插入一个分频器时,首先我们选择101个位置,然后选择102,103等,直到我们达到100 + k-1。因此任何特定插入序列的概率为1 /(100 + k-1)* ... * 101。多少插入序列产生相同的组合物?最终的组合物含有k-1分隔物。它们可以按任何顺序插入,所以有(k-1)!产生给定组成的序列。所以任何特定成分的概率都应该是它应该是的。

在实际代码中,您可能不会代表这样的步骤。你应该能够坚持数字,而不是百分比和分频器的序列。我没有想过这个算法的复杂性。

答案 1 :(得分:7)

生成任意范围的 n 随机整数(称之为a[1] .. a[n])。总结你的整数并称之为b。您的百​​分比为[a[1]/b, ..., a[n]/b]

编辑:好点,将结果四舍五入到总计100是非繁琐的。一种方法是在a[x]/bx1..n作为整数,然后随机分配剩余单位100-(sum of integers)。我不确定这是否会对结果产生任何偏见。

答案 2 :(得分:6)

你可能需要通过“有偏见”来定义你真正的意思 - 但如果你关心的是数字的分布与它们的位置无关,那么你可以简单地以“有偏见”的方式创建数字,然后将他们的职位随机化。

另一种“无偏见”的方法是创建n-1个随机百分比,对它们进行排序(称之为x1 x2 x3 ......),然后将最终的百分比定义为:

x1
x2 - x1
x3 - x2
...
100 - x(n-1)

这样你就会得到n个随机数,加上100个。

答案 3 :(得分:5)

这个问题被称为单纯形式的统一抽样,维基百科提供了两种算法:

http://en.wikipedia.org/wiki/Simplex#Random_sampling

另见这些相关问题:

答案 4 :(得分:3)

关键是要生成0到100之间的N个随机数,但要将它们用作“标记”而不是输出的最后一个数字序列。然后按升序迭代标记列表,计算输出的每个百分比(当前标记 - 上一个标记)。

这样可以提供比一次只生成和输出每个数字更均匀的分布。

示例

import java.util.Random;
import java.util.TreeSet;
import java.util.SortedSet;

public class Main {
  public static void main(String[] args) {
    Random rnd = new Random();
    SortedSet<Integer> set = new TreeSet<Integer>();

    for (int i=0; i<9; ++i) {
      set.add(rnd.nextInt(101));
    }

    if (set.last() < 100) {
      set.add(100);
    }    

    int prev = 0;
    int total = 0;    
    int output;

    for (int j : set) {
      output = j - prev;
      total += output;
      System.err.println(String.format("Value: %d, Output: %d, Total So Far: %d", j, output, total));
      prev = j;
    }
  }
}

<强>输出

$ java Main
Value: 0, Output: 0, Total So Far: 0
Value: 2, Output: 2, Total So Far: 2
Value: 55, Output: 53, Total So Far: 55
Value: 56, Output: 1, Total So Far: 56
Value: 57, Output: 1, Total So Far: 57
Value: 69, Output: 12, Total So Far: 69
Value: 71, Output: 2, Total So Far: 71
Value: 80, Output: 9, Total So Far: 80
Value: 92, Output: 12, Total So Far: 92
Value: 100, Output: 8, Total So Far: 100

答案 5 :(得分:3)

制作一个数组。随机丢弃100%的数据到该数组的每个部分。 示例显示n = 7。

import java.util.Random;

public class random100 {
    public static void main (String [] args) {
        Random rnd = new Random();
            int percents[] = new int[7];
            for (int i = 0; i < 100; i++) {
                int bucket = rnd.nextInt(7);
                percents[bucket] = percents[bucket] + 1;
            }
        for (int i = 0; i < 7; i++) {
            System.out.println("bucket " + i + ": " + percents[i]);
        }

    }

}

答案 6 :(得分:2)

准确地说,这取决于您希望样品如何无偏。这是一个粗略的方式,大致会给你一个好的结果。

  1. 从0,... 100生成n-1个整数,对a[i]i = 0, to n-2
  2. total为这些数字的总和
  3. b[i] = floor(100*a[i]/total)
  4. 计算i = 0, to n-2
  5. 设置b[n-1] = 100 - (b[0] + ... b[n-2])
  6. 然后b是你得到的百分比数组。

    最后一个会有偏见,但其余的应该是统一的。

    当然,如果你想以更准确的方式做到这一点,你将不得不使用Gibbs采样或Metropolis hastings。

答案 7 :(得分:0)

使用您描述的方法选择数字后,按顺序排列数字。这样,最终的数字列表具有更均匀的分布。

然而,请注意,无论你做什么,你都无法获得完美均匀的分布,因为一旦你开始选择数字,你的随机试验就不是独立的了。见ataylor的答案。

另请注意,您描述的算法可能无法为您提供所需的输出。最后一个数字不能是随机的,因为它必须使总和等于100.

答案 8 :(得分:0)

首先,明显的解决方案。

do
    int[] a = new int[n];
    for (int i = 0; i < n; ++i) {
        a[i] = random number between 0 and 100;
    }
until sum(a) == 100;

在复杂性方面并不完美(达到和100的迭代次数可能非常大),但分布肯定是“无偏见的”。

修改
类似的问题:如何在半径为1且以(0,0)为中心的圆中生成随机点?解决方案:继续生成范围(正方形)[-1..1,-1..1]中的随机点,直到其中一个符合圆圈:)

答案 9 :(得分:0)

我有一个类似的问题,最后按照你的说法做了,产生随机整数,直到现有整数和极限之和的差值。然后我随机化了整数的顺序。它工作得很好。这是一种遗传算法。

答案 10 :(得分:0)

想象一下,你有100块石头和N个水桶放置它们。你可以把所有100块放在一个随机的桶里。通过这种方式,总数将是您开始使用的100,并且任何存储桶之间都没有偏差。

public static int[] randomBuckets(int total, int n_buckets) {
    int[] buckets = new int[n_buckets];
    Random rand = new Random();
    for(int i=0;i<total;i++)
        buckets[rand.nextInt(n_buckets)]++;
    return buckets;
}

public static void main(String... args) {
    for(int i=2; i<=10;i++)
        System.out.println(Arrays.toString(randomBuckets(100, i)));
}

打印

[55, 45]
[38, 34, 28]
[22, 21, 32, 25]
[28, 24, 18, 15, 15]
[17, 14, 13, 21, 18, 17]
[17, 19, 14, 15, 6, 15, 14]
[11, 14, 14, 14, 4, 17, 9, 17]
[13, 12, 15, 12, 8, 10, 9, 11, 10]
[11, 13, 12, 6, 6, 11, 13, 3, 15, 10]

随着计数的增加,分布趋于均匀。

System.out.println(Arrays.toString(randomBuckets(100000000, 100)));

打印

[1000076, 1000612, 999600, 999480, 998226, 998303, 1000528, 1000450, 999529, 
998480, 998903, 1002685, 999230, 1000631, 1001171, 997757, 1000349, 1000527, 
1002408, 1000852, 1000450, 999318, 999453, 1000099, 1000759, 1000426, 999404, 
1000758, 1000939, 999950, 1000493, 1001396, 1001007, 999258, 1001709, 1000593,
1000614, 1000667, 1000168, 999448, 999350, 1000479, 999991, 999778, 1000513, 
998812, 1001295, 999314, 1000738, 1000211, 999855, 999349, 999842, 999635, 
999301, 1001707, 998224, 1000577, 999405, 998760, 1000036, 1000110, 1002471, 
1000234, 1000975, 998688, 999434, 999660, 1001741, 999834, 998855, 1001009, 
999523, 1000207, 998885, 999598, 998375, 1000319, 1000660, 1001727, 1000546, 
1000438, 999815, 998121, 1001128, 1000191, 998609, 998535, 999617, 1001895, 
999230, 998968, 999844, 999392, 999669, 999407, 998380, 1000732, 998778, 1000522]

答案 11 :(得分:0)

这是我为我正在创建的程序编写的一些代码。我试图解决这个问题时找到了这个线程,所以希望这会对其他人有所帮助。该设计基于阅读上述@eruonna的回应。

public static int[] randomNumbers(int numOfNumbers){

    int percentN = numOfNumbers;

    int[] intArray = new int[101];

    //set up the array with values
    for(int i = 0; i < intArray.length; i++){
        intArray[i] = i;
    }

    //set up an array to hold the selected values
    int[] selectionArray = new int[(percentN - 1)];

    //run a for loop to go through and select random numbers from the intArray
    for(int n = 0; n < selectionArray.length; n++){
        int randomNum = (int)(Math.random() * 100);
        selectionArray[n] = intArray[randomNum];
    }

    //bubble sort the items in the selectionArray
    for(int out = (selectionArray.length - 1); out > 1; out--){
        for(int in = 0; in < out; in++){
            if(selectionArray[in] > selectionArray[in + 1]){
                int temp = selectionArray[in];
                selectionArray[in] = selectionArray[in + 1];
                selectionArray[in + 1] = temp;
            }
        }
    }

    //create an array to hold the calculated differences between each of the values to create random numbers
    int[] calculationArray = new int[percentN];

    //calculate the difference between the first item in the array and 0
    calculationArray[0] = (selectionArray[0] - 0);

    //calculate the difference between the other items in the array (except for the last value)
    for(int z = 1; z < (calculationArray.length - 1); z++){
        calculationArray[z] = (selectionArray[z] - selectionArray[z - 1]);
    }

    //calculate the difference for the last item in the array
    calculationArray[(calculationArray.length - 1)] = (100 - selectionArray[(selectionArray.length - 1)]);

    return calculationArray;

}