2008-08-18 5 views
8

Скажите, что у вас есть отгрузка. Ему нужно перейти от точки А до точки В, точки В до точки С и, наконец, точки С до точки 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>"; 

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

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

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

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

+0

+1 для приближающихся требований к морфингам с течением времени с «давайте отбросить это дерьмо и начать все». – Alex 2012-07-03 15:12:50

ответ

8

Возможно, некоторые из shortest path algorithms, как и у Дийкстры, должны весить каждый путь по стоимости, а также отслеживать время и останавливаться по определенному пути, если время превышает ваш порог. Должен найти самый дешевый, который подведет вас под своим порогом

5

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

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

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

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

5

Похоже, работа для Dijkstra's algorithm:

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

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

3

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

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

-1

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

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

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

2

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

2

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

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

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

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

Смежные вопросы