Generate n random numbers whose sum is m and all numbers should be greater than zero
I would suggest using:
temp = r.nextInt((250 - sum) / (9 - i)) + 1;
That will make sure that:
- each number is strictly positive
- you won't use the full "250 allowance" before reaching the 9th number
However the distribution of the results is probably biased.
Example output:
Random arraylist [18, 28, 22, 19, 3, 53, 37, 49, 21]
Explanation:
(250 - sum)
is the amount left to reach 250, so you don't want to go over that/ (9 - i)
if your sum has reached for example 200 (need 50 more) and you have 5 more to go, make sure the next random number is not more than 10, to leave some room for the next 4 draws+ 1
to prevent 0
An alternative which probably gives a better distribution is to take random numbers and scale them to get to the desired sum. Example implementation:
public static void n_random(int targetSum, int numberOfDraws) {
Random r = new Random();
List<Integer> load = new ArrayList<>();
//random numbers
int sum = 0;
for (int i = 0; i < numberOfDraws; i++) {
int next = r.nextInt(targetSum) + 1;
load.add(next);
sum += next;
}
//scale to the desired target sum
double scale = 1d * targetSum / sum;
sum = 0;
for (int i = 0; i < numberOfDraws; i++) {
load.set(i, (int) (load.get(i) * scale));
sum += load.get(i);
}
//take rounding issues into account
while(sum++ < targetSum) {
int i = r.nextInt(numberOfDraws);
load.set(i, load.get(i) + 1);
}
System.out.println("Random arraylist " + load);
System.out.println("Sum is "+ (sum - 1));
}
Generate n random numbers whose sum is m and all numbers should be greater than zero
The following is basically what you were trying to achieve. Here, it's written in Perl, since I don't know Java well, but it should be easy to translate.
use strict;
use warnings;
use feature qw( say );
use List::Util qw( shuffle );
my $m = 250;
my $n = 9;
my @nums;
while ($n--) {
my $x = int(rand($m-$n))+1; # Gen int in 1..($m-$n) inclusive.
push @nums, $x;
$m -= $x;
}
say join ', ', shuffle @nums; # shuffle reorders if that matters.
The problem with your approach is that you'll end up with a lot of small numbers. Five sample runs with the numbers in ascending order:
- 1, 1, 1, 1, 2, 3, 6, 50, 185
- 1, 1, 1, 1, 2, 3, 4, 13, 224
- 1, 1, 1, 1, 1, 3, 8, 11, 223
- 1, 1, 1, 1, 2, 4, 19, 103, 118
- 2, 2, 9, 11, 11, 19, 19, 68, 109
A better approach might be to take N random numbers, then scale them so their sum reaches M. Implementation:
use strict;
use warnings;
use feature qw( say );
use List::Util qw( sum );
my $m = 250;
my $n = 9;
# Generate $n numbers between 0 (incl) and 1 (excl).
my @nums;
for (1..$n) {
push @nums, rand();
}
# We subtract $n because we'll be adding one to each number later.
my $factor = ($m-$n) / sum(@nums);
for my $i (0..$#nums) {
$nums[$i] = int($nums[$i] * $factor) + 1;
}
# Handle loss of fractional component.
my $fudge = $m - sum(@nums);
for (1..$fudge) {
# Adds one to a random number.
++$nums[rand(@nums)];
}
say join('+', @nums), '=', sum(@nums);
Five sample runs:
32+32+23+42+29+32+29+20+11=250
31+18+25+16+11+41+37+56+15=250
21+15+40+46+22+40+32+1+33=250
34+24+18+29+45+30+19+29+22=250
3+45+20+6+3+25+18+65+65=250
Your code line:
r.nextInt(250 - sum);
... will generate a pseudo-random from 0
(included) to 250 - sum
(excluded).
See API for Random.nextInt
.
I won't try to solve all your problem here, but simply adding 1
to the expression above would guarantee that it never returns 0
.
All remaining adaptations up to you though :)
For instance if 250 - sum - 1
evaluates to negative, then you'll throw an IllegalArgumentException
.