java随机百分比

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

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

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

要解决这个问题,请考虑从100’百分比开始并插入分隔线。 我将用10示例:

  %%%%%%%%%% 

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

  %%%%%%%%%%% 

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

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

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

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

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

我们可以certificate这给出了均匀分布。 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)! 产生给定组成的序列。 所以任何特定成分的概率都应该是它应该是的。

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

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

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

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

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

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

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

这个问题被称为单纯forms的统一抽样,维基百科给出了两种算法:

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

另见这些相关问题:

  • 从n维单位单形中随机均匀地采样
  • 生成概率分布

关键是要生成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 set = new TreeSet(); 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 

制作一个数组。 随机丢弃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]); } } } 

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

  1. 从0,… 100生成n-1整数,比如i = 0, to n-2 a[i] i = 0, to n-2
  2. total是这些数字的总和
  3. 对于i = 0, to n-2计算b[i] = floor(100*a[i]/total) i = 0, to n-2
  4. 设置b[n-1] = 100 - (b[0] + ... b[n-2])

那么b就是你得到的百分比数组。

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

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

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

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

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

首先,明显的解决方案

 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]中的随机点,直到其中一个适合圆圈:)

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

想象一下,你有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 

打印

 [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] 

这是我为我正在创建的程序编写的一些代码。 我在尝试解决这个问题时发现了这个问题,所以希望这会对其他人有所帮助。 该设计基于阅读上述@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; }