I’m trying to generate an array of integers contains randoms adding up to a particular value. Here’s my code:
private long[] getRandoms(long size , long sum) throws Exception {
double iniSum = 0;
System.out.println("sum = " + sum);
long[] ret = new long[(int) size];
for (int i = 0 ; i < ret.length; i++) {
ret[i] = randomInRange(1, sum);
iniSum += ret[i];
}
double finSum = 0;
for (int i = 0 ; i < ret.length; i++) {
ret[i] = Math.round((sum * ret[i]) / iniSum);
System.out.println("ret[" + i +"] = " + ret[i]);
finSum += ret[i];
}
if (finSum != sum) throw new Exception("Could not find " + size + " numbers adding up to " + sum + " . Final sum = " + finSum);
return ret;
}
private long randomInRange(long min , long max) {
Random rand = new Random();
long ret = rand.nextInt((int) (max - min + 1)) + min;
System.out.println("ret = " + ret);
return ret;
}
However, the results are not accurate, for instance:
Could not find 100 numbers adding up to 4194304 . Final sum =
4194305.0
I think I’m losing accuracy in this bit:
(sum * ret[i]) / iniSum
Can you recommend an alternative algorithm or a fix in my code which can help me achieve this objective?
Each time you scale a value with
ret[i] = Math.round((sum * ret[i]) / iniSum), you will lose some precision, partly from the division operation itself, but mostly from storing the scaled value as an integer. The later situation is similar to proportional electoral systems where a small number of seats must be allocated in proprtion to a larger numbers of votes.Two techniques for mitigating this:
First scale all the values in the list, but keep track of the difference between the ideal scaled value (a real number) and stored scaled value. Use truncation instead of rounding, so that the discrepency will always be positive. If there is a discrepency, you can increment some of the values in order of the difference between the ideal amount and the current stored amount.
Or more simply, you could just set the last value in the list to whatever is outstanding after scaling doing all the others. That does statistically skew the output, however.