PHP - 在数组中查找最小值并组合所有可能的数字以达到给定的总和

时间:2016-08-03 02:49:48

标签: php mysql algorithm sorting knapsack-problem

不确定以下情况是否属于背包问题或硬币变更方法。想在PHP语言中寻求解决这个问题的方法。需要简单的解决方案,因为不了解互联网提供的一些算法(文本理论)。

鉴于表(A)如下:

TABLE (A)
---------
Item    |    Name    |    Price ($)
-------- ------------ --------------
1       |   Adidas   |      35
2       |   Nike Run |      70
3       |   Puma     |     100
4       |   Nike     |      85
5       |   NB       |      65
  1. 合并表格(A)中的3个项目中的任何一个,总计大于或等于200美元。
    • 首先,对表格(A)进行排序。
    • 其次,获得最小/最小金额(价格)。在这种情况下是35美元。
    • 第三,从其他数量逐一检查。
    • 第四,总结3种超过或等于200美元的组合可能性。
  2. 结果:

    Item    |    Name    |    Price ($)
    -------- ------------ --------------
    1       |   Adidas   |      35
    5       |   NB       |      65
    3       |   Puma     |     100
    

    给出另一个样本表(B)如下:

    TABLE (B)
    ---------
    Item    |    Name    |    Price ($)
    -------- ------------ --------------
    1       |   Adidas   |       5
    2       |   Nike Run |      35
    3       |   Puma     |     110
    4       |   Nike     |      65
    5       |   NB       |      15
    
    1. 合并表格(B)中的3个项目中的任何一个,总计大于或等于200美元。
      • 首先,对表格(B)进行排序。
      • 其次,获得最小/最小金额(价格)。在这种情况下是5美元。
      • 第三,从其他数量逐一检查。
      • 第四,总结3种超过或等于200美元的组合可能性。
      • 第五,如果最小的与其他人结合并且未能总计200美元,则得到第二小,并重复第一步到第四步。
      • 第六,在这种情况下,最佳最小值/最小值为35美元。
    2. 结果:

      Item    |    Name    |    Price ($)
      -------- ------------ --------------
      2       |   Nike Run |      35
      4       |   Nike     |      65
      3       |   Puma     |     110
      

2 个答案:

答案 0 :(得分:0)

假设表A是一个关联数组,我们称之为$ a。 要找到将> = 200的第一个组合,请执行以下操作:

$rowsWithValueOver200 = array();
foreach($a as $value) {
    foreach($a as $value2) {
        // Make sure to ignore the value selected in the first loop.
        if ($value2 == $value)
            continue;

        foreach($a as $value3) {
            // Make sure to ignore the value selected in the first and second loop.
            if ($value3 == $value)
                continue;
            if ($value3 == $value2)
                continue;

            $total = $value3['Price'] + $value2['Price'] + $value['Price'];

            if ($total >= 200) {
                // Store all of the rows in the new array.
                $rowsWithValueOver200[] = $value;
                $rowsWithValueOver200[] = $value2;
                $rowsWithValueOver200[] = $value3;
                break;
            }
        }
    }
}

所以我们遍历数组3次。一旦我们查看3个唯一值,请检查总和。始终确保我们不包含先前选择的数组元素($ value或$ value2)。这当然可以稍微清理一下并且更加通用,但它可以用于3个元素。将其重构为更通用的函数将对其自身进行递归调用,允许起始位置(然后使用for循环而不是foreach)。

要找到最小值非常简单。

$minVal = PHP_INT_MAX; // Initially something high, here it is the maximum integer value.
$minRow;
foreach($rowsWithValueOver200 as $value) {
    if ($value['Price'] < $minVal){
        $minVal = $value['Price'];
        $minRow = $value;
    }
}

设置最小值(minVal)以检查真正高的值。迭代数组值,如果$ value [&#39; Price&#39;]小于当前的minVal,那么它现在是minVal并将数组元素存储在$ minRow中供以后参考。

3和4我不确定你在问什么。目前尚不清楚。 如果你想在$ rowsWithValueOver200中找到三行的总和,那么你可以使用我在第一个答案中的内容。如果你想只用3个最终元素来做,那就这样做:

$total = 0;
foreach($rowsWithValueOver200 as $value) {
    $total += $value['Price'];
}

如果您正在寻找总计200或更多的其他可能性,那么只需使用第一个示例,但在第一个循环中使用带有索引的for循环而不是foreach。从每个索引开始。

祝你好运!

答案 1 :(得分:0)

MySQL已经适当地装备完成这一切。您可以拉出整个表格以供PHP使用,但这可能会使资源变得昂贵,而且肯定会产生较慢的结果,尤其是对于较大的数据库。 PHP通常需要多次遍历数据才能进行足够的数组操作,MySQL可以很容易地完成这些操作。

假设您的数据库如下所示:

CREATE TABLE IF NOT EXISTS `A` (
  `Item` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(10) NOT NULL,
  `Price` decimal(10,2) NOT NULL,
  PRIMARY KEY (`Item`),
  UNIQUE KEY `Name` (`Name`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

INSERT INTO `A` (`Item`, `Name`, `Price`) VALUES
(1, 'Adidas', '35.00'),
(2, 'Nike Run', '70.00'),
(3, 'Puma', '100.00'),
(4, 'Nike', '85.00'),
(5, 'NB', '65.00');

您可以这样查询:

SELECT @leastprice := min(`Price`) FROM `A`;
SELECT t1.Name AS Name1, t2.Name AS Name2, t3.Name AS Name3,
       t1.Price AS Price1, t2.Price AS Price2, t3.Price AS Price3,
       (t1.Price+t2.Price+t3.Price) AS `Total`
    FROM `A` t1
    LEFT JOIN `A` t2 ON t1.Item != t2.Item
    LEFT JOIN `A` t3 ON t1.Item != t2.Item AND t2.Item != t3.Item
    WHERE t1.price = @leastprice
      AND t2.price<=t3.price
      AND t1.price+t2.price+t3.price >= 200;

结果:

result A

<小时/> 修改
对于第二种情况,假设您的数据库如下所示:

CREATE TABLE IF NOT EXISTS `B` (
  `Item` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(10) NOT NULL,
  `Price` decimal(10,2) NOT NULL,
  PRIMARY KEY (`Item`),
  UNIQUE KEY `Name` (`Name`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

INSERT INTO `B` (`Item`, `Name`, `Price`) VALUES
(1, 'Adidas', '5.00'),
(2, 'Nike Run', '35.00'),
(3, 'Puma', '110.00'),
(4, 'Nike', '65.00'),
(5, 'NB', '15.00');

您可以这样查询:

SELECT @leastTalliablePrice := min(t1.Price) FROM `B` t1
    LEFT JOIN `B` t2 ON t1.Item != t2.Item
    LEFT JOIN `B` t3 ON t1.Item != t2.Item AND t2.Item != t3.Item
    WHERE t2.price<=t3.price
      AND t1.price+t2.price+t3.price >= 200;
SELECT t1.Name AS Name1, t2.Name AS Name2, t3.Name AS Name3,
       t1.Price AS Price1, t2.Price AS Price2, t3.Price AS Price3,
       (t1.Price+t2.Price+t3.Price) AS `Total`
    FROM `B` t1
    LEFT JOIN `B` t2 ON t1.Item != t2.Item
    LEFT JOIN `B` t3 ON t1.Item != t2.Item AND t2.Item != t3.Item
    WHERE t1.price = @leastTalliablePrice
      AND t2.price <= t3.price
      AND t1.price+t2.price+t3.price >= 200;

结果:

result B

如果由于某种原因,您的数据库没有3个或更多不同的项目,或者它们的价格不足以达到您想要的总数,那么查询将以与SELECT相同的方式返回0行结果为0行,如果它找不到要选择的行。但如果在上述情况下有多个匹配,则会产生多行,就像我的第一个屏幕截图中的情况一样。