所以,我所拥有的想法是能够将2.00美元分成10个人,并且他们每个人将随机收到$ x.xx的金额。 (N和M将始终限制为2位小数且> 0)
Ex:{0.12,0.24,1.03,0.01,0.2,0.04,0.11,0.18,0.05,0.02}
目前我尝试过:
private static BigDecimal[] randSum(int n, double m)
{
Random rand = new Random();
BigDecimal randNums[] = new BigDecimal[n], sum = new BigDecimal(0).setScale(2);
for (int i = 0; i < randNums.length; i++)
{
randNums[i] = new BigDecimal(rand.nextDouble()).setScale(2, RoundingMode.HALF_EVEN);
sum = sum.add(randNums[i]);
}
for (int i = 0; i < randNums.length; i++)
{
BigDecimal temp1 = randNums[i].divide(sum, 2, RoundingMode.HALF_EVEN);
BigDecimal temp2 = temp1.multiply(new BigDecimal(m).setScale(2));
randNums[i] = temp2;
}
return randNums;
}
public static void main(String[] args)
{
BigDecimal d[] = randSum(5, 2);
double sum = 0;
for (BigDecimal n : d)
{
sum += n.doubleValue();
System.out.println(n);
}
System.out.println("total: " + sum);
}
但BigDecimals太混乱了,他们没有加起来。有时总数是1.98或2.01。由于Double-precision浮点,双打不起作用。
代码取自:
答案 0 :(得分:5)
假设您需要一个固定的精度(作为prec
参数传递):
static public BigDecimal[] split(BigDecimal sum, int prec, int count) {
int s = sum.scaleByPowerOfTen(prec).intValue();
Random r = new Random();
BigDecimal[] result = new BigDecimal[count];
int[] v = new int[count];
for (int i = 0; i < count - 1; i++)
v[i] = r.nextInt(s);
v[count - 1] = s;
Arrays.sort(v);
result[0] = BigDecimal.valueOf(v[0]).scaleByPowerOfTen(-prec);
for (int i = 1; i < count; i++)
result[i] = BigDecimal.valueOf(v[i] - v[i - 1]).scaleByPowerOfTen(-prec);
return result;
}
此方法使用Random.nextInt()
均匀分布的属性。排序后,v[]
数组的值是分割整个金额的点,因此您可以使用相邻元素之间的差异生成结果:
[ 2, 5, 10, 11, ..., 197, 200] // v[]
[0.02, 0.03, 0.05, 0.01, ..., ..., 0.03] // result[]
这里使用整数值进行操作,因此舍入问题不再困扰。
答案 1 :(得分:2)
我建议将所有数字乘以100并重新解释您的问题:生成n
随机非负整数,其总和等于给定的m
整数。之后,您可以将生成的所有数字除以100
,以获得所需内容。这是我的实现(类似于@SashaSalauyou版本):
private static int[] randSum(int n, int min, int m) {
Random rand = new Random();
int[] nums = new int[n];
int max = m - min*n;
if(max <= 0)
throw new IllegalArgumentException();
for(int i=1; i<nums.length; i++) {
nums[i] = rand.nextInt(max);
}
Arrays.sort(nums, 1, nums.length);
for(int i=1; i<nums.length; i++) {
nums[i-1] = nums[i]-nums[i-1]+min;
}
nums[nums.length-1] = max-nums[nums.length-1]+min;
return nums;
}
我还添加了一个参数min
,这是最小号码。如果您在答案中接受零,请将其设置为0
。否则,您可以将其设置为1
(然后在100
除以后,最低可能的数字为0.01
。)
答案 2 :(得分:0)
您可以将此问题视为整数,而不是汇总到M
,将其加到100M
。
做算法,你最终得到非整数,例如10.345
,
现在 - 基本上你想做的是取每个数字的最低值(10 in上面的例子),并将数字增加到11,概率与0.345
成比例。
可以通过创建提醒数组来完成:rem[i] = value[i] - ceil(value[i])
,并根据M - sum{ceil(value[i])}
数组的加权概率选择带有替换的rem
值。
代码:
public static BigDecimal[] createRandomSumsTo(BigDecimal M, int n) {
int m = M.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN).intValue();
double[] rands = new double[n];
double sum = 0;
for (int i = 0; i < n; i++) {
rands[i] = rand.nextDouble();
sum += rands[i];
}
for (int i = 0; i < n; i++) rands[i] = (rands[i] / sum) * m;
int[] intVals = new int[n];
double[] rem = new double[n];
//create base and reminder array:
for (int i =0 ; i < n; i++) {
intVals[i] = (int) Math.floor(rands[i]);
rem[i] = rands[i] - intVals[i];
}
//for efficiently chosing a random value by weight
double[] aux = new double[n+1];
for (int i = 1 ; i < n+1; i++) {
aux[i] = aux[i-1] + rem[i-1];
}
//normalize to sum to one.
for (int i = 0 ; i < n+1; i++) {
aux[i] = aux[i] / aux[n];
}
int intsSum = 0;
for (int x : intVals) {
intsSum += x;
}
for (; intsSum < m; intsSum++) {
intVals[chooseWeighted(aux)]++;
}
//and create the BigDecimal array:
BigDecimal[] res = new BigDecimal[n];
for (int i = 0; i < n; i++) {
res[i] = new BigDecimal(intVals[i]).divide(BigDecimal.TEN).divide(BigDecimal.TEN);
}
return res;
}
private static int chooseWeighted(double[] probabilities) {
double r = rand.nextDouble();
int idx = Arrays.binarySearch(probabilities, r);
if (idx >= 0) return idx-1;
return (-1*idx) -2;
}
答案 3 :(得分:0)
另一种可以调整生成数字“均匀度”的实现。
/**
* Split a positive number into N random numbers.
*
* @param sum the sum of the generated numbers
* @param num the number of numbers
* @param min the minimum number
* @param multiplier a parameter used to adjust the evenness of the generated numbers. The farther it is away from 1
* the more uneven the numbers are.
* @param scale the scale of the generated numbers
* @return an array of BigDecimal objects
*/
public static BigDecimal[] split(double sum, int num, double min, double multiplier, int scale) {
double leftover = sum - num * min;
BigDecimal[] numbers = new BigDecimal[num];
for (int i = 0; i < num - 1; i++) {
double number = min;
double max;
if ((max = leftover / (num - i) * multiplier) <= leftover) {
double rand = RandomUtils.nextDouble(0, max);
number += rand;
leftover -= rand;
}
numbers[i] = BigDecimal.valueOf(number).setScale(scale, RoundingMode.DOWN);
}
numbers[num - 1] = BigDecimal.valueOf(sum)
.subtract(Arrays.stream(numbers).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add))
.setScale(scale, RoundingMode.HALF_UP);
ArrayUtils.shuffle(numbers);
return numbers;
}