Найдите лучшую комбинацию из заданного набора нескольких наборов

Скажем, у вас есть посылка. Он должен пройти из точки 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. Найдите самого длинного грузоотправителя для каждого этапа
  2. Устраните самый «дорогой»
  3. Найдите самого дешевого грузоотправителя на каждом этапе
  4. Рассчитайте общую стоимость и дни
  5. Если дни приемлемы, закончить, иначе перейти к 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 * (или алгоритм Дейкстры, или кратчайший путь, или симплекс, или что-то в этом роде). Но все трансформировалось и менялось, и это подводит меня к тому месту, где я сейчас нахожусь.

Так что, я думаю, это означает, что мне нужно забыть обо всем дерьме, которое я натворил до этого момента, и просто использовать то, что, как я знаю, мне следует делать, а именно алгоритм поиска пути.

+1 за приближение к требованиям морфинга с течением времени с настроением «давайте бросим это дерьмо и начнем заново».

Alex 03.07.2012 19:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
10
1
2 224
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Похоже, то, что у вас есть, называется «проблемой линейного программирования». Это тоже похоже на домашнее задание, без обид.

Классическое решение проблемы LP называется «симплекс-методом». Погугли это.

Однако, чтобы использовать этот метод, вы должны правильно сформулировать проблему, чтобы описать ваши требования.

Тем не менее, возможно, удастся перечислить все возможные пути, поскольку у вас такой небольшой набор. Однако такая вещь не масштабируется.

Похоже на работу для Алгоритм Дейкстры:

Dijkstra's algorithm, conceived by Dutch computer scientist Edsger Dijkstra in 1959, 1 is a graph search algorithm that solves the single-source shortest path problem for a graph with non negative edge path costs, outputting a shortest path tree. This algorithm is often used in routing.

Подробности реализации также можно найти в статье в Википедии.

Ответ принят как подходящий

Можно изменить некоторые из алгоритмы кратчайшего пути, например, у Дейкстры, чтобы взвесить каждый путь по стоимости, но также отслеживать время и прекращать движение по определенному пути, если время превышает ваш порог. Должен найти самый дешевый, который позволит вам таким образом оказаться ниже вашего порога

Если бы я знал, что мне нужно иметь дело только с 5 городами в заранее определенном порядке, и что между соседними городами есть только 3 маршрута, я бы использовал грубую силу. Нет смысла быть элегантным.

С другой стороны, если бы это было домашнее задание, и я должен был бы разработать алгоритм, который действительно мог бы масштабироваться, я бы, вероятно, выбрал другой подход.

Я думаю, что алгоритм Дейкстры предназначен для поиска кратчайшего пути.

cmcculloh ищет минимальную стоимость с ограничением, что он получит ее за 5 дней.

Таким образом, простое нахождение самого быстрого пути не приведет к его дешевому результату, а попадание туда по самому дешевому не приведет к тому, что он окажется там за требуемое количество времени.

Это проблема с рюкзаком. Веса - дни в пути, а прибыль должна составлять 5000 долларов - стоимость ноги. Устраните все негативные издержки и приступайте к делу!

Как сказал Балтимарк, это в основном проблема линейного программирования. Если бы только коэффициенты для грузоотправителей (1 для включенных, 0 для не включенных) не были (двоичными) целыми числами для каждого участка, это было бы легче решить. Теперь вам нужно найти некоторые эвристики (двоичного) целочисленного линейного программирования (ILP), поскольку проблема NP-сложная. См. Ссылки в Википедия по целочисленному линейному программированию; на моем курсе линейного программирования мы использовали не менее Ветвь и переплет.

На самом деле теперь, когда я думаю об этом, этот особый случай можно решить без реальной ILP, поскольку количество дней не имеет значения, пока оно равно 5, поэтому мы возвращаемся к первому выбору и пробуем второй самый дешевый (FedEx 2: 3000), а затем повышаем. во втором и FedEx в последнем. Это дает нам 4 дня и 9000 денежных единиц.

Затем мы могли бы использовать эту стоимость для сокращения других поисков в дереве, стоимость которых на некоторых этапах поддерева была бы больше, чем тот, который мы уже нашли, и оставить это поддерево без поиска с этого момента. Это работает только до тех пор, пока мы знаем, что поиск в поддереве не даст лучших результатов, как мы делаем здесь, когда затраты не могут быть отрицательными.

Надеюсь, эта бессвязность немного помогла :).

Другие вопросы по теме