【问题标题】:Keeping big recursive functions to a minimum with PHP使用 PHP 将大型递归函数保持在最低限度
【发布时间】:2015-05-22 09:05:43
【问题描述】:

我想用木板填充一个特定的长度。为此,我需要找到木板组合的最佳可能性,使用尽可能少的木板,最后一块木板的剩余量最少。 为此,我首先创建一棵具有所有可能性的树。之后我将数组展平一点,所以我在一个数组中拥有所有选项(删除树方面)。最后,我遍历数组以获得具有最低深度和最低剩余的结果。 前两个步骤我使用递归函数完成。

问题: 长度为 5000 毫米,木板为 5000 毫米、4000 毫米和 3000 毫米,有 7 种可能性。 当我尝试 20000 毫米的长度和 2450 毫米、2750 毫米、3000 毫米、3150 毫米、3600 毫米、3900 毫米、4000 毫米、4600 毫米、4900 毫米和 5000 毫米的木板时,有几十亿种可能性。 使用我当前的代码,我超过了 php 的 30 秒限制。我尝试在谷歌上寻找一个好的算法来解决这个问题,但我找不到可能的解决方案。

有没有人知道解决这个问题的算法或解决方案?我试图将计算保持在最低限度,以尽可能快地保持速度。

创建树

public function createPossibilities($lengths, $length, $depth = 1) {
    $lengths = [2450, 2750, 3000, 3150, 3600, 3900, 4000, 4600, 4900, 5000]; // example array of all possible lengths
    $res = []; // array with all possible results

    foreach ($lengths as $l) {
        $rest = $length - $l; // calculates the rest length 
        if ($rest > 0) // check if length is complete
            $children = ['depth' => $depth, 'length' => $l, 'children' => $this->createPossibilities($lengths, $rest, ($depth +1))]; // if length is not complete, do function recursively
        else
            $children = ['depth' => $depth, 'length' => $l, 'leftover' => abs($rest)]; // if length is complete, add the leftover to the array
        $res[] = $children;
    }

    return $res;
}

扁平树

public function flattenArray($array, &$possibilities, $str = '') {
    foreach ($array as $element) {
        $temp = $str;
        $temp .= $element['length'] . ', '; // add length to string
        if (array_key_exists('children', $element)) { // check if element has children
            $this->flattenArray($element['children'], $possibilities, $temp); // do  function recursively for the child
        } else { 
            $temp = explode(', ', $temp); // explode the string into an array
            array_pop($temp); // remove last empty element
            $temp['depth'] = count($temp); // add the depth to the array
            $temp['leftover'] = $element['leftover']; // add the leftover to the array
            $possibilities[] = $temp; // add the possibility to the array
        }
    }
}

获得最佳可能性

public function getBestPossibility($options, &$liggersPerBreedte) {
    $minDepth = -1;
    $minLeftover = -1;
    foreach ($options as $option) {
        if ($option['depth'] < $minDepth || $minDepth == -1) { // check if possibility has fewest lengths
            $minDepth = $option['depth']; // put min depth to this option
            unset($option['depth']); // remove depth from option
            $minLeftover = $option['leftover']; // put min leftover to this option
            unset($optie['leftover']); // remove leftover from option
            $bestPosibility = $option; // best possibility is array with lengths
        } else if ($option['depth'] == $minDepth && $option['leftover'] < $minLeftover) { // check if depth is the same as min, but leftover is less
            unset($option['depth']); // remove depth from option
            $minLeftover = $optie['leftover']; // put min leftover to this option
            unset($optie['leftover']); // remove leftover from option
            $bestPosibility = $option; // best possibility is array with lengths
        }
    }
}

示例

$tree = createPossibilities([5000, 4000, 3000], 8000);
$possibilities = [];
$flatten = flattenArray($tree, $possibilities);
$best = getBestPossibilities($possibilities); // result: [5000, 3000]

【问题讨论】:

    标签: php arrays algorithm recursion


    【解决方案1】:

    当您以不同的顺序创建可能性时,您可以改进它,例如随机顺序并在某个递归级别停止。或者您可以使用近似值,例如装箱。背包是一个有点不同的问题,因为它给出了重量和成本。您也可以尝试动态编程:Dynamic programming and memoization: bottom-up vs top-down approaches 和 memoization。

    【讨论】:

    • 谢谢!这帮助我找到了更好的方法。发布我的结果作为答案。
    • @Thalsan:如果我的回答有帮助,请考虑接受和/或投票!谢谢!
    【解决方案2】:

    感谢 Phpdna 的回答,我找到了一种更简单的方法。我没有从最小到最大遍历长度,而是将它们颠倒过来,从最大到最小。当我第一次到达整个长度时,我将深度设置为最大深度,这样函数就不会循环十亿次。这似乎工作得很好,而且比我以前的要快得多。

        $lengths = [5800, 5150, 4900, 4600, 4300, 3950, 3650, 3050, 2750, 2450];
        $length = 10000;
        $maxDepth = ceil($length / $lengths[count($lengths) - 1]);
        $leftover = $lengths[0];
        $result = '';
        getBestPossibility($length, $lengths, $result, $leftover, $maxDepth);
        echo $result; // "5800, 4300, 100(leftover)"
    
    public function getBestPossibility($length, $lengths, &$result, &$leftover, &$maxDepth, $depth = 1, $path = '') {        
        if ($depth <= $maxDepth) {
            foreach ($lengths as $l) {       
                if ($length - $l <= 0) {          
                    $maxDepth = $depth;
                    if (abs($length - $l) < $leftover) {
                        $leftover = abs($length - $l);
                        $result = $depth == 1 ? $l . ', ' . $leftover . '(leftover)' : $path . ', ' . $l . ', ' . $leftover . '(leftover)';
                    }
                } else {
                    $path = $depth == 1 ? $l : $path . ', ' . $l;
                    $depth++;
                    getBestPossibility(($length - $l), $lengths, $result, $leftover, $maxDepth, $depth, $path);
                }
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      您在这里描述的是Knapsack problem 的一个特例:
      给定一组物品(在您的情况下为木板),找到一组物品,以使总重量(在您的情况下为木板的总长度)小于给定的限制并且该值尽可能大(这部分没有'在您的示例中不存在)。

      背包问题有很多解决方案(在 Wikipedia 上有描述),但要小心,因为它是 NP 完全的,这意味着要解决你将使用的任何策略都非常困难。

      编辑:它实际上是“找到一组物品,以使总重量(在您的情况下为木板的总长度)超过给定的限制,并且值(木板的总数)尽可能小。”

      【讨论】:

      • 你能解释为什么你对答案投了反对票吗? Wikipedia 实际上很好地解释了动态编程方法,这是你应该在这里使用的,imo。
      • “您的示例中不存在此部分”。是的,确实如此,但反过来。它必须大于限制,但必须是最短的解决方案。
      • tbh,我不这么理解。我会编辑答案
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-23
      • 2011-10-18
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多