PHP Implementation-Knapsack Problem for Dynamic Programming

Posted by riiel on Tue, 13 Aug 2019 13:11:48 +0200

Reasons for the event

Because our company held an algorithm programming contest, randomly draw the following pictures of the algorithm title, think about a period of time before remembering in the book (algorithm diagrams) there is a more consistent algorithm, that is, the dynamic programming "knapsack problem".

Knapsack problem is a NP-complete problem of combinatorial optimization. The problem can be described as: Given a set of items, each item has its own weight and price. Within the limited total weight, how can we choose to make the total price of the item the highest?

How to choose the most suitable items to put in a given backpack is in line with our topic, so this time we use the "0-1 backpack problem" and use our topic to substitute, "total number" is equivalent to "backpack", "goods" is equivalent to "work order type". The weight of the items is the number of people required. .

Supplement:

There are three extensions of knapsack problem solution: unbounded knapsack problem, 0-1 knapsack problem and secondary knapsack problem.

The title of the algorithm is as follows

Dynamic programming is a multi-stage decision-making problem, which usually starts from the initial state and ends by choosing the middle stage decision-making. These decisions form a decision sequence and determine an activity route (usually the optimal one) to complete the whole process. The design of dynamic programming has a certain pattern. Generally, it has to go through the following steps.

Initial state decision 2 _Decision-making n_Ending State

Dynamic Programming Solution Formula:

f(n,m)=max{f(n-1,m),f(n-1,m-w[n])+P(n,m)}

  • Horizontal m (total number) and longitudinal n (type of work order for 4 cars)
  • F (n-1, m) ==> (decision-making 1) the number of technicians corresponding to the type of work order, the profit of the work order made
  • P (n, m) ==> (Decision 2) The number of technicians corresponding to the current type of work order and the profit of the work order
  • F (n-1, M-W [n]) ==> The profit of the corresponding number in the previous decision by subtracting the number of people required for the current work order

So the answer to the optimal solution is: the corresponding value of the five technicians in decision n, 1799 yuan

PostMan submits parameters:

people:5
carDetail[0][technician]:2
carDetail[0][amount]:49
carDetail[0][type]:Vehicle Safety Inspection
carDetail[1][technician]:2
carDetail[1][amount]:499
carDetail[1][type]:Deep diagnosis
carDetail[2][technician]:3
carDetail[2][amount]:1300
carDetail[2][type]:Used Car Detection
carDetail[3][technician]:1
carDetail[3][amount]:10
carDetail[3][type]:Special Testing of Air Conditioning

Solution 1: Dynamic programming

<?php
// dynamic programming
error_reporting(E_ALL ^ E_NOTICE);
$t1 = microtime(true);

class bestMatch
{
    public function getMethod($postData)
    {
        $peopleArr = $gainArr = $nameArr = [0];
        foreach ($postData['carDetail'] as $val) {
            // Initialize individual packages: required number, profit and package name array
            $peopleArr[] = $val['technician'];
            $gainArr[] = $val['amount'];
            $nameArr[] = $val['type'];
        }

        // Total number of people (backpack)
        $totalPeople = $postData['people'];
        // Check odds
        $items = count($peopleArr);
        // Profit List - Initial Status
        $cacheMap[] = array_fill(1, $items, 0);
        // Package List - Initial Status
        $cacheMapName[] = array_fill(1, $items, '');

        //Intermediate decisions (put items a, b, c, d, E in turn)
        // The first cycle is the total number.
        for($i = 1; $i <= $totalPeople; $i++)
        {
            // The second cycle is the set meal.
            for($j = 1; $j < $items; $j++)
            {
                $requiredPeople = $peopleArr[$j];
                $gain = $gainArr[$j];
                $name = $nameArr[$j];
                // Number of coordinates in the previous row
                $preLine = $j-1;
                $prevGain = $cacheMap[$preLine][$i];
                $prevName = $cacheMapName[$preLine][$i];
                if($requiredPeople > $i)
                {
                    $cacheMap[$j][$i] = $prevGain;
                    $cacheMapName[$j][$i] = $prevName;
                }
                else
                {
                    // surplus value
                    if ($i-$requiredPeople >= 0) {
                        $surplusPeople = $i-$requiredPeople;
                        $surplusGain = $cacheMap[$preLine][$surplusPeople];
                        $surplusName = $cacheMapName[$preLine][$surplusPeople];
                    }else {
                        $surplusGain = 0;
                        $surplusName = '';
                    }
                    $nowTotalGain = $gain + $surplusGain;
                    $cacheMap[$j][$i] = max($prevGain, $nowTotalGain);

                    if ($prevGain > $nowTotalGain) {
                        $cacheMapName[$j][$i] = $prevName;
                    }else{
                        $cacheMapName[$j][$i] = $name.'+'.$surplusName;
                    }
                }
            }
        }

        $actual = count($postData['carDetail']);
        return [
            'maxMatch' => $cacheMap[$actual][$totalPeople],
            'maxMatchName' => trim($cacheMapName[$actual][$totalPeople],'+')
        ];
    }
}
$bestMatch = new bestMatch;
if (empty($_POST) || isset($_POST['people']) && $_POST['people'] > 0) {
    die('Error in submitting parameters');
}
$res = $bestMatch->getMethod($_POST);
$t2 = microtime(true);
echo 'dynamic programming: '.'<br/>';
echo 'Best amount: '.$res['maxMatch'].'<br/>';
echo 'Best Combination: '.$res['maxMatchName'].'<br/>';
echo 'time consuming'.round($t2-$t1,7).'second'.'<br/>' ;
echo 'Consumption of memory: ' . memory_get_usage().'byte'.'<br/>' ;

Solution 2: Recursion

<?php
// recursive query
error_reporting(E_ALL ^ E_NOTICE);
$t1 = microtime(true);

class optimal
{
    public function getSortList($array,$index = 0,$up =0,&$result =[])
    {
        for ($i=$index; $i < count($array); $i++) {
            if($index > 0 ){
                $value['name'] = $up['name'].'+'.$array[$i]['type'];
                $value['amount'] = bcadd($up['amount'],$array[$i]['amount']);
                $value['technician'] = bcadd($up['technician'],$array[$i]['technician']);
            }else{
                $value['name'] = $array[$i]['type'];
                $value['amount'] = bcadd($array[$i]['amount'],0);
                $value['technician'] = bcadd($array[$i]['technician'],0);
            }
            $result[]  = $value;
            $this->getSortList($array,$i+1,$value,$result);
        }
        return $result ;
    }

    public function getMethod($postData)
    {
        $people = $postData['people'];
        $carDetail = $postData['carDetail'];
        $allResult = $this->getSortList($carDetail);
        $bestMatch = [];
        foreach ($allResult as $val) {
            if ($val['technician'] <= $people) {
                if ($bestMatch) {
                    if ($val['amount'] > $bestMatch['amount']) {
                        $bestMatch = $val;
                    }
                }else{
                    $bestMatch = $val;
                }
            }
        }
        return $bestMatch;
    }
}

$optimal = new optimal();
if (empty($_POST) || isset($_POST['people']) && $_POST['people'] > 0) {
    die('Error in submitting parameters');
}
$bestMatch = $optimal->getMethod($_POST);

$t2 = microtime(true);
echo 'recursive query: '.'<br/>';
echo 'Best amount: '.$bestMatch['amount'].'<br/>';
echo 'Best Combination: '.$bestMatch['name'].'<br/>';
echo 'time consuming'.round($t2-$t1,7).'second'.'<br/>' ;
echo 'Consumption of memory: ' . memory_get_usage().'byte'.'<br/>' ;

Reference article link:

Topics: PHP Programming