我需要生成 n 百分比(0到100之间的整数),以便所有 n 数字的总和加起来为100.
如果我只做nextInt()
n 次,每次确保参数为100减去先前累计的总和,那么我的百分比是有偏差的(即第一个生成的数字通常是最大等)。我如何以无偏见的方式做到这一点?
答案 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]/b
中x
取1..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)
答案 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)
准确地说,这取决于您希望样品如何无偏。这是一个粗略的方式,大致会给你一个好的结果。
n-1
个整数,对a[i]
说i = 0, to n-2
。total
为这些数字的总和b[i] = floor(100*a[i]/total)
i = 0, to n-2
b[n-1] = 100 - (b[0] + ... b[n-2])
。然后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;
}