Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate n random numbers whose sum is m and all numbers should be greater than zero

Tags:

java

random

I want to generate 9 non zero random numbers whose sum is 250. I have tried following code it gives me 9 random numbers but some numbers are zero.

 public void n_random()
{
  Random r = new Random();
ArrayList<Integer> load = new ArrayList<Integer>();
    int temp = 0;
    int sum = 0;
    for (int i = 1; i <= 9; i++) {
        if (!(i == 9)) {
            temp = r.nextInt(250 - sum);
            System.out.println("Temp " + (i) + "    " + temp);
            load.add(temp);
            sum += temp;

        } else {
            int last = (250 - sum);
            load.add(last);
            sum += last;
        }
    }

    System.out.println("Random arraylist " + load);
    System.out.println("Sum is "+ sum);

}

Where is my mistake or where i should improve my code or any other solution?

like image 434
swapnil7 Avatar asked Mar 13 '14 13:03

swapnil7


People also ask

Does random random () include 0 and 1?

The Random random() method generates the number between 0.0 and 1.0.

How do you generate a random number between 0 and 1?

Using the random.The random. uniform() function is perfectly suited to generate a random number between the numbers 0 and 1, as it is utilized to return a random floating-point number between two given numbers specified as the parameters for the function.

Does random () include 0?

Math. random() can never generate 0 because it starts with a non-zero seed. Set the seed to zero, the function does not work, or throws an error. A random number generator always returns a value between 0 and 1, but never equal to one or the other.


3 Answers

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));
}
like image 135
assylias Avatar answered Oct 06 '22 15:10

assylias


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
like image 28
ikegami Avatar answered Oct 06 '22 16:10

ikegami


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.

like image 20
Mena Avatar answered Oct 06 '22 15:10

Mena