【问题标题】:PHP - Find minimum value in an array and combine all possible numbers to reach a given sumPHP - 在数组中查找最小值并组合所有可能的数字以达到给定的总和
【发布时间】:2016-12-08 14:02:07
【问题描述】:

不确定以下情况属于背包问题还是硬币更换方法。想用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美元的组合可能性。

结果:

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 美元。

结果:

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

【问题讨论】:

    标签: php mysql algorithm sorting knapsack-problem


    【解决方案1】:

    假设表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['Price'] 小于当前的 minVal,则现在是 minVal 并将数组元素存储在 $minRow 中以供以后参考。

    3 和 4 我不确定你在问什么。目前还不清楚。 如果您想在 $rowsWithValueOver200 中找到三行的总和,那么您可以使用我在第一个答案中的内容。如果您只想使用 3 个最终元素,请执行以下操作:

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

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

    祝你好运!

    【讨论】:

    • 感谢您的解决方案,我已经更新了问题,希望可以更清楚。我需要检查最小值并开始总结 3 个可能的值以满足给定的结果。
    【解决方案2】:

    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;
    

    结果:


    编辑:
    对于第二种情况,假设您的数据库如下所示:
    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;
    

    结果:

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

    【讨论】:

    • 感谢您的解决方案,如果我在问题中给出了喜欢表 (b) 的示例怎么办?请多多指教。
    • @iCol.ivan 已更新。
    • 您还可以通过将200 的所有实例更改为另一个数字(例如200020)来进行查询。如果您对行顺序感兴趣,可以添加ORDER BY (t1.price+t2.price+t3.price); 以获取按升序排列的行。 (在前面加一个减号以使它们按降序排列,或者您可以在后面添加单词DESC
    猜你喜欢
    • 2011-06-05
    • 2021-01-02
    • 2019-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多