Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 13 Min.

Clevere Lösungen

Die Entwicklung eines Optimierungsalgorithmus ist schwierig. Die Google OR-Tools zeigen hierbei großes Potenzial.
© Autor
Über die Anwendungen und die Lösungsansätze (Algorithmen) von Optimierungsproblemen hat der erste Teil dieser Serie ausführlich berichtet [1]. Die Fragen kommen aus dem Bereich Transport (kürzeste Wege), Personalplanung (Schichtpläne bei Beachtung von allerlei Nebenbedingungen), der Produktionsplanung (Zuteilung von Maschinenkapazitäten) und so weiter. Um diese Probleme zu lösen, gibt es eine Reihe von etablierten Algorithmen und Heuristiken, zum Beispiel Verfahren der linearen Programmierung oder Routing-Algorithmen, die dafür eingesetzt werden können und sich auch bewährt haben.In der Programmierpraxis ist das Verständnis und der grundsätzliche Lösungsansatz das eine; der andere Aspekt ist das Entwickeln des konkreten Algorithmus. Das ist nicht einfach, doch mithilfe von Bib­liotheken lässt sich der Weg erheblich abkürzen. Dazu gehören auch die Google OR-Tools, die in diesem Artikel vorgestellt werden [2].

Installation

Die Bibliothek selbst ist in C++ programmiert. Es liegen jedoch Wrapper (SDK) für die Anwendung in den Programmiersprachen Python, Java und für die .NET-Plattform (C#) vor. Von Interesse ist hier logischerweise letztere Variante. Entwickler haben die Wahl zwischen den kompilierten Bibliotheken und den Quellen, um die Bibliotheken selbst zu kompilieren. Es werden Binärdateien und Quellen für die Betriebssysteme Windows, macOS und Linux angeboten. Üblicherweise wird man die kompilierten Versionen der Bibliotheken nutzen.Wer die Google OR-Tools an eigene Probleme anpassen will oder diese tief in eigene Verfahren einbinden möchte, der benötigt die Pakete mit dem Quellcode. In diesem Artikel geht es um die Installation der binären Bibliotheken für Windows. Die Systemvoraussetzungen lauten:
  • Windows 10, 64-bit (x86_64)
  • Microsoft Visual Studio 2019 oder 2022
  • Microsoft Visual C++ Redistributable, Version x64
  • .NET Core 2.1 SDK Version 2.1.302 oder höher
Die meisten Punkte dürfte ein üblicher Windows-Entwicklungsrechner erfüllen. Aktualisieren Sie Visual Studio Version 2022 und installieren Sie die „Microsoft Visual C++ Re­dis­tri­bu­table“-Bibliotheken (Bild 1). Sie können deren Vorhandensein auch über Systemsteuerung | Programme prüfen (Bild 2).
Die Installationder Redistributable-Bibliotheken über Visual Studio(Bild 1) © Autor
Prüfungder Voraussetzungen in der Systemsteuerung(Bild 2) © Autor
Die eigentliche Bibliothek laden Sie dann von der Webseite des Projekts als ZIP-Archiv. Um die Integrität des Downloads zu testen, entpacken Sie das Archiv, benennen es mithilfe eines handhabbaren Namens um (naheliegend wäre „ORTools“) und rufen die PowerShell-Konsole für das Verzeichnis auf. Dort geben Sie folgenden Befehl ein:
tools\<span class="hljs-built_in">make</span> test_dotnet 
Dies stößt einen Testlauf für einige Beispiele der Bibliothek an. Die Ergebnisse werden in der Konsole angezeigt und sollten keinen Fehler ausweisen (Bild 3). Danach können Sie mit dem ersten Beispielen starten.
Der Testder Bibliothek in der PowerShell(Bild 3) © Autor
Um die Google OR-Tools zu verwenden, können Sie diese in eigenen Projekten auf herkömmliche Weise über den ­öffentlichen NuGet-Paketmanager einrichten. Suchen Sie – beispielsweise in einer neuen Konsolen- oder Windows-Forms-Anwendung – nach der entsprechenden Bibliothek mit der Bezeichnung Google.OrTools und installieren Sie diese im Projekt. Mit ihr haben Sie Zugriff auf die entsprechenden Methoden und Klassen.

Erste Gehversuche

Die Beispiele, die dem gerade heruntergeladenen ZIP-Archiv beigefügt sind, eignen sich sehr gut für den Einstieg. Im entpackten Archiv sind sie im Unterordner examples zu finden, sortiert nach den Programmiersprachen C++, Java und .NET (C#).Die Beispiele decken eine Reihe von typischen Problemfällen ab, die sich mit den Google OR-Tools lösen lassen, und können eine gute Ausgangsbasis sein, um die Algorithmen der Google OR-Tools in eigenen Programmen und für ähnliche Optimierungsprobleme anzuwenden.Um ein Beispiel zu testen, wählen Sie Traveling Salesperson Problem (TSP) [3], auf Deutsch das „Problem des Handlungsreisenden“. Google liefert zum TSP gleich mehrere Beispiele, die von der Grundversion ausgehend zunehmend umfassendere Restriktionen und Konkretisierungen ergänzen. Nun testen Sie das Grundbeispiel – zunächst auf Ebene der Kommandozeile: Gehen Sie zur Eingabeaufforderung und arbeiten Sie sich bis zum Ordner …\examples\dotnet\Tsp\bin\Debug\net6.0 vor. Er enthält die kompilierte Beispielanwendung und unter anderem auch eine ausführbare EXE-Datei. Rufen Sie diese auf. Das Programm wird ausgeführt und gibt das Ergebnis der Optimierung direkt auf der Konsole aus (Bild 4).
Die Ausführungdes TSP-Beispielprogramms und Ergebnisanzeige(Bild 4) © Autor

Das Problem des Handlungsreisenden

Die Aufgabe besteht darin, eine Reihenfolge für den Besuch mehrerer Orte eines Handlungsreisenden so zu wählen, dass kein Ort außer dem ersten Standort (Depot, Lager) mehr als einmal besucht wird. Die gesamte zurückgelegte Strecke des Handlungsreisenden soll dabei möglichst kurz und die letzte Station gleich der ersten Station sein (Bild 5).
Handlungsreisendehaben ihre ganz eigenen Probleme beim Festlegen ihrer Reiserouten(Bild 5) © Bild: Google [2]
Die Reiseroute beginnt und endet am Ort mit der Nummer 0 (0 -> 9 -> 5 -> … -> 7 -> 0) und führt zu einer ermittelten Länge der Reisestrecke. Nun interessieren der Blick in den Quellcode und dazu die folgenden Fragen:
  • Wie wird das Problem im Quellcode formuliert?
  • Welche Klassen stehen als Hilfen zur Verfügung?
  • In welcher Form werden die Koordinaten der Orte angegeben?
  • Wie wird die Zielfunktion, in diesem Fall die Minimierung der Gesamtstrecke, definiert?
  • Wie wird die Strecke zwischen den Orten angegeben oder errechnet?
  • Wie wird die Lösung bestimmt?
Mit anderen Worten: Wie einfach wird es, das Problem im Quellcode mithilfe der Google OR-Tools zu beschreiben und zu lösen? Diese Frage untersucht der nächste Abschnitt.

Problem mit den OR-Tools lösen

Öffnen Sie dazu das vorbereitete Projekt in Visual Studio, das sich im Ordner …\examples\dotnet\Tsp befindet, und sehen Sie es sich genauer an. Ein Blick auf die Projektmappe stimmt hoffnungsvoll. Es findet sich lediglich die Referenz auf die Google OR-Tools via NuGet-Package und die Klasse TSP. Dort spielt sich der gesamte Algorithmus in der Methode Main() ab. Diese soll nun schrittweise analysiert und dabei das Vorgehen zur Definition des Problems und zur Lösung mit der Bibliothek abgeleitet werden.Werfen Sie dazu einen Blick in Listing 1. Der Quellcode ist der genannten Methode Main() entnommen und dient hier als Leitfaden durch den Algorithmus. Der einfacheren Nachvollziehbarkeit wegen sind die Zeilen des Codes für diesen Artikel nummeriert. Aus den Originalquellen wurden alle Kommentare entfernt, sodass der Algorithmus noch neun Zeilen umfasst. Die Erläuterungen im Einzelnen:
Listing 1: TSP-Algorithmus aus der Beispiel-App
&lt;span class="hljs-title"&gt;public&lt;/span&gt; static void &lt;span class="hljs-type"&gt;Main&lt;/span&gt;(&lt;span class="hljs-type"&gt;String&lt;/span&gt;[] args) &lt;br/&gt;{ &lt;br/&gt;&lt;span class="hljs-number"&gt;1&lt;/span&gt;  &lt;span class="hljs-type"&gt;DataModel&lt;/span&gt; &lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;data&lt;/span&gt; = new &lt;span class="hljs-type"&gt;DataModel&lt;/span&gt;(); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;2  &lt;span class="hljs-type"&gt;RoutingIndexManager&lt;/span&gt; manager =&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      new &lt;span class="hljs-type"&gt;RoutingIndexManager&lt;/span&gt;(&lt;span class="hljs-title"&gt;data&lt;/span&gt;.&lt;span class="hljs-type"&gt;Locations&lt;/span&gt;.&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      &lt;span class="hljs-type"&gt;GetLength(0)&lt;/span&gt;, &lt;span class="hljs-title"&gt;data&lt;/span&gt;.&lt;span class="hljs-type"&gt;VehicleNumber&lt;/span&gt;, &lt;span class="hljs-title"&gt;data&lt;/span&gt;.&lt;span class="hljs-type"&gt;Depot&lt;/span&gt;); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;3  &lt;span class="hljs-type"&gt;RoutingModel&lt;/span&gt; routing = new &lt;span class="hljs-type"&gt;RoutingModel&lt;/span&gt;(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      &lt;span class="hljs-title"&gt;manager&lt;/span&gt;); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;4  var distanceCallback = new &lt;span class="hljs-type"&gt;ManhattanDistance&lt;/span&gt;(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      &lt;span class="hljs-title"&gt;data&lt;/span&gt;, &lt;span class="hljs-title"&gt;manager&lt;/span&gt;); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;5  int transitCallbackIndex = routing.&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      &lt;span class="hljs-type"&gt;RegisterTransitCallback&lt;/span&gt;(&lt;span class="hljs-title"&gt;distanceCallback&lt;/span&gt;.&lt;span class="hljs-type"&gt;Call&lt;/span&gt;); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;6  routing.&lt;span class="hljs-type"&gt;SetArcCostEvaluatorOfAllVehicles&lt;/span&gt;(&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      &lt;span class="hljs-title"&gt;transitCallbackIndex&lt;/span&gt;); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;7  &lt;span class="hljs-type"&gt;RoutingSearchParameters&lt;/span&gt; searchParameters =&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      operations_research_constraint_solver.&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      &lt;span class="hljs-type"&gt;DefaultRoutingSearchParameters&lt;/span&gt;(); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;8  &lt;span class="hljs-type"&gt;Assignment&lt;/span&gt; solution = routing.&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;      &lt;span class="hljs-type"&gt;SolveWithParameters&lt;/span&gt;(&lt;span class="hljs-title"&gt;searchParameters&lt;/span&gt;); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;9  &lt;span class="hljs-type"&gt;PrintSolution&lt;/span&gt;(&lt;span class="hljs-title"&gt;routing&lt;/span&gt;, &lt;span class="hljs-title"&gt;manager&lt;/span&gt;, &lt;span class="hljs-title"&gt;solution&lt;/span&gt;); &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-class"&gt;}&lt;/span&gt; 
Schritt 1: Definition des Datenmodells (Zeile 1). Das Datenmodell enthält alle Angaben zum Problem. Es wird in einer eigenen Klasse namens DataModel definiert (Listing 2). Die Orte (Locations) werden als Array dargestellt. Jeder Ort enthält dabei eine Angabe zu den Koordinaten ([x, y]). Konkret: Der Ort mit den Koordinaten [4, 4] ist im Beispiel der Ort mit dem Index 0 im Array und damit gemäß der Problemdefini­tion des TSP der Start- und der Zielort (Depot). Ebenso gibt es eine Variable, welche die Anzahl der Fahrzeuge (VehicleNumber = 1) und den Ort des Depots (Depot = 0) angibt.
Listing 2: Datenmodell des TSP-Algorithmus der App
class DataModel &lt;br/&gt;{ &lt;br/&gt;  public DataModel() &lt;br/&gt;  { &lt;br/&gt;    // Convert locations in meters using a city&lt;br/&gt;    // block dimension of &lt;span class="hljs-number"&gt;114&lt;/span&gt;m x &lt;span class="hljs-number"&gt;80&lt;/span&gt;m. &lt;br/&gt;    for (int i = &lt;span class="hljs-number"&gt;0&lt;/span&gt;; i &amp;lt; Locations.GetLength(&lt;span class="hljs-number"&gt;0&lt;/span&gt;); i++) &lt;br/&gt;    { &lt;br/&gt;      Locations[i, &lt;span class="hljs-number"&gt;0&lt;/span&gt;] *= &lt;span class="hljs-number"&gt;114&lt;/span&gt;; &lt;br/&gt;      Locations[i, &lt;span class="hljs-number"&gt;1&lt;/span&gt;] *= &lt;span class="hljs-number"&gt;80&lt;/span&gt;; &lt;br/&gt;    } &lt;br/&gt;  } &lt;br/&gt;  public int[,] Locations = { {&lt;span class="hljs-number"&gt;4&lt;/span&gt;, &lt;span class="hljs-number"&gt;4&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;2&lt;/span&gt;, &lt;span class="hljs-number"&gt;0&lt;/span&gt;},&lt;br/&gt;    {&lt;span class="hljs-number"&gt;8&lt;/span&gt;, &lt;span class="hljs-number"&gt;0&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;0&lt;/span&gt;, &lt;span class="hljs-number"&gt;1&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-number"&gt;1&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;5&lt;/span&gt;, &lt;span class="hljs-number"&gt;2&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;7&lt;/span&gt;, &lt;span class="hljs-number"&gt;2&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;3&lt;/span&gt;, &lt;span class="hljs-number"&gt;3&lt;/span&gt;},&lt;br/&gt;    {&lt;span class="hljs-number"&gt;6&lt;/span&gt;, &lt;span class="hljs-number"&gt;3&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;5&lt;/span&gt;, &lt;span class="hljs-number"&gt;5&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;8&lt;/span&gt;, &lt;span class="hljs-number"&gt;5&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-number"&gt;6&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;2&lt;/span&gt;, &lt;span class="hljs-number"&gt;6&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;3&lt;/span&gt;, &lt;span class="hljs-number"&gt;7&lt;/span&gt;},&lt;br/&gt;    {&lt;span class="hljs-number"&gt;6&lt;/span&gt;, &lt;span class="hljs-number"&gt;7&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;0&lt;/span&gt;, &lt;span class="hljs-number"&gt;8&lt;/span&gt;}, {&lt;span class="hljs-number"&gt;7&lt;/span&gt;, &lt;span class="hljs-number"&gt;8&lt;/span&gt;} }; &lt;br/&gt;  public int VehicleNumber = &lt;span class="hljs-number"&gt;1&lt;/span&gt;; &lt;br/&gt;  public int Depot = &lt;span class="hljs-number"&gt;0&lt;/span&gt;; &lt;br/&gt;}; 
Damit ist das TSP vollständig beschrieben. Ein Hinweis: Beim TSP ist die Anzahl der Fahrzeuge per Definition gleich 1. In anderen Routing-Problemen, zum Beispiel dem allgemeinen Vehicle-Routing-Problem (VRP), kann diese Anzahl auch auf einen höheren Wert festgelegt werden.Schritt 2: Definition der Klasse RoutingIndexManager (Zeile 2). Diese Klasse stammt aus den Google OR-Tools und erwartet einige Argumente zur Initialisierung: Zuerst die Anzahl der Zeilen einer Distanzmatrix für die zu besuchenden Orte; eine Distanzmatrix ist eine zweidimensionale Matrix, die Auskunft über die Entfernung zwischen zwei beliebigen Orten gibt. Da jeder Ort mit jedem anderen Ort kombiniert werden kann, entspricht die Anzahl der Zeilen und Spalten in dieser Matrix jeweils der Anzahl der Orte (siehe zum Thema auch den Kasten Die Sache mit den Entfernungen). Der nächste Parameter ist die Anzahl der Fahrzeuge und der dritte Parameter die Angabe des Depots.

Die Sache mit den Entfernungen

Für die Bestimmung des kürzesten Wegs ist die Kenntnis der Entfernungen zwischen den Orten von entscheidender Bedeutung. Das Routing-Modell verwendet diese Angaben für die Berechnungen. Da die betreffenden Fahrzeuge theoretisch alle Orte in einer beliebigen Reihenfolge anfahren könnten, ist es notwendig, die Entfernung zwischen allen Ortskombinationen zu kennen oder diese errechnen zu können. Dazu gibt es unterschiedliche Ansätze in Theorie und Praxis, wie sich die Entfernung zwischen zwei beliebigen Orten bestimmen lässt.
Schritt 3: Definition des Routing-Modells (Zeile 3): Dieses Modell wird mit dem eben definierten RoutingIndexManager als Parameter initialisiert.Schritt 4: Eine Rückruffunktion für Entfernungen (Zeilen 4 und 5). Die Entfernung zwischen den Standorten ist die Basis für die Suche der Route. Das Lösungsverfahren der Google OR-Tools benötigt einen Verweis auf eine Methode, auf deren Basis es die Entfernung zwischen zwei beliebigen Orten bestimmen kann. Wie diese Methode konkret ausgelegt ist, also ob sie die Entfernung berechnet, die Werte aus einer Matrix ausliest oder ein webbasiertes API abfragt, spielt dabei keine Rolle. Im vorliegenden Fall definieren Sie eine Callback-Funktion für die Berechnung der Entfernung wie folgt:
<span class="hljs-keyword">var</span> distanceCallback =
  <span class="hljs-keyword">new</span> <span class="hljs-type">ManhattanDistance</span>(…) 
Diese Callback.-Funktion ist dann noch im Routing-Modell zu registrieren.Schritt 5: Definition des Kostenmodells (Zeile 6). Was sind die Kosten in einem Optimierungsverfahren? Das ist der numerische Wert, anhand dessen die Optimierung erfolgt. In diesem Fall ist es die ermittelte Entfernung zwischen den Standorten. Gesucht ist eine Lösung zu minimalen Kosten, das heißt mit minimaler Gesamtentfernung. Dazu definieren Sie die Callback-Funktion für die Entfernung als Kostenfunktion mit folgender Zeile:
routing<span class="hljs-selector-class">.SetArcCostEvaluatorOfAllVehicles</span>
  (transitCallbackIndex) 
Über die Kostenfunktion kann das Optimierungsverhalten des Algorithmus gesteuert werden.Schritt 6: Definition der Suchparameter (Zeile 7). Hier wird es spannend. Welches Lösungsverfahren (Heuristik) wird angewendet, um das Optimierungsproblem zu lösen? Im vorliegenden Fall handelt es sich um ein Tourenplanungsproblem. Hier hat die Wissenschaft eine Reihe von Lösungsverfahren entwickelt. Das Spektrum reicht über exakte Verfahren (sehr kleine Problemstellungen) über lokale Suchheuristiken bis hin zu Steuerung der lokalen Suchheuristiken durch übergeordnete Metaheuristiken. Die Google OR-Tools bieten über die Definition der Suchparameter die Möglichkeit, mit unterschiedlichen Lösungsverfahren zu experimentieren und diese dann auch einzusetzen. Es sind Algorithmen aus unterschiedlichen Kategorien implementiert. Die zugrunde liegenden Algorithmen sind kurz und knapp in der Dokumentation beschrieben [4]. Für ein weitergehendes Verständnis der Algorithmen ist dann ein umfassendes Studium der angegebenen Literatur notwendig. Die Bibliothek erlaubt es jedoch, die Algorithmen anzuwenden, ohne sie selbst implementieren zu müssen. In diesem Beispiel werden die Parameter auf den Standardeinstellungen belassen, das heißt, die Wahl des Verfahrens wird dem Algorithmus überlassen.Schritt 7: Problemlösung (Zeile 8). Die eigentliche Lösung des Problems erschöpft sich hier im Aufruf einer einzigen Methode SolveWithParameters(). Ihr werden die zuvor definierten Lösungsparameter als Argument übergeben. Hinter dem Aufruf dieser Methode verbirgt sich das gesamte Lösungsverfahren, das durch die Google OR-Tools bereitgestellt wird.Schritt 8: Ausgabe der Lösung (Zeile 9). Es wird eine eigene Methode namens PrintSolution() aufgerufen. Im Beispiel wird die Lösung als Folge der anzufahrenden Standorte und der errechneten Gesamtstrecke auf der Kommandozeile ausgegeben. In einer produktiven Anwendung werden die errechneten Ergebnisse an den Aufrufer zurückgegeben und dort weiterverarbeitet, im gewünschten Format präsentiert und gegebenenfalls grafisch aufbereitet angezeigt.Die Suche nach einer Lösung für ein Optimierungsproblem muss nicht immer zu einem Ergebnis führen. Eine Statusvariable liefert die Ergebnisse der Suche in der folgenden Form:
  • 0: Problem noch nicht gelöst.
  • 1: Problem erfolgreich gelöst.
  • 2: Keine Lösung gefunden.
  • 3: Keine Lösung vor Ablauf des Zeitintervalls gefunden.
  • 5: Model beziehungsweise Parameter nicht konsistent.
Ob eine Lösung gefunden wird und wie gut diese ist, hängt nicht unmaßgeblich von der verfügbaren Rechenzeit ab. Diese ist bei einem Optimierungsproblem aus der Praxis stets begrenzt. Auch dazu ein Beispiel: Angenommen, Sie suchen eine gute Route für ein Transportfahrzeug, wenn während der laufenden Anfahrt ein zusätzlicher Auftrag eintrifft. Sie müssen also ad hoc umplanen. Es handelt sich um ein dynamisches Optimierungsproblem. Stünde beliebig viel Zeit zur Verfügung, dann könnte man alle denkbaren Varianten für die Einplanung des neuen Auftrags errechnen und die beste – dann optimale – Lösung finden. Diese Zeit haben Sie jedoch nicht, denn das Fahrzeug ist bereits unterwegs und benötigt recht zügig ein Update in Bezug auf die neue Streckenführung. Der Algorithmus muss daher in einer begrenzten Zeit eine gültige und möglichst gute Lösung finden. Den Google OR-Tools kann daher eine maximale Suchzeit übergeben werden. Vor diesem Hintergrund ist dann auch der Status des Lösungsverfahrens zu interpretieren.Das hier anhand des Beispiels der Tourenplanung demonst­rierte Vorgehen lässt sich auch auf alle anderen Lösungsverfahren der Google OR-Tools übertragen. Das Problem wird mithilfe einer geeigneten Datenstruktur, gefolgt von der Definition der Zielfunktion und der Nebenbedingungen (Einschränkungen), beschrieben. Die Lösung des Problems erschöpft sich dann in der Regel im Aufruf einer einzelnen Methode, die das Ergebnis ermittelt. Die Dokumentation beschreibt das konkrete Vorgehen für unterschiedliche Optimierungsprobleme.

Experimente mit Python

Python? Moment mal, gerade standen die Google OR-Tools für .NET (C#) im Zentrum, und nun soll es mit Python weitergehen? Genau, bei diesem Thema lohnt es sich, einen kleinen Exkurs einzulegen und über den .NET-Tellerrand hi­nauszuschauen. Die Entwicklung und vor allem die Kon­figuration der Algorithmen erfordert es meist, minimale Anpassungen am Algorithmus vorzunehmen oder mit unterschiedlichen Parametern zu experimentieren. Dazu bietet sich die interaktive Ausführung des betreffenden Algorithmus in einem Jupyter-Notebook an.Für einen Großteil der beigefügten Beispiele gibt es vorbereitete Dateien für die Online-Version des Jupyter-Notebooks von Google, dem „Google Colaboratory“ (Bild 6), die auch online verfügbar sind [5], was einen Google-Account erfordert. Vollziehen Sie das TSP-Beispiel nach. Im Notebook müssen Sie mithilfe des ersten Codeabschnitts die Google OR-Bibliothek installieren. Danach können Sie den eigentlichen Algorithmus zum TSP interaktiv ausführen und erhalten nach kurzer Zeit das berechnete Ergebnis (Bild 7).
Die Algorithmender Beispiele lassen sich direkt im Jupyter-Notebook von Google öffnen(Bild 6) © Autor
Experimentelle Ausführungder Algorithmen mit OR-Tools im Jupyter-Notebook(Bild 7) © Autor
Auf diese Weise können Sie die Lauffähigkeit des Algorithmus schneller prüfen und ihn nach Bedarf justieren. Passt alles, dann können Sie das Beispiel in .NET umsetzen und das Lösungsverfahren in eine aktive Anwendung, zum Beispiel mit grafischer Nutzeroberfläche, einbauen. Der Transfer von Python zu C# (.NET) geht dann schnell von der Hand, da die Google OR-Bibliothek für beide Programmiersprachen zur Verfügung steht.

Praxistauglichkeit

Die genannten Probleme sind nicht nur theoretischer Natur, sondern tauchen auch in der betrieblichen Praxis auf. Weist eine Frage in der Anforderungsdefinition auf Probleme in diesem Bereich hin, dann sind die Google OR-Tools mehr als einen Blick wert. Dabei wird das Problem in der Praxis selten eins zu eins mit dem Musterbeispiel übereinstimmen. In einem ersten Schritt muss das Problem genau spezifiziert und einer Problemklasse zugeordnet werden. Geht es beispielsweise um eine Frage der Routenoptimierung für Transportmittel, dann ist das Problem in Bezug auf die folgenden Größen genau zu spezifizieren:
  • Welches Grundproblem aus der Theorie kann als Basis herangezogen werden (TSP, VRP, Pickup- und Delivery)?
  • Welche Einschränkungen und Vorgaben sind zu beachten (Zeitfenster, Ladekapazitäten der Fahrzeuge)?
  • Wie viele Fahrzeuge stehen maximal zur Verfügung?
  • Wie wird die Entfernung zwischen den Orten bestimmt?
  • Welche Dimension hat das Problem, das heißt, wie viele Orte sind zu verplanen?
  • Welche Güte der Lösung ist erforderlich?
Anhand dieser und weiterer Vorgaben lässt sich dann bestimmen, wie gut ein reales Problem der Tourenplanung auf ein vorhandenes Problem der Google OR-Tools abgebildet und damit gelöst werden kann. Dieses Vorgehen gilt ebenso für Probleme aus den anderen Kategorien/Klassen. Die Dokumentation der Google OR-Tools bietet eine gute Übersicht über das Spektrum der lösbaren Optimierungsprobleme. Ergänzt wird sie durch die zahlreichen Beispiele, die in Form von Quellcode und ausführbaren Programmen vorliegen.Der grundsätzliche Ablauf lässt sich gemäß der Schrittfolge in Bild 8 zusammenfassen. Die allgemeinen Schritte haben wir dabei um das Beispiel der Tourenplanung ergänzt. Ebenso macht das Bild deutlich, an welcher Stelle Bib­liotheken wie die Google OR-Library zum Einsatz kommen.
Vom realen Problemzur Lösung mithilfe der Google OR-Tools(Bild 8) © Autor

Einschränkungen

Die vorgestellten Lösungen für durchgängig komplexe Optimierungsprobleme dürfen nicht darüber hinwegtäuschen, dass nicht garantiert werden kann, dass die gefundenen ­Lösungen optimal sind. Beispielsweise gilt für die hier oft als Beispiel herangezogenen Vehicle-Routing-Probleme, dass diese ab einer bestimmten Anzahl von Orten nicht mehr lösbar sind und die Zeit, um alle möglichen Lösungen zu verifizieren, exponentiell mit der Größe des Problems wächst. Bei ausreichend großen Problemen kann es Jahre dauern, bis Software die optimale Lösung findet. Infolgedessen liefern auch die hier vorgestellten OR-Tools manchmal nur gute, aber nicht optimale Lösungen.Um eine bessere Lösung zu finden, sind die Suchoptionen für den Solver anzupassen. Das geht oft nur über umfassende Experimente. Die Wissenschaft hat für viele Optimierungsprobleme bereits gute Lösungsverfahren entwickelt, welche die Güte der Lösung der OR-Tools an einigen Stellen übertreffen werden.Sehr einfache Fragen, beispielsweise Rundreiseprobleme ohne Zeitfenster und Ladebeschränkungen und gleichzeitig mir nur einer begrenzten Anzahl von Standorten, lassen sich noch sehr gut mit einer Vielzahl von Algorithmen lösen, wie Studien und Experimente ergeben haben; dabei wurden standardisierte, künstlich generierte Problemdaten verarbeitet. Ab einer bestimmten Komplexität und Größe des Pro­blems sind jedoch Transportprobleme nur noch mit einem Suchverfahren zu bearbeiten. Je nach ihrer Eignung für das spezifische Problem ermitteln diese Verfahren korrekte, brauchbare oder gute Lösungen. Optimal wird eine solche Lösung nur durch Zufall und im Einzelfall sein. Ob es so ist, wird man in der Regel auch nicht wissen.Diese Einschränkung bei der Qualität der Ergebnisse muss man immer im Hinterkopf behalten. Sie schränken jedoch die Tauglichkeit der Google OR-Bibliothek nicht ein.

Fazit und Ausblick

Die Google OR-Tools sind eine interessante Sammlung von Algorithmen und Heuristiken, um Optimierungsprobleme aus den unterschiedlichsten Bereichen zu lösen. Die Probleme sind zunächst bekannte Fragen, die immer wieder in der Wissenschaft betrachtet, analysiert und gelöst werden. Es bleibt jedoch nicht bei der Theorie. Reale Probleme in der Wirtschaft lassen sich auch oft auf diese Basisfragen zurückführen. Dann können Bibliotheken eine große Hilfe bieten.Die Google OR-Tools sind auch deshalb von größerem Interesse, da sie für unterschiedliche Programmiersprachen und Entwicklungsansätze bereitstehen. Sie werden fortlaufend gepflegt und weiterentwickelt, und die vielen bereitgestellten Beispiele motivieren zum Experimentieren. Sie bieten damit eine erste Anlaufstelle für jeden, der ein konkretes eigenes Problem aus diesem Bereich lösen möchte.

Fussnoten

  1. Elena Bochkor, Veikko Krypkzyk, Eine gute Lösung finden, Methoden des Operations Research, Teil 1, dotnetpro 8/2022, Seite 100 ff., http://www.dotnetpro.de/A2208ORTools
  2. Route. Schedule. Plan. Assign. Pack. Solve. – Google OR Tools, http://www.dotnetpro.de/SL2209ORTools1
  3. Travelling salesman problem bei Wikipedia, http://www.dotnetpro.de/SL2209ORTools2
  4. Distance Matrix API overview, http://www.dotnetpro.de/SL2209ORTools3
  5. Willkommen bei Colaboratory, http://www.dotnetpro.de/SL2209ORTools4
  6. Routing Options: First solution strategy, http://www.dotnetpro.de/SL2209ORTools5

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige