Das „Haus vom Nikolaus„, ein einfaches geometrisches Muster aus fünf Linien, das die Form eines stilisierten Hauses darstellt, mag auf den ersten Blick wie eine unschuldige Kindermalerei wirken. Doch hinter diesem scheinbar simplen Muster verbirgt sich eine faszinierende Verbindung zur Graphentheorie, einem Teilgebiet der Mathematik, das sich mit den Eigenschaften und Beziehungen von Graphen beschäftigt. Dann mal los, und ein kleines Java Programm dazu.
Was ist das „Haus vom Nikolaus“?
Das „Haus vom Nikolaus“ ist eine Zeichnung, die aus fünf Strichen besteht und die Form eines stilisierten Hauses zeigt. Es besteht aus einem Quadrat als Basis, einem Dreieck als Dach und einem rechteckigen Schornstein oben auf dem Dach. Es ist benannt nach dem „Nikolaus, dem Schutzpatron der Kinder“, da es oft von Kindern gezeichnet wird.
Graphentheorie und das „Haus vom Nikolaus“
Die Verbindung zwischen dem „Haus vom Nikolaus“ und der Graphentheorie liegt in der Möglichkeit, die Zeichnung als Graphen darzustellen. Ein Graph in der Mathematik besteht aus Knoten (auch als Ecken oder Punkte bezeichnet) und Kanten (auch als Linien oder Verbindungen bezeichnet), die die Beziehungen zwischen den Knoten repräsentieren. In diesem Zusammenhang können die Ecken des „Hauses vom Nikolaus“ als Knoten betrachtet werden, während die Linien zwischen den Ecken die Kanten darstellen. Wie oben oder hier als gerichteter Graph:
Die Bedeutung der „Haus vom Nikolaus“-Graphen
Der „Haus vom Nikolaus“-Graph ist ein Beispiel für einen nicht-planaren Graphen. Ein planarer Graph ist ein Graph, der so auf einer Ebene gezeichnet werden kann, dass sich keine seiner Kanten überschneiden. Der „Haus vom Nikolaus“-Graph kann jedoch nicht ohne Kantenüberschneidungen auf eine Ebene gezeichnet werden, was ihn zu einem klassischen Beispiel für einen nicht-planaren Graphen macht.
Ein Graph G ist ein Tupel (V,E), wobei V eine Menge von Knoten (englisch vertex, oft auch Ecken genannt) und E eine Menge von Kanten (engl. edge, manchmal auch Bögen genannt) bezeichnet.
In der Graphentheorie gibt es den berühmten Satz von dem polnischen Mathematiker Kuratowski, der besagt, dass ein Graph genau dann nicht-planar ist, wenn er einen Teilgraphen enthält, der homomorph zu einem „Haus vom Nikolaus“-Graphen ist. Das bedeutet, dass das „Haus vom Nikolaus“ eine wichtige Rolle in der Untersuchung von nicht-planaren Graphen spielt und als Beispiel dient, um verschiedene Konzepte und Eigenschaften von Graphen zu veranschaulichen.
Das „Haus vom Nikolaus“ mag auf den ersten Blick wie eine einfache Kindermalerei aussehen, aber es hat eine tiefere Bedeutung in der Welt der Graphentheorie. Es veranschaulicht die Idee von nicht-planaren Graphen und bietet ein anschauliches Beispiel für den berühmten Satz von Kuratowski. Dieses scheinbar unschuldige Muster ist ein hervorragendes Beispiel dafür, wie abstrakte mathematische Konzepte in visuellen Darstellungen veranschaulicht werden können, um komplexe Ideen zu vermitteln.
Dann mal hier ein Beispiel in Java und mit jGrapht:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
package de.wenzlaff.twgraph; import java.util.ArrayList; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jgrapht.ListenableGraph; import org.jgrapht.alg.cycle.HierholzerEulerianCycle; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DefaultListenableGraph; import org.jgrapht.graph.SimpleDirectedGraph; import org.jgrapht.traverse.DepthFirstIterator; import org.jgrapht.traverse.GraphIterator; /** * Ziel ist es, ein „Haus“ in einem Linienzug aus genau acht Strecken zu * zeichnen, ohne eine Strecke zweimal zu durchlaufen. Begleitet wird das * Zeichnen mit dem simultan gesprochenen Reim aus acht Silben: „Das ist das * Haus vom Ni-ko-laus.“ * * https://de.wikipedia.org/wiki/Haus_vom_Nikolaus * * @author Thomas Wenzlaff */ public class HausVomNikolaus { private static final Logger LOG = LogManager.getLogger(HausVomNikolaus.class); private ListenableGraph<Object, DefaultEdge> graph; public HausVomNikolaus() { erstelleHausVomNikolausGraph(); } private void erstelleHausVomNikolausGraph() { // Erstelle einen gerichteten Graphen mit SimpleDirectedGraph edges graph = new DefaultListenableGraph<>(new SimpleDirectedGraph<>(DefaultEdge.class)); // Füge 5 Knoten zum Graphen hinzu graph.addVertex("1"); graph.addVertex("2"); graph.addVertex("3"); graph.addVertex("4"); graph.addVertex("5"); // Füge 8 Kanten = Verbindungsstrecken zwischen den Knoten hinzu graph.addEdge("1", "2"); graph.addEdge("2", "4"); graph.addEdge("4", "3"); graph.addEdge("3", "5"); graph.addEdge("5", "4"); graph.addEdge("4", "1"); graph.addEdge("1", "3"); graph.addEdge("3", "2"); alleKnotenAusgeben(); } public ListenableGraph<Object, DefaultEdge> getGraph() { return graph; } /** * Problemgegenstand ist ein Graph, für den ein Eulerweg, aber kein Eulerkreis * existiert, da er zwei Knoten von ungeradem Grad (die Knoten 1 und 2 haben * hier jeweils einen Grad von 3) enthält. * * @return true wenn es ein eulerscher Graph währe, sonst immer false */ public boolean isHausVomNikolausEulerian() { HierholzerEulerianCycle<Object, DefaultEdge> hier = new HierholzerEulerianCycle<>(); boolean isEulerian = hier.isEulerian(graph); // eigentlich nur für ungerichtete Graphen, geht in diesem Fall aber so auch if (isEulerian) { LOG.info("Das HausVomNikolaus ist ein eulerscher Graph. Das trifft nie zu."); } else { LOG.info("Das HausVomNikolaus für den es ein Eulerweg, aber kein Eulerkreis existiert wenn von 1 oder 2 gestartet wird."); } return isEulerian; } public List<Object> alleKnotenAusgeben() { String startVertex = "1"; // start Knoten GraphIterator<Object, DefaultEdge> it = new DepthFirstIterator<>(graph, startVertex); List<Object> ergebnisKnoten = new ArrayList<>(); while (it.hasNext()) { ergebnisKnoten.add(it.next()); } LOG.info(ergebnisKnoten); return ergebnisKnoten; } } |
Und eine Testklasse dazu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package de.wenzlaff.twgraph; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jgrapht.ListenableGraph; import org.jgrapht.alg.cycle.HierholzerEulerianCycle; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DefaultListenableGraph; import org.jgrapht.graph.SimpleGraph; import org.jgrapht.nio.dot.DOTExporter; import org.junit.jupiter.api.Test; /** * * @author Thomas Wenzlaff * */ class HausVomNikolausTest { private static final Logger LOG = LogManager.getLogger(HausVomNikolausTest.class); /** SUT. */ private HausVomNikolaus graph = new HausVomNikolaus(); @Test void testHausVomNikoloaus() { assertNotNull(graph); } @Test void testIsHausVomNikolausEulerian() { DotExporter dotExp = new DotExporter(); assertFalse(graph.isHausVomNikolausEulerian()); File dotFile = new File("target", "junit-test-hausvomnikolaus.dot"); DOTExporter<Object, DefaultEdge> writeDotFile = dotExp.writeDotFile(graph.getGraph(), dotFile); assertNotNull(writeDotFile); assertTrue(dotFile.exists()); } @Test void testIsEulerian() { // https://de.wikipedia.org/wiki/Algorithmus_von_Hierholzer // der in einem ungerichteten Graphen Eulerkreise bestimmt HierholzerEulerianCycle<Object, DefaultEdge> hier = new HierholzerEulerianCycle<>(); boolean isEulerian = hier.isEulerian(getTestEulerGraph()); assertTrue(isEulerian); } private ListenableGraph<Object, DefaultEdge> getTestEulerGraph() { ListenableGraph<Object, DefaultEdge> eulersGraphQuadrat = new DefaultListenableGraph<>(new SimpleGraph<>(DefaultEdge.class)); eulersGraphQuadrat.addVertex("1"); eulersGraphQuadrat.addVertex("2"); eulersGraphQuadrat.addVertex("3"); eulersGraphQuadrat.addVertex("4"); eulersGraphQuadrat.addEdge("1", "2"); eulersGraphQuadrat.addEdge("2", "3"); eulersGraphQuadrat.addEdge("3", "4"); eulersGraphQuadrat.addEdge("4", "1"); return eulersGraphQuadrat; } } |
Wer noch ein Template für Graphviz braucht:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
strict digraph G { 1 [ label="1" style="filled" fillcolor="#EEEEEE" color="#31CEF0" ]; 2 [ label="2" style="filled" fillcolor="#EEEEEE" color="#31CEF0" ]; 3 [ label="3" style="filled" fillcolor="#EEEEEE" color="#31CEF0" ]; 4 [ label="4" style="filled" fillcolor="#EEEEEE" color="#31CEF0" ]; 5 [ label="5" style="filled" fillcolor="#EEEEEE" color="#31CEF0" ]; 1 -> 2; 2 -> 4; 4 -> 3; 3 -> 5; 5 -> 4; 4 -> 1; 1 -> 3; 3 -> 2; } |
Nun fehlt noch ein kleines Programm, um alle 88 Möglichkeiten zu erzeugen. Der ganze Quellcode ist auf GitLab. Aber nicht mehr heute …