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'] >= $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 &$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 循环遍历表并优化输出的主要内容,如果未能解决你的问题,请参考以下文章
DataTables 循环遍历表并删除特定列中包含特定字符串的所有行