9

do you know a way to split an integer into say... 5 groups. Each group total must be at random but the total of them must equal a fixed number.

for example I have "100" I wanna split this number into

1- 20
2- 3
3- 34
4- 15
5- 18

EDIT: i forgot to say that yes a balance would be a good thing.I suppose this could be done by making a if statement blocking any number above 30 instance.

Sandro Antonucci
  • 1,683
  • 5
  • 29
  • 59

8 Answers8

7

I have a slightly different approach to some of the answers here. I create a loose percentage based on the number of items you want to sum, and then plus or minus 10% on a random basis.

I then do this n-1 times (n is total of iterations), so you have a remainder. The remainder is then the last number, which isn't itself truley random, but it's based off other random numbers.

Works pretty well.

/**
 * Calculate n random numbers that sum y.
 * Function calculates a percentage based on the number
 * required, gives a random number around that number, then
 * deducts the rest from the total for the final number.
 * Final number cannot be truely random, as it's a fixed total,
 * but it will appear random, as it's based on other random
 * values.
 * 
 * @author Mike Griffiths
 * @return Array
 */
private function _random_numbers_sum($num_numbers=3, $total=500)
{
    $numbers = [];

    $loose_pcc = $total / $num_numbers;

    for($i = 1; $i < $num_numbers; $i++) {
        // Random number +/- 10%
        $ten_pcc = $loose_pcc * 0.1;
        $rand_num = mt_rand( ($loose_pcc - $ten_pcc), ($loose_pcc + $ten_pcc) );

        $numbers[] = $rand_num;
    }

    // $numbers now contains 1 less number than it should do, sum 
    // all the numbers and use the difference as final number.
    $numbers_total = array_sum($numbers);

    $numbers[] = $total - $numbers_total;

    return $numbers;
}

This:

$random = $this->_random_numbers_sum();
echo 'Total: '. array_sum($random) ."\n";
print_r($random);

Outputs:

Total: 500
Array
(
    [0] => 167
    [1] => 164
    [2] => 169
)
Mike
  • 8,767
  • 8
  • 49
  • 103
5

Pick 4 random numbers, each around an average of 20 (with distribution of e.g. around 40% of 20, i.e. 8). Add a fifth number such that the total is 100.

In response to several other answers here, in fact the last number cannot be random, because the sum is fixed. As an explanation, in below image, there are only 4 points (smaller ticks) that can be randomly choosen, represented accumulatively with each adding a random number around the mean of all (total/n, 20) to have a sum of 100. The result is 5 spacings, representing the 5 random numbers you are looking for.

only 4 random point between 0 and 100

Remi
  • 20,619
  • 8
  • 57
  • 41
  • @Sandro Antonucci: just adapted my explanation and saw that your example 20,3,34,15,18 does not add up to 100. But this is what you mean, right? (got a downvote on my previous answer and wondered why) – Remi Sep 27 '11 at 13:23
4

Depending on how random you need it to be and how resource rich is the environment you plan to run the script, you might try the following approach.

<?php
set_time_limit(10);

$number_of_groups   = 5;
$sum_to             = 100;

$groups             = array();
$group              = 0;

while(array_sum($groups) != $sum_to)
{
    $groups[$group] = mt_rand(0, $sum_to/mt_rand(1,5));

    if(++$group == $number_of_groups)
    {
        $group  = 0;
    }
}

The example of generated result, will look something like this. Pretty random.

[root@server ~]# php /var/www/dev/test.php
array(5) {
  [0]=>
  int(11)
  [1]=>
  int(2)
  [2]=>
  int(13)
  [3]=>
  int(9)
  [4]=>
  int(65)
}
[root@server ~]# php /var/www/dev/test.php
array(5) {
  [0]=>
  int(9)
  [1]=>
  int(29)
  [2]=>
  int(21)
  [3]=>
  int(27)
  [4]=>
  int(14)
}
[root@server ~]# php /var/www/dev/test.php
array(5) {
  [0]=>
  int(18)
  [1]=>
  int(26)
  [2]=>
  int(2)
  [3]=>
  int(5)
  [4]=>
  int(49)
}
[root@server ~]# php /var/www/dev/test.php
array(5) {
  [0]=>
  int(20)
  [1]=>
  int(25)
  [2]=>
  int(27)
  [3]=>
  int(26)
  [4]=>
  int(2)
}
[root@server ~]# php /var/www/dev/test.php
array(5) {
  [0]=>
  int(9)
  [1]=>
  int(18)
  [2]=>
  int(56)
  [3]=>
  int(12)
  [4]=>
  int(5)
}
[root@server ~]# php /var/www/dev/test.php
array(5) {
  [0]=>
  int(0)
  [1]=>
  int(50)
  [2]=>
  int(25)
  [3]=>
  int(17)
  [4]=>
  int(8)
}
[root@server ~]# php /var/www/dev/test.php
array(5) {
  [0]=>
  int(17)
  [1]=>
  int(43)
  [2]=>
  int(20)
  [3]=>
  int(3)
  [4]=>
  int(17)
}
Gajus
  • 69,002
  • 70
  • 275
  • 438
3
$number = 100;
$numbers = array();
$iteration = 0;
while($number > 0 && $iteration < 5) {
    $sub_number = rand(1,$number);
    if (in_array($sub_number, $numbers)) {
        continue;
    }
    $iteration++;
    $number -= $sub_number;
    $numbers[] = $sub_number;    
} 

if ($number != 0) {
    $numbers[] = $number;
}

print_r($numbers);
Andrej
  • 7,474
  • 1
  • 19
  • 21
  • this looks ok, problem is that if one number is high it will mathematically reduce the total "groups" reaching the total sooner. How can tell the script to "keep trying" a random number until it's lower than 30, for instance? – Sandro Antonucci Sep 02 '11 at 21:27
  • `if($sub_number <= 30){ $iteration++; $number -= $sub_number; $numbers[] = $sub_number; }` adding this works but the last number could become huge – Sandro Antonucci Sep 02 '11 at 21:30
  • It'll blow up if your first number is 95 or higher. 95,1,2,?,?,? if no repeats are allowed, or 99,1,?,?,? if they are. – Marc B Sep 02 '11 at 22:00
2

This should do what you need:

<?php
$tot = 100;
$groups = 5;
$numbers = array();
for($i = 1; $i < $groups; $i++) {
    $num = rand(1, $tot-($groups-$i));
    $tot -= $num;
    $numbers[] = $num;
}
$numbers[] = $tot;

It won't give you a truly balanced distribution, though, since the first numbers will on average be larger.

EdoDodo
  • 8,220
  • 3
  • 24
  • 30
-1

I think the trick to this is to keep setting the ceiling for your random # generator to 100 - currentTotal

dubvfan87
  • 641
  • 5
  • 18
  • This approach won't give you a particularly balanced distribution (the first elements are likely to be much larger than later elements, on average). But to be fair, the OP didn't specify the distribution. – Oliver Charlesworth Sep 02 '11 at 21:02
-1

The solution I found to this problem is a little different but makes makes more sense to me, so in this example I generate an array of numbers that add up to 960. Hope this is helpful.

// the range of the array 
$arry = range(1, 999, 1);
// howmany numbers do you want
$nrresult = 3;
do {
    //select three numbers from the array
    $arry_rand = array_rand ( $arry, $nrresult );
    $arry_fin = array_sum($arry_rand);
    // dont stop till they sum 960
} while ( $arry_fin != 960 );

//to see the results
foreach ($arry_rand as $aryid) {
    echo $arryid . '+ ';
}
mrjimoy_05
  • 3,452
  • 9
  • 58
  • 95
Mircea Sandu
  • 926
  • 7
  • 21
-1

The solution depends on how random you want your values to be, in other words, what random situation you're going to simulate.

To get totally random distribution, you'll have to do 100 polls in which each element will be binded to a group, in symbolic language

foreach i from 1 to n
  group[ random(1,n) ] ++;

For bigger numbers, you could increase the selected group by random(1, n/100) or something like that until the total sum would match the n.

However, you want to get the balance, so I think the best for you would be the normal distribution. Draw 5 gaussian values, which will divide the number (their sum) into 5 parts. Now you need to scale this parts so that their sum would be n and round them, so you got your 5 groups.

Danubian Sailor
  • 1
  • 38
  • 145
  • 223