2008-08-18 9 views
8

Angenommen, Sie haben eine Lieferung. Es muss von Punkt A zu Punkt B, Punkt B zu Punkt C und schließlich Punkt C zu Punkt D gehen. Sie brauchen es, um in fünf Tagen dorthin zu gelangen, wo das geringste Geld möglich ist. Es gibt drei mögliche Versendern für jedes Bein, jede mit ihrer eigenen unterschiedlichen Zeit und Kosten für jedes Bein:Finden Sie die beste Kombination aus einem gegebenen Satz von mehreren Sets

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 
       ) 

     ) 

) 

Wie würden Sie gehen über programmatisch die beste Kombination zu finden?

Mein bester Versuch so weit (dritter oder vierte Algorithmus) ist:

  1. Finden Sie den längsten Versender für jedes Bein
  2. beseitigen die „teueren“ ein
  3. den günstigster Versender für jedes Bein Finden
  4. berechnen die Gesamtkosten & Tage
  5. Wenn Tage sind akzeptabel, fertig, sonst, gehe zu 1

Schnell verspottet-up in PHP (beachten Sie, dass die Testanordnung unter swimmingly funktioniert, aber wenn man es mit der Testanordnung von oben versuchen, es nicht die richtige Kombination finden):

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

Ich denke, Ich muss tatsächlich etwas tun, wo ich buchstäblich jede Kombination einzeln mache (mit einer Reihe von Schleifen) und summiere die Gesamt "Punktzahl" von jedem, und finde das beste ....

EDIT: Um zu verdeutlichen, ist dies keine "Hausaufgabe" Aufgabe (ich bin nicht in der Schule). Es ist Teil meines aktuellen Projekts bei der Arbeit.

Die Anforderungen (wie immer) haben sich ständig geändert. Wenn ich zu dem Zeitpunkt, als ich anfing, an diesem Problem zu arbeiten, die aktuellen Einschränkungen erhalten habe, würde ich eine Variante des A * -Algorithmus (oder Dijkstra's oder kürzesten Pfad oder Simplex oder etwas) verwenden. Aber alles hat sich verändert und verändert, und das bringt mich dahin, wo ich gerade bin.

Also ich denke, das bedeutet, ich muss den ganzen Mist vergessen, den ich bis zu diesem Punkt gemacht habe und gehe einfach mit dem, was ich weiß, mit dem ich gehen sollte, das ist ein Pfadfindungsalgorithmus.

+0

+1 für die Annäherung an Morphing-über-Zeit-Anforderungen mit einem "lass uns diesen Mist Graben und neu beginnen" Gemütszustand. – Alex

Antwort

8

Konnte einige der shortest path algorithms, wie Dijkstra ändern, um jeden Pfad nach Kosten zu gewichten, aber auch die Zeit zu verfolgen und zu stoppen, einen bestimmten Weg zu gehen, wenn die Zeit Ihren Schwellwert überschreitet. Sollte ich das billigste finden, das Sie unter Ihre Schwelle auf diese Weise bringt,

5

Klingt wie das, was Sie haben, ist ein "lineares Programmierproblem". Es klingt auch wie ein Hausaufgabenproblem, nichts für ungut.

Die klassische Lösung für ein LP-Problem heißt "Simplex-Methode". Google es.

Um diese Methode verwenden zu können, muss das Problem korrekt formuliert sein, um Ihre Anforderungen zu beschreiben.

Dennoch kann es möglich sein, alle möglichen Pfade aufzuzählen, da Sie einen so kleinen Satz haben. So etwas wird jedoch nicht skalieren.

5

Klingt wie ein Job für Dijkstra's algorithm:

von holländischen Informatiker Edsger Dijkstra 1959 konzipiert Dijkstra-Algorithmus ist, 1 ein Graph Suchalgorithmus, der die Single-Source kürzesten Weg Problem für einen Graphen mit nicht löst negative Kantenpfadkosten, Ausgabe eines Baums für den kürzesten Pfad. Dieser Algorithmus wird häufig beim Routing verwendet.

Es gibt auch Implementierung Details in der Wikipedia-Artikel.

3

Wenn ich wusste, dass ich nur mit 5 Städten in einer vorherbestimmten Reihenfolge handeln musste, und dass es nur 3 Wege zwischen benachbarten Städten gab, würde ich rohe Gewalt haben es. Kein Grund, elegant zu sein.

Wenn dies andererseits eine Hausaufgabe wäre und ich einen Algorithmus erstellen sollte, der skalieren könnte, würde ich wahrscheinlich einen anderen Ansatz wählen.

-1

Ich denke, dass Dijkstra-Algorithmus ist für die Suche nach einem kürzesten Weg.

cmcculloh sucht nach den minimalen Kosten unterliegt der Beschränkung, dass er es dort in 5 Tagen bekommt.

Also, nur den schnellsten Weg zu finden wird ihn nicht dort am billigsten bringen, und dorthin für das billigste zu kommen, wird es dort in der erforderlichen Menge an Zeit nicht bekommen.

2

Dies ist ein knapsack problem. Die Gewichte sind die Tage auf der Durchreise, und der Gewinn sollte 5000 $ - Kosten der Bein sein. Beseitigen Sie alle negativen Kosten und gehen Sie von dort aus!

2

Wie Baltimark sagte, ist dies im Grunde ein lineares Programmierproblem. Wenn nur die Koeffizienten für die Verlader (1 für enthalten, 0 für nicht enthalten) keine (binären) ganzen Zahlen für jedes Bein wären, wäre dies leichter lösbar. Jetzt müssen Sie einige (binäre) Ganzzahl-Linear-Programmierung (ILP) Heuristiken finden, da das Problem NP-schwer ist. Siehe Wikipedia on integer linear programming für Links; auf meinem linearen Programmierkurs haben wir mindestens verwendet.

Eigentlich jetzt, dass ich daran denke, ist dieser spezielle Fall lösbar ohne tatsächliche ILP als die Anzahl der Tage spielt keine Rolle, solange es < = 5. Jetzt beginnen mit der Auswahl der günstigsten Träger für die erste Wahl (Conway 5 : 1000). Als nächstes wählen Sie wieder die billigsten, resultierenden 8 Tage und 4000 Währungseinheiten, was zu viel ist, so dass wir das abbrechen. Wenn wir auch andere ausprobieren, sehen wir, dass alle Ergebnisse Tage> 5 sind, also gehen wir zurück zur ersten Wahl und versuchen die zweitgünstigste (FedEx 2: 3000) und dann ups in der zweiten und fedex in der letzten. Dies ergibt insgesamt 4 Tage und 9000 Währungseinheiten.

Dann könnten wir diese Kosten verwenden, um andere Suchvorgänge in der Baumstruktur zu bereinigen, die um einiges mehr kosten würden als die, die wir bereits gefunden haben, und diesen Teilbaum von diesem Punkt an nicht durchsucht haben. Dies funktioniert nur so lange, wie wir wissen, dass die Suche im Teilbaum keine besseren Ergebnisse liefert, wie hier, wenn die Kosten nicht negativ sein können.

Ich hoffe, diese Wanderung half ein bisschen :).