From 6206aa87917804892374484c6df85ca88c82518d Mon Sep 17 00:00:00 2001 From: Sean Reich Date: Mon, 8 Jul 2024 18:26:18 +0200 Subject: [PATCH] JavaDoc ExampleGraphs --- .idea/workspace.xml | 32 +-- OurApplication/OurApplication.java | 6 - graph/ExampleGraphs.java | 248 ++++++++++-------- .../OurApplication/OurApplication.class | Bin 3016 -> 2847 bytes .../ProjektGraph/graph/ExampleGraphs.class | Bin 6300 -> 6300 bytes 5 files changed, 152 insertions(+), 134 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index f43f747..01c547d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -6,15 +6,9 @@ - - - - - - - - - + + + - { + "keyToString": { + "Application.Display.executor": "Run", + "Application.OurApplication.executor": "Run", + "Application.OurLegendArea.executor": "Run", + "RunOnceActivity.ShowReadmeOnStart": "true", + "git-widget-placeholder": "main", + "kotlin-language-version-configured": "true", + "last_opened_file_path": "C:/Git/ProjektGraphMain" } -}]]> +} diff --git a/OurApplication/OurApplication.java b/OurApplication/OurApplication.java index 055edfb..3ba9a59 100644 --- a/OurApplication/OurApplication.java +++ b/OurApplication/OurApplication.java @@ -43,13 +43,7 @@ public class OurApplication { */ public static void main(String[]args){ - Random random = new Random(); - DirectedGraph myGraph = new DirectedGraph<>(); - - ExampleGraphs temp = new ExampleGraphs(); - //myGraph = temp.example2(); - //sean: Ich wollte erst hier dann das ausgewählte Beispiel reinhauen, jedoch wird das hier nur einmal am Anfang aufgerufen System.out.println(myGraph.toString()); diff --git a/graph/ExampleGraphs.java b/graph/ExampleGraphs.java index 3208d3b..5a47940 100644 --- a/graph/ExampleGraphs.java +++ b/graph/ExampleGraphs.java @@ -1,24 +1,30 @@ package graph; import java.awt.*; -import java.util.ArrayList; -import java.util.List; +/** + * Class containing example graphs for demonstration purposes. + */ public class ExampleGraphs { + /** + * Creates an example graph suitable for demonstrating Dijkstra's and A* algorithms. + * This graph contains multiple nodes connected by weighted edges, allowing efficient + * calculation of shortest paths from a start node to an end node. + * + * @return Directed graph instance for example 1. + */ public DirectedGraph example1() { /* - * Erstellt einen Beispielgraphen (Beispiel 4), der sich ideal für die Demonstration der Funktionsweise + * Erstellt einen Beispielgraphen, der sich ideal für die Demonstration der Funktionsweise * des Dijkstra- und A*-Algorithmus eignet. Der Graph enthält eine Vielzahl von Knoten, die durch * gewichtete Kanten verbunden sind. Dies ermöglicht es, den kürzesten Weg von einem Startknoten zu einem - * Zielknoten effizient zu berechnen und die Unterschiede zwischen den beiden Algorithmen in der - * Pfadfindung zu veranschaulichen. - * + * Zielknoten effizient zu berechnen und die Pfadfindung der beiden Algorithmen zu veranschaulichen. * */ - DirectedGraph example4 = new DirectedGraph<>(); + DirectedGraph example1 = new DirectedGraph<>(); // Erstellung der Knoten mit Koordinaten MarkedVertex A = new MarkedVertex<>(50, 250, "Start", null, null); @@ -32,48 +38,53 @@ public class ExampleGraphs { // Hinzufügen der Knoten zum Graphen - example4.addVertex(A); - example4.addVertex(B); - example4.addVertex(C); - example4.addVertex(D); - example4.addVertex(E); - example4.addVertex(F); - example4.addVertex(G); - example4.addVertex(H); + example1.addVertex(A); + example1.addVertex(B); + example1.addVertex(C); + example1.addVertex(D); + example1.addVertex(E); + example1.addVertex(F); + example1.addVertex(G); + example1.addVertex(H); // Erstellung der Kanten mit Gewichtungen - example4.addEdge(new MarkedEdge<>("AB", A, B, new EdgeWeightMarking(4))); - example4.addEdge(new MarkedEdge<>("AC", A, C, new EdgeWeightMarking(2))); - example4.addEdge(new MarkedEdge<>("BC", B, C, new EdgeWeightMarking(5))); - example4.addEdge(new MarkedEdge<>("BD", B, D, new EdgeWeightMarking(10))); - example4.addEdge(new MarkedEdge<>("CD", C, D, new EdgeWeightMarking(3))); - example4.addEdge(new MarkedEdge<>("CE", C, E, new EdgeWeightMarking(7))); - example4.addEdge(new MarkedEdge<>("DE", D, E, new EdgeWeightMarking(2))); - example4.addEdge(new MarkedEdge<>("DF", D, F, new EdgeWeightMarking(2))); - example4.addEdge(new MarkedEdge<>("EF", E, F, new EdgeWeightMarking(5))); - example4.addEdge(new MarkedEdge<>("EG", E, G, new EdgeWeightMarking(10))); - example4.addEdge(new MarkedEdge<>("FG", F, G, new EdgeWeightMarking(3))); - example4.addEdge(new MarkedEdge<>("FH", F, H, new EdgeWeightMarking(6))); - example4.addEdge(new MarkedEdge<>("GH", G, H, new EdgeWeightMarking(1))); + example1.addEdge(new MarkedEdge<>("AB", A, B, new EdgeWeightMarking(4))); + example1.addEdge(new MarkedEdge<>("AC", A, C, new EdgeWeightMarking(2))); + example1.addEdge(new MarkedEdge<>("BC", B, C, new EdgeWeightMarking(5))); + example1.addEdge(new MarkedEdge<>("BD", B, D, new EdgeWeightMarking(10))); + example1.addEdge(new MarkedEdge<>("CD", C, D, new EdgeWeightMarking(3))); + example1.addEdge(new MarkedEdge<>("CE", C, E, new EdgeWeightMarking(7))); + example1.addEdge(new MarkedEdge<>("DE", D, E, new EdgeWeightMarking(2))); + example1.addEdge(new MarkedEdge<>("DF", D, F, new EdgeWeightMarking(2))); + example1.addEdge(new MarkedEdge<>("EF", E, F, new EdgeWeightMarking(5))); + example1.addEdge(new MarkedEdge<>("EG", E, G, new EdgeWeightMarking(10))); + example1.addEdge(new MarkedEdge<>("FG", F, G, new EdgeWeightMarking(3))); + example1.addEdge(new MarkedEdge<>("FH", F, H, new EdgeWeightMarking(6))); + example1.addEdge(new MarkedEdge<>("GH", G, H, new EdgeWeightMarking(1))); - return example4; + return example1; } + /** + * Creates an example grid graph where each node is connected to its horizontal and vertical neighbors. + * All edges have the same weight. The start node is in one corner of the grid, and the end node is + * in the opposite corner, demonstrating the efficiency of the A* algorithm in structured grid graphs. + * + * @return Directed graph instance for example 2. + */ public DirectedGraph example2() { /* - * Beispiel 1 zeigt ein Gitter aus Knoten und Kanten, bei dem jeder Knoten mit + * Beispiel 2 zeigt ein Gitter aus Knoten und Kanten, bei dem jeder Knoten mit * seinen horizontal und vertikal benachbarten Knoten verbunden ist. Alle Kanten * haben die gleiche Gewichtung. Der Startknoten befindet sich in einer Ecke - * des Gitters und der Zielknoten in der gegenüberliegenden Ecke. - * - * Ziel: - * Demonstrieren, wie der A*-Algorithmus durch gezieltere Suche effizienter ist + * des Gitters und der Zielknoten in der gegenüberliegenden Ecke. Dadurch wird gezeigt, + * wie der A*-Algorithmus durch gezieltere Suche effizienter ist * als der Dijkstra-Algorithmus in einem strukturierten Gittergraphen. * */ - DirectedGraph example1 = new DirectedGraph<>(); + DirectedGraph example2 = new DirectedGraph<>(); int size = 5; MarkedVertex[][] vertices = new MarkedVertex[size][size]; @@ -88,7 +99,7 @@ public class ExampleGraphs { name = "Ende"; } vertices[row][col] = new MarkedVertex<>(50 + col * 100, 50 + row * 100, name, null, null); - example1.addVertex(vertices[row][col]); + example2.addVertex(vertices[row][col]); } } @@ -96,36 +107,36 @@ public class ExampleGraphs { for (int row = 0; row < size; row++) { for (int col = 0; col < size; col++) { if (col < size - 1) { - addBidirectionalEdge(example1, vertices[row][col], vertices[row][col + 1], new EdgeWeightMarking(1)); + addBidirectionalEdge(example2, vertices[row][col], vertices[row][col + 1], new EdgeWeightMarking(1)); } if (row < size - 1) { - addBidirectionalEdge(example1, vertices[row][col], vertices[row + 1][col], new EdgeWeightMarking(1)); + addBidirectionalEdge(example2, vertices[row][col], vertices[row + 1][col], new EdgeWeightMarking(1)); } } } - return example1; - } - - private void addBidirectionalEdge(DirectedGraph graph, MarkedVertex from, MarkedVertex to, EdgeWeightMarking weight) { - MarkedEdge forwardEdge = new MarkedEdge<>("edge" + from.getName() + "_" + to.getName(), from, to, weight); - MarkedEdge backwardEdge = new MarkedEdge<>("edge" + to.getName() + "_" + from.getName(), to, from, weight); - graph.addEdge(forwardEdge); - graph.addEdge(backwardEdge); + return example2; } + /** + * Creates an example graph illustrating two paths from a start node to an end node with different characteristics: + * one path with few nodes and mostly low-weight edges, and another path with more nodes and higher-weight edges. + * The algorithms will alternate exploration between these paths. + * + * @return Directed graph instance for example 3. + */ public DirectedGraph example3() { - DirectedGraph example2 = new DirectedGraph<>(); - /* - * Beispiel 2 zeigt zwei Wege von einem Startknoten zu einem Zielknoten mit unterschiedlichen Eigenschaften: - * Ein Weg (oben) hat wenige Knoten mit Kanten geringer Gewichtung, außer der letzten Kante, die hohe Gewichtung hat. - * Der andere Weg (unten) hat mehr Knoten mit höherer Gewichtung der Kanten. Der Algorithmus sollte zuerst den oberen - * Weg erkunden, bis er die hohe Gewichtung der letzten Kante des unteren Wegs berücksichtigt. + * Beispiel 3 zeigt zwei Wege von einem Startknoten zu einem Zielknoten mit unterschiedlichen Eigenschaften: + * Ein Weg (oben) hat wenige Knoten mit Kanten geringer Gewichtung, außer der vorletzten Kante, die hohe Gewichtung hat. + * Der andere Weg (unten) hat mehr Knoten mit höherer Gewichtung der Kanten. Die Algorithmen wechseln also mit dem Erkunden + * zwischen den beiden Pfaden. * */ + DirectedGraph example3 = new DirectedGraph<>(); + MarkedVertex A = new MarkedVertex<>(100, 100, "Start", null, null); MarkedVertex B = new MarkedVertex<>(250, 50, "B", null, null); MarkedVertex C = new MarkedVertex<>(400, 100, "C", null, null); @@ -134,38 +145,44 @@ public class ExampleGraphs { MarkedVertex F = new MarkedVertex<>(250, 200, "F", null, null); MarkedVertex G = new MarkedVertex<>(550, 200, "G", null, null); - example2.addVertex(A); - example2.addVertex(B); - example2.addVertex(C); - example2.addVertex(D); - example2.addVertex(E); - example2.addVertex(F); - example2.addVertex(G); + example3.addVertex(A); + example3.addVertex(B); + example3.addVertex(C); + example3.addVertex(D); + example3.addVertex(E); + example3.addVertex(F); + example3.addVertex(G); - example2.addEdge(new MarkedEdge<>("AB", A, B, new EdgeWeightMarking(1))); - example2.addEdge(new MarkedEdge<>("BC", B, C, new EdgeWeightMarking(1))); - example2.addEdge(new MarkedEdge<>("CD", C, D, new EdgeWeightMarking(10))); - example2.addEdge(new MarkedEdge<>("DE", D, E, new EdgeWeightMarking(1))); + example3.addEdge(new MarkedEdge<>("AB", A, B, new EdgeWeightMarking(1))); + example3.addEdge(new MarkedEdge<>("BC", B, C, new EdgeWeightMarking(1))); + example3.addEdge(new MarkedEdge<>("CD", C, D, new EdgeWeightMarking(10))); + example3.addEdge(new MarkedEdge<>("DE", D, E, new EdgeWeightMarking(1))); - example2.addEdge(new MarkedEdge<>("AF", A, F, new EdgeWeightMarking(5))); - example2.addEdge(new MarkedEdge<>("FG", F, G, new EdgeWeightMarking(5))); - example2.addEdge(new MarkedEdge<>("GE", G, E, new EdgeWeightMarking(5))); + example3.addEdge(new MarkedEdge<>("AF", A, F, new EdgeWeightMarking(5))); + example3.addEdge(new MarkedEdge<>("FG", F, G, new EdgeWeightMarking(5))); + example3.addEdge(new MarkedEdge<>("GE", G, E, new EdgeWeightMarking(5))); - return example2; + return example3; } - - + /** + * Creates an example labyrinth graph with four horizontal paths, each consisting of four nodes. + * Each path leads to a respective endpoint, demonstrating how algorithms may explore potentially incorrect paths + * before finding the correct one, and showing the efficiency of A* in finding the correct path quickly. + * + * @return Directed graph instance for example 4. + */ public DirectedGraph example4() { /* - * Beispiel 3 zeigt ein Labyrinth mit vier horizontalen Wegen, die jeweils aus vier Knoten bestehen. - * Jeder Weg führt zum Endpunkt E1, E2, E3 bzw. E4. Ziel ist es zu zeigen, dass die Algorithmen auch - * potenziell falsche Wege erkunden können, bevor sie den richtigen Endpunkt erreichen. + * Beispiel 4 zeigt ein Labyrinth mit vier horizontalen Wegen, die jeweils aus vier Knoten bestehen. + * Jeder Weg führt zum Endpunkt E1, E2, E3 bzw. Ende. Ziel ist es zu zeigen, dass die Algorithmen auch + * potenziell falsche Wege erkunden können, bevor sie den Endknoten erreichen. Zudem wird gezeigt, + * wie A* schnell den richtigen Weg findet, während Djkstra alle Wege durchsucht. * */ - DirectedGraph example3 = new DirectedGraph<>(); + DirectedGraph example4 = new DirectedGraph<>(); // Startpunkt A in der Mitte der Y-Koordinate MarkedVertex A = new MarkedVertex<>(100, 250, "Start", null, null); @@ -194,46 +211,59 @@ public class ExampleGraphs { MarkedVertex D4 = new MarkedVertex<>(400, 400, "D4", null, null); MarkedVertex E4 = new MarkedVertex<>(500, 400, "Ende", null, null); // Endpunkt des vierten Weges - example3.addVertex(A); - example3.addVertex(B1); - example3.addVertex(C1); - example3.addVertex(D1); - example3.addVertex(B2); - example3.addVertex(C2); - example3.addVertex(D2); - example3.addVertex(B3); - example3.addVertex(C3); - example3.addVertex(D3); - example3.addVertex(B4); - example3.addVertex(C4); - example3.addVertex(D4); - example3.addVertex(E1); - example3.addVertex(E2); - example3.addVertex(E3); - example3.addVertex(E4); + example4.addVertex(A); + example4.addVertex(B1); + example4.addVertex(C1); + example4.addVertex(D1); + example4.addVertex(B2); + example4.addVertex(C2); + example4.addVertex(D2); + example4.addVertex(B3); + example4.addVertex(C3); + example4.addVertex(D3); + example4.addVertex(B4); + example4.addVertex(C4); + example4.addVertex(D4); + example4.addVertex(E1); + example4.addVertex(E2); + example4.addVertex(E3); + example4.addVertex(E4); - example3.addEdge(new MarkedEdge<>("AB1", A, B1, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("AB2", A, B2, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("AB3", A, B3, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("AB4", A, B4, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("AB1", A, B1, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("AB2", A, B2, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("AB3", A, B3, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("AB4", A, B4, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("B1C1", B1, C1, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("B2C2", B2, C2, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("B3C3", B3, C3, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("B4C4", B4, C4, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("B1C1", B1, C1, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("B2C2", B2, C2, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("B3C3", B3, C3, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("B4C4", B4, C4, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("C1D1", C1, D1, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("C2D2", C2, D2, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("C3D3", C3, D3, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("C4D4", C4, D4, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("C1D1", C1, D1, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("C2D2", C2, D2, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("C3D3", C3, D3, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("C4D4", C4, D4, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("D1E1", D1, E1, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("D2E2", D2, E2, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("D3E3", D3, E3, new EdgeWeightMarking(1))); - example3.addEdge(new MarkedEdge<>("D4E4", D4, E4, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("D1E1", D1, E1, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("D2E2", D2, E2, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("D3E3", D3, E3, new EdgeWeightMarking(1))); + example4.addEdge(new MarkedEdge<>("D4E4", D4, E4, new EdgeWeightMarking(1))); - return example3; + return example4; + } + + /** + * Helper method to add a bidirectional edge between two vertices in a graph. + * + * @param graph The graph instance where the edge should be added. + * @param from The starting vertex of the edge. + * @param to The ending vertex of the edge. + * @param weight The weight marking of the edge. + */ + private void addBidirectionalEdge(DirectedGraph graph, MarkedVertex from, MarkedVertex to, EdgeWeightMarking weight) { + MarkedEdge forwardEdge = new MarkedEdge<>("edge" + from.getName() + "_" + to.getName(), from, to, weight); + MarkedEdge backwardEdge = new MarkedEdge<>("edge" + to.getName() + "_" + from.getName(), to, from, weight); + graph.addEdge(forwardEdge); + graph.addEdge(backwardEdge); } } - - diff --git a/out/production/ProjektGraph/OurApplication/OurApplication.class b/out/production/ProjektGraph/OurApplication/OurApplication.class index e4a10fa2fd9b652d99272f3cf8b0db56b76bc1ec..289c516d8e59cfc2d6ccf2107deb1de5aa8d0771 100644 GIT binary patch delta 1023 zcmXYwTW=d>5Qd+%*Is+Q&?bR|H3@MYC*{wr^R;XT0ov~}htw!<6vghe&_`^gnG#)W{ltTyKr9&D$2EFth{FR72 zPQS*0!4sH{rdXp-D)g6kN+r8eO0Djc7R$wJ#Y$tiiRo#zTxL^maPP+4bE*+1@WS?463v2;&9;CY*nj8Fk9z-w#D~ok^xW z{HSxm7gL^buKQv}iWv_|D=dyXzX$uAd%m=KOkvQO2oFm2S+C^06Y-CPGFTqE)rBwtA(v?oeVDE%DNFDsl!U+bH)t;VWYG4mp9!C`Iv&E{S(2i5KQ7b~l0Yvv?7~ z?z(9CHsRF==u<5Yw|LAYD#+lP%TT`EWO&^rCP3U$T}JZB-qAKsTLG8Je6mf_3c957 z)27?Y)fEk=NwWJyHqQ|YUOE%y8e4CS&ZtB4nr%b)Yk{NDscEh_~ z@UE9UlQZ?6z*3Jt)@+eCb+yGC-e3Ch{&Jl@u}2A!l8sKwip~<}7*ov2bTaZb=2_!7 zIV`q0$(|q|af&ZF&DXL92P|@%C4OR=JFIk+s&>`jUHJmZ{3B(qiB`=h-%?>mgpG5H zD%YinDQ-}sE;47tZ?G$qSrCc$cuyv@CcE%HAIK}r$tLadp`_d9gtTx&MBihEk7>%N fK9NH5gdP_AREl+LeI~WK{LcyN0)HVyA25XOIpz0Bgay{!CBb-E zjqw^$sqr~^uu846EK9B0wH%E;T0U6%T$MiHOCv+W3bm|AB|f-xoW>3Qw`B3ePpxW zu9{;9wR?}E(f`G4l%0ok-0uIOYW>Zq88YegZ>l=8jV_xA3IAKwqwW*9G2TtDPW34o z{O^@xw#^ybuY;eX4VFbe4{CV8zfdu$2K`Se_Qza(_F=_7|8HZTP=-0GDI@-$73G14 z6g7Gb8;SfCZ*?g#IhISUq)m<~Vsl=8J(cnDX^TqN*bCMa~ z33tXH;k3aSn@5@TPlYFoqnx#QjHn`P7?Fl1{j$qs+ao5zs9`nI6{+=ubKkCtBtK zHx}Rud&so{hLeurn1aNduw!Zbh#LHSaqnW#8ZwPF0r65A3|S#j#{x@m~>M`k9>uG4lqCmE`1D&+C%bihMDCs^Bj=@ z9pxHhY%-3|ac(lf2g3N66MV`PUop+?A~V~QyuLFnvOFV2Ov>cQ%Pgz-i~{R2_j*3! zGFPOT4&G;jXGMiRZg7?7M7Uuo^*k?#fRn<0k)nL{S!w$vUY675IWPDtqRFy6-|M_8 vN?wyauQB{Oap|y!H+WOzd`pVUH~zQm4Jo@*>}@Hwvtd*Cig$!p!MpzesyE-| diff --git a/out/production/ProjektGraph/graph/ExampleGraphs.class b/out/production/ProjektGraph/graph/ExampleGraphs.class index ed65523276ef30de0162973a84782a5e6dc9ea07..e9feef10ed5ef0a03e7b71787661a0c3bb2eac20 100644 GIT binary patch literal 6300 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 B?xO$z literal 6300 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