如何在php中找到总和等于X数的数组中的最佳子集

时间:2012-10-17 10:30:39

标签: php algorithm combinations

我有一个数组

$array = array(3,5,6,10,15,30);
$sum = 69;

然后有3个不同的总和等于69:

3+5+6+10+15+30,
3+6+10+20+30,
3+6+30+30,

最好的是3 + 6 + 30 + 30。因为它包含数组中较高的数字以完成减少数量计数的总和。

(一个数字可以在一个总和中使用,就像它在列表中出现的次数一样,并且一个数字计为总和。) 这是我正在实施的代码

$sum = 18;
$list = array();
$this->sumUpRecursive(array(3,5,6,10,15,30), $list);  //function call
$list = array_filter($list,function($var) use ($sum) { return(array_sum($var) == $sum);});
var_dump($list);

function sumUpRecursive($array, &$list, $temp = array()) {
      if (count($temp) > 0 && !in_array($temp, $list))
        $list[] = $temp;
      for ($i = 0; $i < sizeof($array); $i++) {
        $copy = $array;
        $elem = array_splice($copy, $i, 1);
        if (sizeof($copy) > 0) {
          $add = array_merge($temp, array($elem[0]));
          sort($add);
          $this->sumUpRecursive($copy, $list, $add);
        }
        else {
          $add = array_merge($temp, array($elem[0]));
          sort($add);
          if (!in_array($temp, $list)) {
            $list[] = $add;
          }
        }
      }
    }

结果:

Array
    (
[9] => Array
    (
        [0] => 3
        [1] => 5
        [2] => 10
    )

[28] => Array
    (
        [0] => 3
        [1] => 15
    )

我希望这可能有点复杂。它从阵列中取一个数字。但是如何找出69 ......

由于

1 个答案:

答案 0 :(得分:1)

这应该适合你:

此代码的作用是什么(函数getKnapsackSum())?

1。准备数据

在函数的前几行中,我准备数据,这意味着我使用rsort()对数组DESC进行排序。我使用end()获取列表的最后一个元素,使用rest()重置数组指针并将其分配给临时变量。

2。输入检查

在开始计算总和之前,我会检查输入是否为0。

3。计算总和

在此之后真正的事情发生了(IT伏都教,不是真的)!我开始的循环是假的,只要我没有得到总和。 while循环中接下来的2行是检查,首先如果无法计算总和,它返回一个数字为0和秒,当我们得到数组指针的“偏移量”时,我设置它支持最后一个数组元素。

然后第一个if子句用于检查它是否出错并且必须以较低的值尝试它。举个例子:

list = [3, 5, 6]
sum = 8

                        //entire sum
                        | 0
                        |
check 6:                |
                        |
      6 + sum  -> 8     | 6               | fits
      6 + sum  -> 8     |                 | does not fit
                        |
check 5:                |
                        |
      5 + sum  -> 8     |                 | does not fit
                        |
check 3:                |
                        |
      3 + sum  -> 8     |                 | does not fit

--------------------------------------------------------
                          = sum = [6] != 8

正如你在这个例子中看到的那样,算法犯了一个错误,因为它开始用6构建总和,但在此之后它无法构建它直到结束,现在这里是第一个if子句检查的地方,如果数组指针的当前元素是数组的末尾,并且此值+当前总和高于目标。

如果是这种情况,它将从sum数组中删除最后一个值,并弹出列表的最高元素。所以现在它再次尝试使用以下数据:

list = [3, 5]
          //^ As you can see it removed the highest value of the list 
sum = 8

                        //entire sum
                        | 0 <- it removed the last value of the sum array, here 6
                        |
check 5:                |
                        |
      5 + sum  -> 8     | 5               | fits
      5 + sum  -> 8     |                 | does not fit
                        |
check 3:                |
                        |
      3 + sum  -> 8     | 3               | fits
      3 + sum  -> 8     |                 | does not fit

--------------------------------------------------------
                          = sum = [5, 3] == 8

因此,您现在可以看到第一个if子句使得可以使用列表构建总和。所以if语句确保它纠正了1级的错误。

现在我的while循环中的第二个if语句纠正了2级错误。那么2级错误是什么?我们再看一个例子:

list = [3, 5, 6]
sum = 22

                        //entire sum
                        | 0
                        |
check 6:                |
                        |
      6 + sum  -> 22    | 6               | fits
      6 + sum  -> 22    | 6               | fits
      6 + sum  -> 22    | 6               | fits
      6 + sum  -> 22    |                 | does not fit
                        |
check 5:                |
                        |
      5 + sum  -> 22    |                 | does not fit
                        |
check 3:                |
                        |
      3 + sum  -> 22    | 3               | fits
      3 + sum  -> 22    |                 | does not fit

--------------------------------------------------------
                          = sum = [6, 6, 6, 3] != 22

所以你可以看到它无法完全建立总和。但这次它不是1级的错误,因为如果我们从最后一个元素中取出最后一个元素并通过我们删除最高元素的新列表,它仍然无法构建总和,如此处所示:

纠正错误级别1:

list = [3, 5]
          //^ As you can see it removed the highest value of the list 
sum = 22

                        //entire sum
                        | [6, 6, 6] <- it removed the last value of the sum array, here 3
                        |
check 5:                |
                        |
      5 + sum  -> 22    |                | does not fit
                        |
check 3:                |
                        |
      3 + sum  -> 22    | 3              | fits
      3 + sum  -> 22    |                | does not fit
                        |
--------------------------------------------------------
                          = sum = [6, 6, 6, 3] != 22

因为你可以看到它现在被卡住了,它只能用第一个if子句来纠正错误。这里是第二个if语句检查列表是否为空的情况,如果是这种情况意味着您尝试了每个值,但错误在sum数组中更高。

所以现在它返回sum数组的1个数组元素并删除它。它也将错误作为偏移来获得没有错误的数字的列表。所以这里:[6, 6, 6, 3] 3从第一个if子句中删除,所以现在数组看起来像这样:[6, 6, 6]。现在第二个if语句看到错误是6.它删除了它并且还纠正了从中选择数字的列表。

现在,结果数组看起来像是:[6, 6]以及用于选择数字的列表,如下所示:[5, 3]。现在,它对2级进行了修正,它可以构建总和,如下所示:

list = [3, 5]
          //^ As you can see it removed the highest value of the list 
sum = 22

                        //entire sum
                        | [6, 6] <- it removed the last value of the sum array, here 3 and 6
                        |
check 5:                |
                        |
      5 + sum  -> 22    | 5              | fits
      5 + sum  -> 22    | 5              | fits
      5 + sum  -> 22    |                | does not fit
                        |
check 3:                |
                        |
      3 + sum  -> 22    |                | does not fit
                        |
                        |
--------------------------------------------------------
                          = sum = [6, 6, 5, 5] == 22

所以在此之后,if else语句只是检查列表的当前值是否适合sum数组并且不大于目标。如果它适合它将它添加到数组,否则它将转到下一个列表元素。


现在您可以看到我的代码纠正了一些错误,但我并不是100%确定它适用于所有内容而且我也不确定您是否可以产生级别3的错误,您必须返回sum数组是一个正确的3个值,如果有一个,我也不确定我的代码是否纠正了它,或者甚至可能陷入无限循环。

但毕竟这里谈论的是完成所有魔术的代码:

<?php

    //data
    $arr = [3,5,6,10,15,30];
    $sum = 69;


    //get sum
    function getKnapsackSum($arr, $sum) {
        //prepare data
        rsort($arr);
        $end = end($arr);
        reset($arr);
        $tmp = $arr;
        $result = [];

        //check if it is not a empty input
        if($sum == 0 || empty($tmp) || array_sum($tmp) == 0) return [0];

        //calculate the sum
        while(array_sum($result) != $sum) {
            if(empty($tmp) && empty($result)) return [0];
            if(current($tmp) === FALSE) end($tmp);

            //correction for errors level 1
            if(current($tmp) == $end && array_sum($result) + current($tmp) > $sum) {
                array_pop($result);
                array_shift($tmp);
                reset($tmp);
            }

            //correction for errors level 2
            if(empty($tmp)) {
                $mistake = array_pop($result);
                if($mistake == NULL) return [0];
                $tmp = array_slice($arr, array_search($mistake, $arr)+1);
                reset($tmp);
            }

            if(array_sum($result) + current($tmp) <= $sum)
                $result[] = current($tmp);
            else
                next($tmp);     
        }
        return $result;
    }


    $result = getKnapsackSum($arr, $sum);
    print_r($result);

?>

输出:

Array ( [0] => 30 [1] => 30 [2] => 6 [3] => 3 )

此处还有一些输入示例和我的代码输出:

    input        |        output    
------------------------------------
 list: []        |
 goal:  0        |  sum: [0]
------------------------------------
 list: []        |
 goal:  1        |  sum: [0]
------------------------------------
 list: [0]       |
 goal:  1        |  sum: [0]
------------------------------------
 list: [0]       |
 goal:  0        |  sum: [0]
------------------------------------
 list: [3, 5, 6] |  //error level 1
 goal:  8        |  sum: [5, 3]
------------------------------------
 list: [3, 5, 6] |  //error level 2
 goal: 22        |  sum: [6, 6, 5, 5]
------------------------------------
 list: [5, 7, 9] |  //can't build the sum
 goal: 56        |  sum: [0]
------------------------------------
 list: [5, 7, 9] |
 goal: 34        |  sum: [9, 9, 9, 7]
------------------------------------