If you look at the oezis algorithm , one of the drawbacks immediately becomes clear: it spends a lot of time summing up numbers that are not known to work. (For example, if 1 + 2 is already too large, it makes no sense to try 1 + 2 + 3, 1 + 2 + 3 + 4, 1 + 2 + 3 + 4 + 5, ..., too.)
So I wrote an improved version. He does not use bit magic, he does everything manually. The downside is that it requires sorting the input values โโ(use rsort ). But this should not be a big problem;)
function array_sum_parts($vals, $sum){ $solutions = array(); $pos = array(0 => count($vals) - 1); $lastPosIndex = 0; $currentPos = $pos[0]; $currentSum = 0; while (true) { $currentSum += $vals[$currentPos]; if ($currentSum < $sum && $currentPos != 0) { $pos[++$lastPosIndex] = --$currentPos; } else { if ($currentSum == $sum) { $solutions[] = array_slice($pos, 0, $lastPosIndex + 1); } if ($lastPosIndex == 0) { break; } $currentSum -= $vals[$currentPos] + $vals[1 + $currentPos = --$pos[--$lastPosIndex]]; } } return $solutions; }
Modified version of oezis testing program (see end):
possibilities: 540 took: 3.0897309780121
Thus, it took only 3.1 seconds to execute, while the oezis code ran 65 seconds on my machine (yes, my machine is very slow). It is over 20 times faster!
Also, you may notice that my code found 540 instead of 338 possibilities. This is because I configured the testing program to use integers instead of floats. Direct comparisons with floating point are rarely correct, this is a great example: you sometimes get 59.959999999999 instead of 59.96 , and therefore a match will not be considered. So, if I run oezis code with integers, it will also find 540 possibilities;)
Testing program:
// Inputs $n = array(); $n[0] = 6.56; $n[1] = 8.99; $n[2] = 1.45; $n[3] = 4.83; $n[4] = 8.16; $n[5] = 2.53; $n[6] = 0.28; $n[7] = 9.37; $n[8] = 0.34; $n[9] = 5.82; $n[10] = 8.24; $n[11] = 4.35; $n[12] = 9.67; $n[13] = 1.69; $n[14] = 5.64; $n[15] = 0.27; $n[16] = 2.73; $n[17] = 1.63; $n[18] = 4.07; $n[19] = 9.04; $n[20] = 6.32; // Convert to Integers foreach ($n as &$num) { $num *= 100; } $sum = 57.96 * 100; // Sort from High to Low rsort($n); // Measure time $start = microtime(true); echo 'possibilities: ', count($result = array_sum_parts($n, $sum)), '<br />'; echo 'took: ', microtime(true) - $start; // Check that the result is correct foreach ($result as $element) { $s = 0; foreach ($element as $i) { $s += $n[$i]; } if ($s != $sum) echo '<br />FAIL!'; } var_dump($result);