0

For example I got this integer 110

Let Nx be an integer

Is it possible to generate N1 to N8 (N1 to N8 is an integer) using PHP that confine the following rules:

N1 + N2 + N3 + N4 + N5 + N6 + N7 + N8 = 110 ?

Got no idea what function (e.g. rand(), mt_rand(), using loops etc.) can PHP do that.

bytecode77
  • 14,163
  • 30
  • 110
  • 141
DarkAmazon
  • 43
  • 8
  • 1
    What are the rules for relationships between N1 and N2, etc.? – BigScar Mar 21 '15 at 14:57
  • Just random numbers, it could be any integers which can be sum up as 110 in the end. – DarkAmazon Mar 21 '15 at 15:05
  • 2
    Then the answer is "yes, you can do that in PHP." rand(), mt_rand() and loops would probably be effective. Try writing some code and see what happens. – BigScar Mar 21 '15 at 15:07
  • 1
    There's an algorithm to do that explained here: http://stackoverflow.com/questions/22380890/generate-n-random-numbers-whose-sum-is-m-and-all-numbers-should-be-greater-than – Phate01 Mar 21 '15 at 15:45
  • Do you want any constraints such as no duplicates? Also, must it be exactly 8 numbers or is it up to 8 numbers? And do you mean integers or positive integers? – Elin Mar 21 '15 at 17:39

2 Answers2

0

the shortest solution for your problem that i can think of would be:

$m = 110;  // desired sum
$v = 8;    // desired elements
$x = [];   // result

for($i = 1; $i < $v; $i++)
    $x[$i] = rand(1, $m - array_sum($x) - (($v - $i) * $v));
$x[0] = $m-array_sum($x);

// print results
print_r($x);
printf("Elements sum to: %d\n",array_sum($x));

which results in

Array
(
    [1] => 16
    [2] => 13
    [3] => 15
    [4] => 23
    [5] => 5
    [6] => 7
    [7] => 1
    [0] => 30
)
Elements sum to: 110

so hurray, just what you wanted, although it could be optimized for nicer spread. but let me explain line for line.

first i just initialized three variables. $m is the desired sum you want to have for all of your elements. $v is the number of elements (N's) you want to generate. and $x holds an array with all N's that where calculated, so the result we are interested in:

$m = 110;  // desired sum
$v = 8;    // desired elements
$x = [];   // result

now we start a loop through the elements we want to calculate. notice we start with 1, because we will be using 0 for calculating last value later on:

for($i = 1; $i < $v; $i++)

and here now comes the "brainwork" we are setting the current element $x[$i] to a random value. in PHP we can pass min and max values to the random function, so i chose 1 as min to avoid 0's and the max i chose i shall explain a bit more detailed at the end, but obviously we want to avoid having 110 as first value and the rest zeros and this is how we do so:

    $x[$i] = rand(1, $m - array_sum($x) - (($v - $i) * $v));

in the last step now we need to make sure that we will end up with the desired sum, so instead of going random, i simply set the element to the desired maximum sum minus the sum of all existing elements:

$x[0]=$m-array_sum($x);

the last rows of the code just print out the results, but as promised, let me explain the "complicated" max part of the random function that i chose.

    $m - array_sum($x) - (($v - $i) * $v)

the one PHP function that comes really handy here is the array_sum function, which adds up all elements of an array. if we would just call rand(1,110) on every element, then we would could easily end up with a sum higher than 110, so we subtract the sum of all existing elements from the max value to avoid this on first hand: $m - array_sum($x). so if last three generated elements add up to 43 then next element can be no higher than 67 (110 - 43). we still could end up with a result like (12,7,24,5,62,0,0,0) and we don't wan't zeros, so we will have to subtract the number of elements left $v - $i which will avoid the zeros.

again we still could end up with a result like (12,7,24,5,59,1,1,1) and that does not look nice, so last optimization i did was to multiply this by an arbitrary number to leave more space for higher values - in this case i just used the $v (the number of elements) as arbitrary number, but you can play around with this value i.e. (($v - $i) * 12)) until you get desired result.

hexerei software
  • 3,100
  • 2
  • 15
  • 19
  • That really is not a set of random numbers. That is sort of a set of random numbers padded with 1s. – Elin Mar 21 '15 at 17:36
  • sure it is! if you test it you will see... i just gave the sample with the 1s to show what would happen if you avoid "softening" the max value. this could be perfected even more, but actually from my test runs i had no more than 7 ones per 10 iterations - the only non-random value is the last one, which in my case never will be '1' but since it is max minus a sum of random values it is random enough - so please test code before commenting! – hexerei software Mar 21 '15 at 17:49
  • What is the probablity of getting 7 1s by chance alone if you are picking 8random numbers between 1 and 100? – Elin Mar 21 '15 at 17:53
  • the probability is given by PHPs rand() function, so if you keep the last multiplier low, yes you can have a result like (103,1,1,1,1,1,1,1), the multiplier reduces this effect and spreads the random numbers more over the range so you have "nicer" results, if this is not desired then leave multiplier out so it is just `$m - array_sum($x) - ($v-$i)' and if you set min to zero then you can set max just to `$m - array_sum($x)` and you will have "real" non-optimized random numbers with zeros... my implementation is random, but with cosmetics – hexerei software Mar 21 '15 at 18:00
  • No the rand function (which has well known issues) gives you random numbers. You must use a probability function to answer my question. If you think 7 ones in a row is likely to happen by chance I'd like to play a dice game with you. Random numbers are about each value being equally likely not about being nice. – Elin Mar 21 '15 at 18:05
  • you defiantly are right, if focus lies on random numbers any standard rand() function in any language will fail.. one will have to go for a more complex implementation. as i got the original question though, it seemed to me the questionnaire is a newer to PHP - so my focus was on simple and explaining in detail what i did - and i did mention and admit, it can be optimized a lot - depending on what you need. with my solution the results look nice, work and i will not have seven ones in a row. it can be optimized - but most likely not in three lines of code :D – hexerei software Mar 21 '15 at 18:30
0

Thanks a lot! Almost the same as what I wanted.

$m = 110;  // desired sum
$v = 8;    // desired elements
$x = [];   // result

for($i = 1; $i < $v; $i++)
    $x[$i] = mt_rand(1, $m - array_sum($x) - (($v - $i) * $v));
$x[] = $m-array_sum($x);

// print results
print_r($x);
printf("Elements sum to: %d\n",array_sum($x));

I've changed rand() into mt_rand() which might rand things faster
And $x[0] = $m-array_sum($x); => $x[] = $m-array_sum($x);

So that the Array can start from 1 and ends at 8

Array
(
  [1] => 16
  [2] => 13
  [3] => 15
  [4] => 23
  [5] => 5
  [6] => 7
  [7] => 1
  [8] => 30
)
Elements sum to: 110
DarkAmazon
  • 43
  • 8