PHP Mysql Logic 循环遍历表并优化输出

Posted

技术标签:

【中文标题】PHP Mysql Logic 循环遍历表并优化输出【英文标题】:PHP Mysql Logic to loop through table and optimize output 【发布时间】:2012-10-14 06:17:18 【问题描述】:

不是 100% 确定标题,请随意编辑以使其更通用且对所有用户都有帮助

我有一个订单捕获“门户”,可以捕获订单。部分订单是数量或订单。

我现在需要处理订单,以便可以构建一个托盘以优化使用托盘。

例如,一个包装好的托盘尺寸可以是 1.62 立方米 (1x1.2x1.35)

然后我的订单上有以下物品

产品代码、数量、总量

1,      10,   0.5 立方米 2,       3,    0.2立方米 3,       5,    1.2立方米 4、       1、    0.15立方米 5,      15,   0.6立方米

我想处理这个,我假设使用 mysql 事务,以便获得以下输出:

托盘 1:产品 1、2、5。总体积=1.3 立方米 托盘 2:产品 3,4。总体积=1.35 立方米

因此,为了澄清,脚本将采用第一个 0.5 立方体托盘,然后在列表中查找最接近 0.85 的下一个订单以实现 1.35 立方米的目标。在这种情况下,它将是 0.6。现在总共 1.1 个立方体。剩余空间现在为 0.25 个立方体。列表中最接近的项目现在是产品 2 @ 0.2 立方体。现在总共 1.3 个立方体,并且不存在 0.05 个或更少的产品,因此托盘是完整的。下一个可用产品现在是产品 3 @ 1.2 立方体。因此需要 0.15 或更小的产品。产品 4 是确切的体积,因此添加到托盘并关闭。

我希望这有助于澄清我的要求。

是否有 mysql 事务可以帮助解决这个问题还是需要用 php 处理。

对此的任何帮助将不胜感激,只是不确定如何构建此逻辑。

一如既往地感谢您的宝贵时间,

mysql表如下:

【问题讨论】:

盒子的形状不重要吗?说托盘具有容积容量很好,但除非容器是流动的,否则还会有“形状”容量(这是一个更严格的限制):托盘中可能保留相当大的空间,但分散在不规则的大小和形状;而您可能只有体积较小的立方体形盒子,但实际上无法放置在任何地方。或者这是一个纯粹的学术练习,你不关心形状? 嗨@eggyal。你是 100% 正确的。订单在层级完成,因此水平足迹将始终与托盘尺寸相同。它实际上只是结合垂直层来提高效率。我在问题中添加了一个粗略的草图以帮助澄清。谢谢 我确实通读了背包问题,这个逻辑可以用mysql或者php完成吗?再次感谢。 好的,在这种情况下,您实际上只关心将订单的总高度保持在托盘的总高度容量内。问题陈述是等价的:我只是发现一维比三维更容易思考! 感谢@eggyal 是有道理的。关于我如何用 PHP 完成这个的任何建议?谢谢。 【参考方案1】:

这是经典的bin packing problem,是NP-hard(也就是说,我们还没有弄清楚这个问题是否可以在多项式时间内解决)。寻找最佳包装无异于尝试所有可能的组合。

如果您不关心找到最佳解决方案,那么您在问题中描述的贪婪启发式(称为first fit decreasing)是一个合理的近似值;还有其他启发式方法可以保证总体上更优化的结果,但它们变得相当复杂。

您可以在PHP中实现FFD如下(使用PDO进行数据库访问):

define('PALLET_SIZE', 1 * 1.2 * 1.35);

$dbh = new PDO("mysql:dbname=$dbname", $username, $password);

$qry = $dbh->prepare('
  SELECT   code, qty, volume
  FROM     orders
  WHERE    order_id = :order
  ORDER BY volume DESC
');

$qry->execute(array(':order' => 123));

$pallets = array();
while ($order = $qry->fetch()) 
  if ($order['volume'] > PALLET_SIZE) die('item too big');

  for ($i = 1; $i <= $order['qty']; $i++)  // remove this loop if not needed
    $placed = false;
    foreach ($pallets as &$pallet) 
      if ($pallet['remaining'] >= $order['volume']) 
        $pallet['remaining'] -= $order['volume'];
        $pallet['items'][] = $order['code'];
        $placed = true;
        break;
      
    
    if (!$placed) $pallets[] = array(
      'remaining' => PALLET_SIZE - $order['volume'],
      'items' => array($order['code'])
    );
  


print_r($pallets);

【讨论】:

感谢eggyal、legendery 的解释和示例代码。我需要了解它,但很快就会恢复。谢谢一百万。 再次感谢@eggyal。我只是想了解您的代码,目前稍微超出了我的范围。您能否为我详细说明/解释此代码段:code foreach ($pallets as $pallet) if ($pallet['remaining'] &gt;= $order['volume']) $pallet['remaining'] -= $order['volume']; $pallet['items'][] = $order['code']; $placed = true; break; 除此之外,我如何使用它的输出。感谢您对此的帮助,非常感谢。干杯。 您将依次查看订单,从最大到最小,然后循环遍历每个可用托盘以找到适合当前订单的托盘。有一组托盘,$pallets,其中一个迭代;每个成员本身都是一个包含元素$pallet['remaining'] 的数组,该元素指示该托盘上剩余的空间量。如果足以容纳当前订单,我们会减少剩余空间并将订单代码推送到$pallet['items'] 元素(另一个数组)。最后,我们注意到我们找到了一个托盘并从循环中中断。 要使用输出,请检查 $pallets 数组。例如,您可以简单地执行 print_r($pallets);(我应该包括在内 - 抱歉!)。 抱歉,我在foreach 中省略了一个&;结果$pallet 是按值而不是按引用分配的(并且在$pallets 中保存的实际数组中没有对其进行更新)。替换为foreach ($pallets as &amp;$pallet),如上图。除此之外,您对上一条评论中问题的理解是完全正确的。【参考方案2】:

有没有mysql事务

我假设你的意思是一个 mysql 函数。

不,mysql 中没有任何东西可以帮助您解决这个问题。这是一个NP难题。解决它的最有效方法可能是 genetic algorithm,但是您的数据中没有足够的信息来准确地实现这一点 - 包装取决于形状(即所有尺寸)与体积一样多。

如果您确实有尺寸,那么让人类设置包装比尝试在代码中进行设置要容易得多。

作为一个快速的解决方案,我会比较一组 N 个列表副本,随机排序,遍历每个副本以创建完整的托盘,然后选择具有最少托盘(或其他需要的特征)的列表。为 N 选择正确的值将取决于大小的分布,以及您希望花费多少时间处理数据。

【讨论】:

感谢 symcbean。我确实有很多可用的数据,只是认为体积是最好的测量方法。根据@eggyal,也许我们可以只看高度限制。如何创建 N 个副本以在 PHP 中进行比较?感谢您的宝贵时间。 无论您处理体积还是高度,它在计算上都没有任何区别:唯一的区别是人类理解模型的概念上的容易程度。 明白了。有什么例子或起始代码让我看看吗?谢谢,我知道这总是一个厚脸皮的问题。 我在图层更新之前添加了我的答案。这使得它更加复杂 - 因为您无法在半完成层之上平衡完整层 - 但您必须在算法中对层进行建模 在我的示例中,因为排序是在层级别完成的,所以永远不会有一半完成的层?它只是试图确保各个层以最佳方式组合到托盘上,以便使用最少的托盘?【参考方案3】:

这类似于背包问题;这是一个只有尝试所有可能的组合才能完美解决的问题。随着可能组合数量的增加,这很快成为一个问题。在您的示例中,将有 33 个!组合 - 相当大的数量。所以那条路线可能不是我们想走的……

虽然有背包问题here 的示例代码,但没有“开箱即用”的解决方案。这可能会有所帮助。

几年前我写了一个程序,它在类似的问题上做了一个合理的工作;伪代码类似于:

for each order
  while (unallocated items on order)
    create new pallet 
    while not (pallet = full)
      allocate largest remaining item smaller than remaining capacity to pallet 
    loop
  loop
next order

这个算法做得不错 - 船厂里的人喜欢它,因为最大的物品总是在装箱单的顶部(因此最终在堆栈的底部)。企业承认算法不能保证是最优的;在实践中,这只会对大订单产生影响,因为算法的轻微改进可能会为我们节省一个托盘;在大多数情况下,使算法更有效意味着货物中的最后一个托盘几乎是空的。

【讨论】:

以上是关于PHP Mysql Logic 循环遍历表并优化输出的主要内容,如果未能解决你的问题,请参考以下文章

Struts的Logic标签的用途

DataTables 循环遍历表并删除特定列中包含特定字符串的所有行

循环遍历 Excel 工作表并使用 C# 将文本保存到 TXT 文件中

mysql 遍历所有表并条件查询

PHP MySQL循环遍历行并在日期更改时打印日期

遍历表并更改值时访问崩溃