从给定的多个集合中找到最佳组合

Posted

技术标签:

【中文标题】从给定的多个集合中找到最佳组合【英文标题】:Find the best combination from a given set of multiple sets 【发布时间】:2010-09-06 02:31:56 【问题描述】:

假设您有货物。它需要从 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"] . " &lt;?&gt; " . $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>";

我想我实际上可能需要做一些事情,我逐个制作每个组合(带有一系列循环)并将每个组合的总“分数”相加,然后找到最好的一个...... .

编辑: 澄清一下,这不是“家庭作业”(我不在学校)。这是我当前工作项目的一部分。

需求(一如既往)一直在不断变化。如果在我开始解决这个问题时给我当前的限制,我将使用 A* 算法的一些变体(或 Dijkstra 或最短路径或单纯形或其他东西)。但一切都在不断变化和变化,这将我带到了现在的位置。

所以我想这意味着我需要忘记我到目前为止所做的所有废话,只使用我知道我应该使用的东西,这是一种寻路算法。

【问题讨论】:

+1 用于以“让我们抛弃这种废话重新开始”的心态来满足随时间变化的需求。 【参考方案1】:

听起来你所拥有的被称为“线性规划问题”。这听起来也像是一个家庭作业问题,没有冒犯。

LP 问题的经典解决方案称为“单纯形法”。谷歌一下。

但是,要使用该方法,您必须正确表述问题以描述您的要求。

仍然可以枚举所有可能的路径,因为您的集合如此之小。但是,这样的事情不会扩展。

【讨论】:

【参考方案2】:

听起来像是Dijkstra's algorithm 的工作:

Dijkstra 算法,由荷兰计算机科学家 Edsger Dijkstra 于 1959 年构思,1 是一种图搜索算法,用于解决具有非负边路径成本的图的单源最短路径问题,输出最短路径树。该算法常用于路由。

***文章中也有实现细节。

【讨论】:

【参考方案3】:

可以更改某些shortest path algorithms,例如 Dijkstra 的,以按成本对每条路径进行加权,但还可以跟踪时间并在时间超过您的阈值时停止沿特定路径前进。应该找到让你在你的门槛以下的最便宜的方式

【讨论】:

【参考方案4】:

如果我知道我只需要按预定顺序处理 5 个城市,并且相邻城市之间只有 3 条路线,我会强行使用它。优雅没有意义。

另一方面,如果这是一项家庭作业,并且我应该生成一个可以实际扩展的算法,我可能会采取不同的方法。

【讨论】:

【参考方案5】:

我认为 Dijkstra 的算法是用来寻找最短路径的。

cmcculloh 正在寻找最低成本,但受限于他在 5 天内得到它。

因此,仅仅找到最快的方法不会让他以最便宜的方式到达那里,并且以最便宜的方式到达那里,不会在所需的时间内到达那里。

【讨论】:

【参考方案6】:

这是knapsack problem。重量是运输天数,利润应该是 5000 美元 - 航程成本。消除所有负面成本并从那里开始!

【讨论】:

【参考方案7】:

正如 Baltimark 所说,这基本上是一个线性规划问题。如果托运人的系数(1 表示包含,0 表示不包含)不是每条腿的(二进制)整数,这将更容易解决。现在你需要找到一些(二进制)整数线性规划(ILP)启发式,因为问题是 NP 难的。 链接见Wikipedia on integer linear programming;在我的线性规划课程中,我们至少使用了Branch and bound。

实际上,现在我想起来了,这种特殊情况可以在没有实际 ILP 的情况下解决,因为天数无关紧要,只要它

然后,我们可以使用此成本来修剪树中的其他搜索,这些搜索会因某些子树阶段结果的成本大于我们已经找到的结果,并从此不再搜索该子树。 这只有在我们知道在子树中搜索不会产生更好的结果时才有效,就像我们在成本不能为负时所做的那样。

希望这个漫无边际的帮助有点:)。

【讨论】:

以上是关于从给定的多个集合中找到最佳组合的主要内容,如果未能解决你的问题,请参考以下文章

数据结构 集合_集合实例(集合覆盖)

从集合中随机选择一个/多个函数并应用组合

从多个集合中创建所有可能的组合

如何从所有蛮力组合中找到最佳解决方案?

在Matlab中生成包含给定集合中至少一个元素的所有组合

如何找到从多个数组中提取的数字的最小总和?