从给定的多组中找到最佳组合
假设您有一批货物。 它需要从 A 点到 B 点,从 B 点到 C 点,最后从 C 点到 D 点。你需要它在五天内以尽可能少的钱到达目的地。 每条航线都有三个可能的托运人,每个托运人都有自己不同的时间和成本:
Array
(
[leg0] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 5000
)
[FedEx] => Array
(
[days] => 2
[cost] => 3000
)
[Conway] => Array
(
[days] => 5
[cost] => 1000
)
)
[leg1] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 3000
)
[FedEx] => Array
(
[days] => 2
[cost] => 3000
)
[Conway] => Array
(
[days] => 3
[cost] => 1000
)
)
[leg2] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 4000
)
[FedEx] => Array
(
[days] => 1
[cost] => 3000
)
[Conway] => Array
(
[days] => 2
[cost] => 5000
)
)
)
您将如何以编程方式找到最佳组合?
到目前为止,我最好的尝试(第三或第四种算法)是:
- 找到每条航线最长的托运人
- 消除最“昂贵”的托运人
- 找到每条航线最便宜的托运人
- 计算总成本和费用 天
- 如果天数可以接受,则完成,否则,转到 1
在 PHP 中快速模拟(请注意,下面的测试数组工作顺利,但如果您使用上面的测试数组尝试它,它不会找到正确的组合):
$shippers["leg1"] = array(
"UPS" => array("days" => 1, "cost" => 4000),
"Conway" => array("days" => 3, "cost" => 3200),
"FedEx" => array("days" => 8, "cost" => 1000)
);
$shippers["leg2"] = array(
"UPS" => array("days" => 1, "cost" => 3500),
"Conway" => array("days" => 2, "cost" => 2800),
"FedEx" => array("days" => 4, "cost" => 900)
);
$shippers["leg3"] = array(
"UPS" => array("days" => 1, "cost" => 3500),
"Conway" => array("days" => 2, "cost" => 2800),
"FedEx" => array("days" => 4, "cost" => 900)
);
$times = 0;
$totalDays = 9999999;
print "<h1>Shippers to Choose From:</h1><pre>";
print_r($shippers);
print "</pre><br />";
while($totalDays > $maxDays && $times < 500){
$totalDays = 0;
$times++;
$worstShipper = null;
$longestShippers = null;
$cheapestShippers = null;
foreach($shippers as $legName => $leg){
//find longest shipment for each leg (in terms of days)
unset($longestShippers[$legName]);
$longestDays = null;
if(count($leg) > 1){
foreach($leg as $shipperName => $shipper){
if(empty($longestDays) || $shipper["days"] > $longestDays){
$longestShippers[$legName]["days"] = $shipper["days"];
$longestShippers[$legName]["cost"] = $shipper["cost"];
$longestShippers[$legName]["name"] = $shipperName;
$longestDays = $shipper["days"];
}
}
}
}
foreach($longestShippers as $leg => $shipper){
$shipper["totalCost"] = $shipper["days"] * $shipper["cost"];
//print $shipper["totalCost"] . " <?> " . $worstShipper["totalCost"] . ";";
if(empty($worstShipper) || $shipper["totalCost"] > $worstShipper["totalCost"]){
$worstShipper = $shipper;
$worstShipperLeg = $leg;
}
}
//print "worst shipper is: shippers[$worstShipperLeg][{$worstShipper['name']}]" . $shippers[$worstShipperLeg][$worstShipper["name"]]["days"];
unset($shippers[$worstShipperLeg][$worstShipper["name"]]);
print "<h1>Next:</h1><pre>";
print_r($shippers);
print "</pre><br />";
foreach($shippers as $legName => $leg){
//find cheapest shipment for each leg (in terms of cost)
unset($cheapestShippers[$legName]);
$lowestCost = null;
foreach($leg as $shipperName => $shipper){
if(empty($lowestCost) || $shipper["cost"] < $lowestCost){
$cheapestShippers[$legName]["days"] = $shipper["days"];
$cheapestShippers[$legName]["cost"] = $shipper["cost"];
$cheapestShippers[$legName]["name"] = $shipperName;
$lowestCost = $shipper["cost"];
}
}
//recalculate days and see if we are under max days...
$totalDays += $cheapestShippers[$legName]['days'];
}
//print "<h2>totalDays: $totalDays</h2>";
}
print "<h1>Chosen Shippers:</h1><pre>";
print_r($cheapestShippers);
print "</pre>";
I我想我可能必须真正做一些事情,我逐个进行每个组合(通过一系列循环)并将每个组合的总“分数”相加,然后找到最好的一个......
编辑: 澄清一下,这不是“家庭作业”(我不在学校)。 这是我当前工作项目的一部分。
需求(一如既往)一直在不断变化。 如果我在开始解决这个问题时受到当前的约束,我将使用 A* 算法的某些变体(或 Dijkstra 算法或最短路径或单纯形算法或其他算法)。 但一切都在发生变化,这让我陷入了现在的境地。
所以我想这意味着我需要忘记到目前为止我所做的所有废话,只做我知道应该做的事情,这是一种路径查找算法。
Say you have a shipment. It needs to go from point A to point B, point B to point C and finally point C to point D. You need it to get there in five days for the least amount of money possible. There are three possible shippers for each leg, each with their own different time and cost for each leg:
Array
(
[leg0] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 5000
)
[FedEx] => Array
(
[days] => 2
[cost] => 3000
)
[Conway] => Array
(
[days] => 5
[cost] => 1000
)
)
[leg1] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 3000
)
[FedEx] => Array
(
[days] => 2
[cost] => 3000
)
[Conway] => Array
(
[days] => 3
[cost] => 1000
)
)
[leg2] => Array
(
[UPS] => Array
(
[days] => 1
[cost] => 4000
)
[FedEx] => Array
(
[days] => 1
[cost] => 3000
)
[Conway] => Array
(
[days] => 2
[cost] => 5000
)
)
)
How would you go about finding the best combination programmatically?
My best attempt so far (third or fourth algorithm) is:
- Find the longest shipper for each leg
- Eliminate the most "expensive" one
- Find the cheapest shipper for each leg
- Calculate the total cost & days
- If days are acceptable, finish, else, goto 1
Quickly mocked-up in PHP (note that the test array below works swimmingly, but if you try it with the test array from above, it does not find the correct combination):
$shippers["leg1"] = array(
"UPS" => array("days" => 1, "cost" => 4000),
"Conway" => array("days" => 3, "cost" => 3200),
"FedEx" => array("days" => 8, "cost" => 1000)
);
$shippers["leg2"] = array(
"UPS" => array("days" => 1, "cost" => 3500),
"Conway" => array("days" => 2, "cost" => 2800),
"FedEx" => array("days" => 4, "cost" => 900)
);
$shippers["leg3"] = array(
"UPS" => array("days" => 1, "cost" => 3500),
"Conway" => array("days" => 2, "cost" => 2800),
"FedEx" => array("days" => 4, "cost" => 900)
);
$times = 0;
$totalDays = 9999999;
print "<h1>Shippers to Choose From:</h1><pre>";
print_r($shippers);
print "</pre><br />";
while($totalDays > $maxDays && $times < 500){
$totalDays = 0;
$times++;
$worstShipper = null;
$longestShippers = null;
$cheapestShippers = null;
foreach($shippers as $legName => $leg){
//find longest shipment for each leg (in terms of days)
unset($longestShippers[$legName]);
$longestDays = null;
if(count($leg) > 1){
foreach($leg as $shipperName => $shipper){
if(empty($longestDays) || $shipper["days"] > $longestDays){
$longestShippers[$legName]["days"] = $shipper["days"];
$longestShippers[$legName]["cost"] = $shipper["cost"];
$longestShippers[$legName]["name"] = $shipperName;
$longestDays = $shipper["days"];
}
}
}
}
foreach($longestShippers as $leg => $shipper){
$shipper["totalCost"] = $shipper["days"] * $shipper["cost"];
//print $shipper["totalCost"] . " <?> " . $worstShipper["totalCost"] . ";";
if(empty($worstShipper) || $shipper["totalCost"] > $worstShipper["totalCost"]){
$worstShipper = $shipper;
$worstShipperLeg = $leg;
}
}
//print "worst shipper is: shippers[$worstShipperLeg][{$worstShipper['name']}]" . $shippers[$worstShipperLeg][$worstShipper["name"]]["days"];
unset($shippers[$worstShipperLeg][$worstShipper["name"]]);
print "<h1>Next:</h1><pre>";
print_r($shippers);
print "</pre><br />";
foreach($shippers as $legName => $leg){
//find cheapest shipment for each leg (in terms of cost)
unset($cheapestShippers[$legName]);
$lowestCost = null;
foreach($leg as $shipperName => $shipper){
if(empty($lowestCost) || $shipper["cost"] < $lowestCost){
$cheapestShippers[$legName]["days"] = $shipper["days"];
$cheapestShippers[$legName]["cost"] = $shipper["cost"];
$cheapestShippers[$legName]["name"] = $shipperName;
$lowestCost = $shipper["cost"];
}
}
//recalculate days and see if we are under max days...
$totalDays += $cheapestShippers[$legName]['days'];
}
//print "<h2>totalDays: $totalDays</h2>";
}
print "<h1>Chosen Shippers:</h1><pre>";
print_r($cheapestShippers);
print "</pre>";
I think I may have to actually do some sort of thing where I literally make each combination one by one (with a series of loops) and add up the total "score" of each, and find the best one....
EDIT:
To clarify, this isn't a "homework" assignment (I'm not in school). It is part of my current project at work.
The requirements (as always) have been constantly changing. If I were given the current constraints at the time I began working on this problem, I would be using some variant of the A* algorithm (or Dijkstra's or shortest path or simplex or something). But everything has been morphing and changing, and that brings me to where I'm at right now.
So I guess that means I need to forget about all the crap I've done to this point and just go with what I know I should go with, which is a path finding algorithm.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
可以改变一些最短路径算法,例如Dijkstra算法,按成本对每条路径进行加权,但也保持跟踪时间,如果时间超过你的阈值,就停止沿着特定的路径前进。 应该找到最便宜的,让你低于你的门槛
Could alter some of the shortest path algorithms, like Dijkstra's, to weight each path by cost but also keep track of time and stop going along a certain path if the time exceeds your threshold. Should find the cheapest that gets you in under your threshold that way
听起来你所遇到的问题被称为“线性规划问题”。 这听起来也像是一个家庭作业问题,无意冒犯。
LP 问题的经典解决方案称为“单纯形法”。 去谷歌上查询。
但是,要使用该方法,您必须正确表述问题来描述您的需求。
尽管如此,还是可以枚举所有可能的路径,因为你的路径很小。 不过,这样的事情不会规模化。
Sounds like what you have is called a "linear programming problem". It also sounds like a homework problem, no offense.
The classical solution to a LP problem is called the "Simplex Method". Google it.
However, to use that method, you must have the problem correctly formulated to describe your requirements.
Still, it may be possible to enumerate all possible paths, since you have such a small set. Such a thing won't scale, though.
听起来像是 Dijkstra 算法 的工作:
维基百科文章中也有实现细节。
Sounds like a job for Dijkstra's algorithm:
There are also implementation details in the Wikipedia article.
如果我知道我只需要按照预先确定的顺序处理 5 个城市,并且相邻城市之间只有 3 条路线,我就会强制执行。 优雅是没有意义的。
另一方面,如果这是一项家庭作业,并且我应该生成一个可以实际扩展的算法,我可能会采取不同的方法。
If I knew I only had to deal with 5 cities, in a predetermined order, and that there were only 3 routes between adjacent cities, I'd brute force it. No point in being elegant.
If, on the other hand, this were a homework assignment and I were supposed to produce an algorithm that could actually scale, I'd probably take a different approach.
正如 Baltimark 所说,这基本上是一个线性规划问题。 如果每个航段的托运人系数(1 表示包含,0 表示不包含)不是(二进制)整数,那么这将更容易解决。 现在您需要找到一些(二进制)整数线性规划(ILP)启发式,因为该问题是 NP 困难的。
请参阅整数线性规划维基百科获取链接; 在我的线性编程课程中,我们至少使用了分支与界限。
其实现在我想起来了,这种特殊情况是可以在没有实际ILP的情况下解决的,因为只要<= 5天数并不重要。现在首先选择最便宜的航空公司作为首选(Conway 5:1000) 。 接下来,您再次选择最便宜的,结果是 8 天和 4000 个货币单位,这太多了,所以我们中止。 通过尝试其他人,我们发现他们的结果都是天> 5 所以我们回到第一个选择,尝试第二便宜的(联邦快递 2:3000),然后在第二个选择 ups,最后一个选择联邦快递。 这给了我们总共 4 天和 9000 个货币单位。
然后,我们可以使用此成本来修剪树中的其他搜索,这些搜索在某些子树阶段结果中的成本将大于我们已经找到的搜索结果,并从该点开始不搜索该子树。
只有当我们知道在子树中搜索不会产生更好的结果时,这才有效,就像我们在成本不能为负时所做的那样。
希望这篇闲言碎语对您有所帮助:)。
As Baltimark said, this is basically a Linear programming problem. If only the coefficients for the shippers (1 for included, 0 for not included) were not (binary) integers for each leg, this would be more easily solveable. Now you need to find some (binary) integer linear programming (ILP) heuristics as the problem is NP-hard.
See Wikipedia on integer linear programming for links; on my linear programming course we used at least Branch and bound.
Actually now that I think of it, this special case is solveable without actual ILP as the amount of days does not matter as long as it is <= 5. Now start by choosing the cheapest carrier for first choice (Conway 5:1000). Next you choose yet again the cheapest, resulting 8 days and 4000 currency units which is too much so we abort that. By trying others too we see that they all results days > 5 so we back to first choice and try the second cheapest (FedEx 2:3000) and then ups in the second and fedex in the last. This gives us total of 4 days and 9000 currency units.
We then could use this cost to prune other searches in the tree that would by some subtree-stage result costs larger that the one we've found already and leave that subtree unsearched from that point on.
This only works as long as we can know that searching in the subtree will not produce a better results, as we do here when costs cannot be negative.
Hope this rambling helped a bit :).
这是一个背包问题。 权重是运输天数,利润应该是 5000 美元 - 航段成本。 消除所有负面成本并从那里开始!
This is a knapsack problem. The weights are the days in transit, and the profit should be $5000 - cost of leg. Eliminate all negative costs and go from there!
我认为迪杰斯特拉算法是为了寻找最短路径。
cmcculloh 正在寻找最低成本,但限制是他在 5 天内得到它。
因此,仅仅找到最快的方式并不能以最便宜的价格到达目的地,而以最便宜的价格到达目的地也无法在所需的时间内到达目的地。
I think that Dijkstra's algorithm is for finding a shortest path.
cmcculloh is looking for the minimal cost subject to the constraint that he gets it there in 5 days.
So, merely finding the quickest way won't get him there cheapest, and getting there for the cheapest, won't get it there in the required amount of time.