Links und Funktionen
Sprachumschaltung

Navigationspfad


Inhaltsbereich

graph_matrix_dijkstra.py

Python Source icon graph_matrix_dijkstra.py — Python Source, 13 KB (14333 bytes)

Dateiinhalt

# Graph mit Adjazenzmatrix und Dijkstra-Implementierung
# Daten: Einzugsgebiet der Kursschüler (Ignaz-Kögler-Gymnasiums LL)
# Prioriätswarteschlange aus heap.py in Verwendung

from turtle import *                    # Zeichenmodul laden
from heap import Heap                   # Prioritätswarteschlange

class GKnoten(object):
    "Modelliert Graph-Knoten"
    def __init__(self,x, y, name, größe, marker):
        "Konstruktor"
        self.x = x                      # x-Koordinate
        self.y = y                      # y-Koordinate
        self.name = name                # Knotenname
        self.größe = größe              # Knotengröße
        self.marker = marker            # Markierungsattribut

    def __str__(self):
        return self.name                # Ausgabe Konsole

    def __repr__(self):
        return self.name                # Ausgabe via 'print()'

class GMatrix(object):
    "Adjazenzmatrix"
    def __init__(self, x,y,name, größe=1, marker=None):
        "Konstruktor"
        self.gliste=[GKnoten(x,y,name,größe,marker)] # Liste mit Start-GKnoten
        self.gmatrix = [[0]]                    # vorbefüllt
        self.tsucheliste = []                   # Hilfsliste für Baumdarstellung bei tsuche

    def textausgabe(self):
        "gibt Matrix in Textform als Tabelle aus"
        print("\nvon\\n\t", end="")             # Starte Kopfzeile mit Bezeichnern
        for i in self.gliste:                   # durchlaufe die Liste aller GKnoten
            print(i.name[:3], end="\t")         # nur die ersten drei Zeichen
        print()                                 # neue Zeile
        
        for i in range(len(self.gmatrix)):      # durchlaufe zeilenweise alle Indexe
            print(self.gliste[i].name[:3],
                  end="\t")                     # Bezeichner Start-GKnoten    
            for j in self.gmatrix[i]: 
                print(j, end="\t")              # printe die Entfernungen pro Zeile
            print()                             # neue Zeile

    def addGKnoten(self, neuKnot):
        "fügt neuen Knoten in gliste ein und erweitert die Matrix"
        if neuKnot not in self.gliste:          # Knoten neuKnot echt neu?...
            self.gliste.append(neuKnot)         # ...dann aufnehmen
            for i in self.gmatrix:              # durchlaufe alle Zeilen...
                i.append(0)                     # ...und füge eine 0 an
            self.gmatrix.append([0 for j in self.gliste])# neue 0er-Zeile
        else:
            print("Graphknoten",neuKnot,"bereits vorhanden")

    def addKante(self, startKnoten, zielKnoten, gewicht,rückweg=True):
        "fügt Kante zw. startKnoten und zielKnoten mit 'gewicht' ein"
        for i in range(len(self.gliste)):
            if self.gliste[i].name == startKnoten:
                startIndex = i
            if self.gliste[i].name == zielKnoten:
                zielIndex = i
        self.gmatrix[startIndex][zielIndex]=gewicht
        if rückweg:
	        self.gmatrix[zielIndex][startIndex]=gewicht

    def zeichneKante(self, v1,v2, farbe, gewicht, stiftbreite=1, geschw=10, verzögerung=0):
        "zeichnet Kante zw. 'v1' und 'v2' mit 'gewicht' und 'farbe'"
        speed(geschw)                   # Geschwindigkeit
        delay(verzögerung)              # Verzögerung
        up()                            # Stift hoch
        setpos(v1.x,v1.y)               # Position v1
        color(farbe)                    # Farbe setzen
        pensize(stiftbreite)            # Stiftbreite setzen
        pd()                            # Stift runter
        st()                            # Turtle zeigen
        setpos(v2.x,v2.y)               # Zeichnet Verbindung zu 
        ht()                            # Turtle verstecken
        up()                            # Stift hoch
        setpos(v1.x + (v2.x-v1.x)/2,
               v1.y + (v2.y-v1.y)/2)    # Streckenmitte anvisieren
        write(gewicht)                  # Kantengewicht notieren

    def zeichneGKnoten(self,GKnoten, schriftfarbe, gknotenfarbe, geschw=10, verzögerung=0):
        "zeichnet GKnoten in 'gknotenfarbe' und beschriftet ihn mit 'schriftfarbe'"
        speed(geschw)                   # Geschwindigkeit
        delay(verzögerung)              # Verzögerung
        ht()                            # verstecke Turtle
        setpos(GKnoten.x,GKnoten.y)	# steuere GKnoten-Koordinaten an
        pd()     			# Turtle runter
        dot(GKnoten.größe, gknotenfarbe)# zeichnet Punkt entspr. d. Größe
        color(schriftfarbe)             # wechselt Farbe 
        write(GKnoten.name) 		# Ortsnamen schreiben
        up() 			        # Turtle wieder hoch

    def zeichneGraph(self):
        "erstelle Turtle-Abbild des Graphen"
        # 0. Voreinstellungen
        bgcolor(.8,1,.9)                            # Hintergrundfarbe (RGB-Modus)
        reset();up();pensize(1);speed(0);delay(0)   # Zeichenfenster auf Start
        setworldcoordinates(-120,-190,450,300)      # Koordinaten anpassen
        title("Turtle4Dijkstra")
        # 1. Kanten zeichnen
        for v1 in self.gliste:                              # durchlaufe Knotenliste vertikal
            for v2 in self.gliste:                          # durchlaufe Knotenliste horizontal
                gewicht = self.gmatrix[self.gliste.index(v1)][self.gliste.index(v2)]
                if gewicht:                                 # Falls Kantengewicht vorhanden...
                    self.zeichneKante(v1,v2,"grey",gewicht)
        # 2. GKnoten zeichnen
        for i in self.gliste: 		                    # besuche jeden GKnoten...
            self.zeichneGKnoten(i,"black","red")


    def starteTsuche(self,vstart):                          # bereitet Graph auf Tiefensuche vor:
        "Startmethode für Tiefensuche"
        print("\nTiefensuche startet am GKnoten:", vstart)  # Kommentar
        namensliste=[]                                      # Liste der Gknotennamen
        for i in self.gliste:                               # iteriere über gliste...
            i.marker = False                                # demarkiere alle Knoten
            namensliste.append(i.name)                      # und füge den Namen in Liste ein
        self.tsuche(self.gliste[namensliste.index(vstart)]) # starte Tiefensuche
            

    def tsuche(self,vstart,farbe="green"):
        "eigentliche Tiefensuche"
        vstart.marker=True                                  # GKnoten markieren
        self.zeichneGKnoten(vstart,"black","blue",1,10)     # färbe GKnoten blau
        for i in self.gliste:
            if self.gmatrix[self.gliste.index(vstart)][self.gliste.index(i)] and not i.marker:
                print(repr(vstart).ljust(10),"--->",i)      # Textausgabe
                self.zeichneKante(vstart,i,
                                  vstart in self.tsucheliste and "orange" or "green"
                                  ,"",2,1,10)               # Kanten zeichnen: grün bzw. orange
                self.tsucheliste.append(vstart)             # fügt akt. GKnoten in Hilfsliste ein
                self.tsuche(i)                              # rekursiver Aufruf


    def dijkstra(self, startknoten, zielknoten=False):
        "ermittelt den kürzesten Weg vom Startknoten zu allen/einem Zielknoten"
        
        # Vorbereitung: alle Knoten demarkieren und Startknoten finden...
        for i in self.gliste:           # iteriere über gliste...
            i.marker = False            # demarkiere aktuellen Knoten
            if i.name == startknoten:   # suche nach Knoten aus Namen...
                startknoten = i         # Namenszug ersetzt durch echten Knoten
            if i.name == zielknoten:    # suche nach Knoten aus Namen...
                zielknoten = i          # Namenszug ersetzt durch echten Knoten

        # 0. Markiere Startstadt, gib Kennzahl 0, =aktueller Knoten
        startknoten.marker = True       # Markierung setzen
        startknoten.kennzahl = 0        # neues Attribut 'en passant' vergeben
        aktknoten = startknoten

        h = Heap()                      # Prioritätswarteschlange erzeugen
        while True:
            # 1. Gehe vom aktuellen Knoten zu allen Nachbarknoten
            for i in self.gliste:
               # Berechne die Länge vom aktuellenKnoten zum Nachbarknoten
                längeStrecke = self.gmatrix[self.gliste.index(aktknoten)][self.gliste.index(i)]
                if längeStrecke and not i.marker:
                    # hat der Nachbarknoten keine Kennzahl...
                    if not hasattr(i,"kennzahl"):
                        # ...weise ihr die Summe als Kennzahl zu und packe den Knoten in die PQ
                        i.kennzahl = aktknoten.kennzahl + längeStrecke
                        h.einfügen(i, i.kennzahl)
                        i.kennknoten = aktknoten # Quasi-Markierung der Strecke
                    # hat der Nachbarknoten eine Kennzahl größer als berechnete Entfernung..
                    elif i.kennzahl > aktknoten.kennzahl + längeStrecke:
                        # ...dann weise ihm die kleinere Kennzahl zu und aktualisiere die PQ
                        h.neuePrio(i,i.kennzahl,aktknoten.kennzahl + längeStrecke)
                        i.kennzahl = aktknoten.kennzahl + längeStrecke # neue Kennzahl eintragen
                        i.kennknoten = aktknoten # Quasi-Markierung der Strecke

            # 2.+3. hole den marktieren Knoten mit der kleinsten Kennzahl (aus der PQ)
            try:
                aktknoten = h.entfernen()[0]
            except IndexError:
                print("PQ leer")
                break
            aktknoten.marker = True
            ## Ausgabe aller kürzesten Wegstrecken, wenn kein Ziel vorgegeben ----------
            if not zielknoten:
                self.zeichneKante(aktknoten.kennknoten, aktknoten,"magenta",
                             self.gmatrix[self.gliste.index(aktknoten)]
                             [self.gliste.index(aktknoten.kennknoten)],3,2,10)

        # Ausgabe der kürzesten Wegstrecke vom Start- zum Zielknoten --------------
        if zielknoten:
            strecke = 0
            rückliste=[(zielknoten,"")]
            while hasattr(zielknoten,"kennknoten"):
                rückliste.insert(0,(zielknoten.kennknoten,
                                    self.gmatrix[self.gliste.index(zielknoten)]
                                    [self.gliste.index(zielknoten.kennknoten)]))
                strecke += self.gmatrix[self.gliste.index(zielknoten)]\
                           [self.gliste.index(zielknoten.kennknoten)]
            # Pfad zeichnen ------------------------------------------------------------
                self.zeichneKante(zielknoten,zielknoten.kennknoten,"blue",
                                  self.gmatrix[self.gliste.index(zielknoten)]
                                  [self.gliste.index(zielknoten.kennknoten)],3,1,20)
                zielknoten=zielknoten.kennknoten            

            # Pfad in Textform  --------------------------------------------------------    
            color("black")                                  # ab hier: Wegbeschreibung
            textausgabe="""Kürzester Pfad\n\n"""            # Kopfzeile
            for i in rückliste:                             # Durchlaufe die Wegeliste
                textausgabe+="%s\n"%i[0].name               # Ortsnamen
                textausgabe+=i[1] and "%4s km\n"%i[1] or "\n"# km-Angabe
            textausgabe+="Gesamt: %s km"%round(strecke,1)   # Gesamtstrecke
            setpos(300,150) 
            write(textausgabe,font=('Courier',10,'bold'))   # Ausgabe des Pfades
            
            

if __name__ =="__main__":
    gm = GMatrix(0,0,"Landsberg",37)
    gm.addGKnoten(GKnoten(15,-98,"Pitzling",8,None))
    gm.addGKnoten(GKnoten(20,280,"Burching",15,None))
    gm.addGKnoten(GKnoten(-22,100,"Kaufering",20,None))
    gm.addGKnoten(GKnoten(83,-63,"Pürgen",10,None))
    gm.addGKnoten(GKnoten(95,55,"Penzing",13,None))
    gm.addGKnoten(GKnoten(97,-4,"Schwifting",8,None))
    gm.addGKnoten(GKnoten(90,-120,"Hagenheim",2,None))
    gm.addGKnoten(GKnoten(72,-18,"Reisch",5,None))
    gm.addGKnoten(GKnoten(180,-80,"Finning",8,None))
    gm.addGKnoten(GKnoten(370,40,"Greifenberg",8,None))
    gm.addGKnoten(GKnoten(260,90,"Eresing",4,None))
    gm.addGKnoten(GKnoten(10,-180,"Mundraching",4,None))
    gm.addGKnoten(GKnoten(157,88,"Ramsach",4,None))
    
    gm.addKante("Landsberg","Kaufering",6) 
    gm.addKante("Landsberg","Penzing",6.6)
    gm.addKante("Landsberg","Eresing",14.8)
    gm.addKante("Landsberg","Burching",19.4) 
    gm.addKante("Landsberg","Greifenberg",22.1)
    gm.addKante("Landsberg","Schwifting",6.4)
    gm.addKante("Pürgen","Finning",6.9)
    gm.addKante("Pürgen","Reisch",2.5)
    gm.addKante("Schwifting","Reisch",1.5)
    gm.addKante("Landsberg","Pitzling",6.9)
    gm.addKante("Landsberg","Pürgen",5.8)
    gm.addKante("Penzing","Ramsach",4)
    gm.addKante("Finning","Schwifting",9.3)
    gm.addKante("Hagenheim","Pürgen",4.6)
    gm.addKante("Mundraching","Landsberg",14)
    gm.addKante("Landsberg","Reisch",4.8)
    gm.addKante("Pürgen","Pitzling",3.6)
    gm.addKante("Burching","Kaufering",13.8)
    gm.addKante("Greifenberg","Eresing",6.5)
    gm.addKante("Penzing","Schwifting",2.6)
    gm.addKante("Mundraching","Pitzling",10.4)
    gm.addKante("Greifenberg","Finning",11)
    gm.addKante("Ramsach","Eresing",6.9)
    gm.addKante("Ramsach","Kaufering",11.7)
    gm.addKante("Schwifting","Pürgen",3)
    gm.addKante("Schwifting","Greifenberg",13.4)
    gm.addKante("Kaufering","Penzing",7.3)
    gm.addKante("Finning","Eresing",10.7)
    gm.addKante("Burching","Eresing",18.1)
    gm.addKante("Finning","Hagenheim",5)
    gm.addKante("Mundraching","Hagenheim",9.6)

    #gm.textausgabe()
    gm.zeichneGraph()
    gm.starteTsuche("Burching")
    #gm.dijkstra("Kaufering","Greifenberg")
    #gm.dijkstra("Kaufering")
    exitonclick()
    
    
    

Funktionsleiste