如果你看一下oezis algorithm,一个缺点就很明显了:它会花费大量时间来汇总已知无效的数字。 (例如,如果 1 + 2 已经太大,那么尝试 1 + 2 + 3、1 + 2 + 3 + 4、1 + 2 + 3 + 4 + 5、... 也没有任何意义.)
因此我写了一个改进的版本。它不使用一点魔法,它使一切都是手动的。一个缺点是,它需要对输入值进行排序(使用rsort)。但这应该不是什么大问题;)
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;
}
oezis 测试程序的修改版本(见末尾)输出:
possibilities: 540
took: 3.0897309780121
所以执行只需要 3.1 秒,而 oezis 代码在我的机器上执行 65 秒(是的,我的机器很慢)。这比 快 20 倍!
此外,您可能会注意到,我的代码找到了540 而不是338 的可能性。这是因为我调整了测试程序以使用整数而不是浮点数。 直接浮点比较很少是正确的做法,这是一个很好的例子:您有时会得到59.959999999999 而不是59.96,因此不会计算匹配。所以,如果我用整数运行 oezis 代码,它也会找到 540 种可能性;)
测试程序:
// 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);