Найти лучшую комбинацию из заданного множества множеств - PullRequest
10 голосов
/ 18 августа 2008

Скажем, у вас есть груз. Он должен пройти из пункта А в пункт В, из пункта В в пункт С и, наконец, из пункта С в пункт 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 (обратите внимание, что приведенный ниже тестовый массив работает плавно, но если вы попробуете его с тестовым массивом сверху, он не найдет правильной комбинации):

<code>$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 "

"; while ($ totalDays> $ maxDays && $ times <500) { $ totalDays = 0; $ раз ++; $ худшийShipper = нуль; $ longestShippers = null; $ cheapShippers = null; foreach ($ shippers as $ legName => $ leg) { // найти самую длинную поставку для каждого этапа (в днях) переменная не установлена ​​($ 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"]; // печать $ shipper ["totalCost"]. "& lt;? & gt;". $ худшийShipper ["totalCost"]. ";"; if (empty ($ худшийShipper) || $ shipper ["totalCost"]> $ худшийShipper ["totalCost"]) { $ худшийShipper = $ shipper; $ худшийShipperLeg = $ leg; } } // выводим «худший грузоотправитель: shippers [$ Художественная ширма» [{$ Художник ['имя']}] ». $ грузоотправители [$ worstShipperLeg] [$ worstShipper [ "имя"]] [ "дней"]; переменные не установлены ($ грузоотправители [$ worstShipperLeg] [$ worstShipper [ "имя"]]); print "

Next:

";
            print_r($shippers);
            print "

"; foreach ($ shippers as $ legName => $ leg) { // найти самую дешевую отправку для каждой очереди переменная не установлена ​​($ cheapestShippers [$ legName]); $ lowerCost = null; foreach ($ leg as $ shipperName => $ shipper) { if (empty ($ lowerCost) || $ shipper ["cost"] <$ lowerCost) { $ cheapShippers [$ legName] ["days"] = $ shipper ["days"]; $ cheapShippers [$ legName] ["cost"] = $ shipper ["cost"]; $ cheapShippers [$ legName] ["name"] = $ shipperName; $ lowerCost = $ shipper ["cost"]; } } // пересчитаем дни и посмотрим, не достигли ли мы максимальных дней ... $ totalDays + = $ cheapShippers [$ legName] ['days']; } // print "<h2> totalDays: $ totalDays "; } печать "

Избранные грузоотправители:

";
        print_r($cheapestShippers);
        print "
";

Я думаю, что мне, возможно, придется сделать что-то вроде того, где я буквально составляю каждую комбинацию одну за другой (с серией циклов), складываю общую «оценку» каждой и нахожу лучшую ... .

EDIT:Чтобы уточнить, это не домашнее задание (я не в школе). Это часть моего текущего проекта на работе.

Требования (как всегда) постоянно менялись. Если бы мне дали текущие ограничения в то время, когда я начал работать над этой проблемой, я бы использовал какой-то вариант алгоритма A * (или Дейкстры, или кратчайшего пути, или симплекса, или чего-то еще). Но все меняется и меняется, и это приводит меня туда, где я сейчас нахожусь.

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

Ответы [ 7 ]

8 голосов
/ 18 августа 2008

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

5 голосов
/ 18 августа 2008

Звучит как работа для Алгоритм Дейкстры :

Алгоритм Дейкстры, разработанный голландским ученым-программистом Эдсгером Дейкстрой в 1959 году, 1 - это алгоритм поиска графа, который решает проблему кратчайшего пути из одного источника для графа с неотрицательными затратами на ребро, выводя кратчайший путь путь дерева. Этот алгоритм часто используется при маршрутизации.

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

5 голосов
/ 18 августа 2008

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

Классическое решение проблемы ЛП называется «Симплексный метод». Google это.

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

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

3 голосов
/ 18 августа 2008

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

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

2 голосов
/ 19 сентября 2008

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

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

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

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

2 голосов
/ 23 августа 2008

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

0 голосов
/ 18 августа 2008

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

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

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

...