Eulertour (wenn es eine gibt) in gegebenem Graphen finden

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
nadrosch
User
Beiträge: 2
Registriert: Donnerstag 14. Dezember 2017, 01:47

Hallo zusammen,

ich muss für die Uni in einem gegebenen Graphen (Adjazenzliste) eine Eulertour finden.

Gibt es keine soll False zurückgegeben werden

Ansonsten die Tour in der Form [Startknoten, nächsterKnoten. nächsterKnoten, nächsterKnoten, ..., Startknotem]

Mein Programm funktioniert soweit (für die im folgenden Code angegebenen Beispiele (Funkition: test()))

Das Abnahmeprogramm gibt jedoch noch ca 1 min zurück:

Dein Programm gibt falsche Ergebnisse zurück
oder hat zu lange gebraucht.

Kann mir jemand weiterhelfen ob es einen Fehler gibt (und welche Eingabe diesen provoziert) oder ein Tipp geben wie ich die Laufzeit verbessern kann!?
Vielen Dank!!

Code: Alles auswählen

#!/usr/bin/env python


def eulertour(adj):	                    	#übergabe adj ist adjazenzliste (liste von listen)

#prüfen ob es Knoten mit ungeradem Knotenknotengrad gibt?
#Suche des Starknotens 
	

	startknoten = -1	
	j=-1
	aloi = len(adj)
	for knoten in adj:			               	#gehe die Verknüpfungs listen der einzelnen Knoten durch (listen in adj)
		j += 1        					#Hilfsvariable für startknoten
		if len(knoten)%2 != 0:	          		#wenn knotengrad ungerade
			#print("knotengrad")			
			return False               		 
		if len(knoten) == 0:
			aloi -= 1 
		else:
			startknoten= j

	if startknoten == -1:				   	#Sollte Starknoten immer noch = -1 sein ,dann besteht adj nur aus leeren listen bzw. aus isolierten knoten
		listeO = []
		return listeO                             	#in diesem Fall wird ein einzelner Knoten (der erste) aus der (index) adj liste ausgegeben oder eine Leere liste


#Eulertoursuche

	#print(a)	
	Kreisliste = [[startknoten]]                          #Kreisliste mit dem startknoten 
	Kreisanzahl = 1
	nextknoten = startknoten				#nextknoten als startknoten initialisieren  
	NeuerKreis = 0                          			#Hilfsvariable um zu ueberpruefen ob ein Kreis geschlossen wurde
	Knotengrade = 0                            
	GeloeschteGrade = 0                   		#variable um die geloeschten verknüpfungen zu zaehlen
	h =0								#Hilfsvariable um spaeter festzustellen ob der Graph ohne die iso Knoten zushaengend ist

	for elemente in adj:                 			#summiere die knotengrade
		Knotengrade += len(elemente)

	while GeloeschteGrade != Knotengrade:	#solange nicht alle verknüpfungen gelöscht sind
		#print(nextknoten)
		if len(adj[nextknoten]) == 1 and adj[nextknoten][0] == startknoten:	#wenn nextknoten nur noch eine verknuepfung hat und diese der aktuelle startknoten ist
			#print("neuer Kreis", Kreisliste)	
			
			Kreisliste[Kreisanzahl-1].append(startknoten)		#dann wird der Kreis geschlossen
			d =adj[startknoten].index(el)					# d wird zum index des aktuellen nextknotens in startknotenliste
			del adj[startknoten][d]						# verknuepfung von nextknoten in startknotenliste  wird geloescht
			del adj[nextknoten][0]						# verknuepfung des startknotens in der nextknotenliste wird geleoscht
			GeloeschteGrade +=2

			for elem in adj:
				
				if len(elem) > 0:					#wenn ein Knoten noch mehr als 1 verknuepfung hat (die liste alsoi mehr als einen eintrag
					h +=1 
					#print(h)
					for elle in elem:				#gehe die verknuepfungen durch

						if elle in Kreisliste[Kreisanzahl-1]:	#wenn verknuepfungsknoten (elle) bereits besucht wurde			
							#print("asdfasdf")
							startknoten = elle		#neuer Startknoten wird initialisiert     
							nextknoten = startknoten	#naechster Knoten ist der Startknoten
							NeuerKreis = 1			#Hilfsvariable zum überpruefen ob zushaengend
							Kreisanzahl += 1
							Kreisliste.append([nextknoten])	#Fügt Kreisliste neuen Kreis hinzu mit startknoten a

							break
				if NeuerKreis == 1:					
					break	

			if NeuerKreis != 1 and h > 0:					#überpruefung zusammenhaengend???
				#print("zushang")				
				return False	 	
			else:
				Neuerkreis = 0							
				h =0	
			
		

			
		for el in adj[nextknoten]:					#gehe die (el) die elemente (verknüpfungen) des nextKnoten durch
			
			if el != startknoten:					#wenn el nicht der Startknoten 
				
				b = adj[nextknoten].index(el)			# b wird zum index des elementes in nextknoten
				c = adj[el].index(nextknoten)			# c wird zum index von nextknoten in el
				
				del adj[nextknoten][b]				#lösche die verknüpfung von nextknoten zu el in nextknotenliste
				del adj[el][c]					#loesche die verknuepfung von el zu nextknoten in elliste
				GeloeschteGrade +=2

				nextknoten = el					#naechster knoten wird el
				Kreisliste[Kreisanzahl-1].append(nextknoten)	#naechster knoten (el) wird der Kreisliste hinzugefuegt
				
				break

	while len(Kreisliste)>1:						#zusammenfügen der Kreislisten zu einer Eulertour
		#print(Kreisliste)
		
		a = Kreisliste[1][0]
		#print("a",a)
		
		b = Kreisliste[0].index(a)
		#print("b",b)
		
		Kreisliste[0] =Kreisliste[0][0:b]+Kreisliste[1]+Kreisliste[0][b+1:]
		#print(Kreisliste)
		del Kreisliste[1]
	Eulertour = Kreisliste[0]

	return Eulertour                 #Eulertour fertig (alle kanten benutzt)

def test():
	
	a=[[1,2,3],[0,2,3],[0,1,3,4],[0,1,2,4],[2,3]]
	b=[[1,1],[0,0]]
	c=[[1,2,3,4],[0,2,3,4],[0,1,3,4],[0,1,2,4],[0,1,2,3]]
	d=[[1,3],[0,3],[],[0,1]]
	e=[[1,1],[0,0],[3,3],[2,2]]
	f=[[],[2,4],[1,3,6,12],[2,4],[1,3,5,7],[4,8],[2,8],[4,8,10,9],[7,9,10,11,6,5],[7,8,10,11],[7,8,9,11],[8,9,10,12],[2,11]]
	g=[[1,3],[0,3],[4],[0,1],[2],[],[]]
	h=[[1,3],[0,3],[4],[0,1],[2]]
	i=[[],[],[]]
	j=[[7,9],[2,4],[1,3],[2,4,5,6],[1,3],[3,10,7,11],[7,3],[0,8,5,6],[7,9],[8,0],[12,5],[5,12],[10,11]]
	k=[[7,9,13,4],[2,4],[1,3],[2,4,5,6],[1,3,13,0],[3,10,7,11],[7,3],[0,8,5,6],[7,9],[8,0],[12,5],[5,12],[10,11],[4,0]]
	List=[a,b,c,d,e,f,g,h,i,j,k]
	for list in List:
		print(list)
		print(eulertour(list), "\n")
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@nadrosch: der Code ist wegen der vielen Kommentare, die zumeist überflüssig sind, weil sie nur das offensichtliche beschreiben und dann auch noch rechts an die Seite gequetscht sind, schwer zu lesen. Auch die vielen Leerzeilen an Stellen, wo keine logische Trennung des Codes existiert, sind irritierend. Die Funktion ist mit 7 Einrücktiefen und mehr als 100 Zeilen viel zu komplex. Unterteile die Funktion in mehrere Funktionen, die jede für sich eine bestimmte Aufgabe erledigen. Nach welchem Algorithmus gehst Du vor? Beschreibe diesen in einem Kommentar. Das sind vielleicht 5 Schritte für die Du dann 5 Funktionen schreiben kannst. Ein Schritt kann dann vielleicht nochmal aus mehreren Unterschritten bestehen. Ist der Algorithmus am Anfang einer (dann kurzen Funktion) beschrieben, brauchst Du auch die vielen Kommentare im Code nicht mehr.

Dinge, die Du Dir am besten gleich angewöhnst, damit Du sie später nicht wieder mühselig verlernen mußt: es gibt eine Namenskonvention die in PEP8 beschrieben ist: alle Variablennamen werden komplett klein geschrieben. Große Anfangsbuchstaben sind für Klassennamen reserviert. Benutze aussagekräftige Namen, einbuchstabige sind das nicht. Z.B. in Zeile 101 ist `a` ein Knoten, zwei Zeilen tiefer `b` ein Index. Aus den Namen wird das nicht klar.

Das Arbeiten mit `del` und Listenzusammenstückeln ist mir zu kompliziert zu verstehen. Schreibe Funktionen für die Listenverarbeitung, die kurz sind und eine klare Aufgabe haben, die Du dann auch einzeln testen kannst.

Trotz dass es in der Aufgabe gefordert ist, sollten Funktionen nie zwei unterschiedliche Typen zurückliefern: False oder eine Liste. Wenn keine Tour gefunden werden kann, ist das eine Ausnahme und sollte daher auch eine Exception werfen.
nadrosch
User
Beiträge: 2
Registriert: Donnerstag 14. Dezember 2017, 01:47

@Sirius3 and @all
Vielen Dank für die Tipps!
Ich habe versucht meinen Code übersichtlicher zu gestalten. Um in in einzelne Funktionen aufzuteilen fehlt mir gerade leider die Zeit.
Folgenden Algorihtmus Verwende ich

1. Überpruefen ob jeder Knoten geraden Knotengrad hat (Voraussetzung für Eulertour)
2. Nicht isolierten Startknoten wählen (falle alle Knotengrade = 0 Leere Liste zurück)
3. Vom startknoten eine kante zum naechsten knoten gehen
4 Diese Kante aus der Adjazenzliste löschen
5 So lange weiter gehen bis Kreis entsteht
6 neuen Startknoten mit min 2 freien Kanten wählen (dieser muss element des ersten Kreises sein)
4. 5. und 6. so lange wiederholen bis kein neuer startknoten mehr gefunden werden kann
7. Überprüfen ob nun noch isolierter Graph übrig ist (geloeschtegrade != knotengrade) -> False
8. Sind alle Kanten begangen (geloeschtegrade == knotengrad)i
9. Die einzelnen Kreise zu einer Eulertour zusammenfügen

Ich werde mein Glück in der Rechnerbetreuung des moduls versuchen (Betreuungskapazitäten zur Zeit stark ausgelastet).
Falls jemand doch noch eine Idee (mögliche Fehlerquellen, Laufzeitverbesserung) hat bin ich sehr Dankbar für jeden Tipp!
Liebe Grüße
nadrosch

Code: Alles auswählen

#!/usr/bin/env python


def eulertour(adj):	#übergabe adj ist adjazenzliste (liste von listen)

#prüfen ob es Knoten mit ungeradem Knotenknotengrad gibt?
#Suche des Starknotens 
	
	startknoten = -1	
	for knoten in adj:  	#gehe die Verknüpf ungs listen der einzelnen Knoten durch (listen in adj)       				
		if len(knoten)%2 != 0:		#wenn knotengrad ungerade 
			#print("knotengrad")			
			return False   	            		
	
	knoteninde =-1	
	for knoten in adj:	#finde nicht isolierten startknoten
		knoteninde +=1
		if len(knoten) > 0:	
			startknoten= knoteninde
			break

	if startknoten == -1:		#falls alle knoten isolierte knoten sind
		listeO = []
		return listeO           #in diesem Fall wird ein einzelner Knoten (der erste) aus der (index) adj liste ausgegeben

#Eulertoursuche
	
	kreisliste = [[startknoten]]                        
	kreisanzahl = 1
	nextknoten = startknoten                   
	hneuerkreis = 0			#Hilfsvariable um zu ueberpruefen ob ein Kreis geschlossen wurde
	knotengrade = 0                            
	geloeschtegrade = 0		
	hzushang =0			#Hilfsvariable um festzustellen ob Graph ohne die iso Knoten zushaengend ist

	for elemente in adj:                       #summe der knotengrade
		knotengrade += len(elemente)

	while geloeschtegrade != knotengrade:		#solange nicht alle verknüpfungen gelöscht sind
		#print(nextknoten)
		if len(adj[nextknoten]) == 1 and adj[nextknoten][0] == startknoten:	#wenn nextknoten nur eine verknuepfung hat und diese der aktuelle startknoten ist
			#print("neuer Kreis", Kkeisliste)	
			kreisliste[kreisanzahl-1].append(startknoten)	#dann wird der Kreis geschlossen
			indnextinst =adj[startknoten].index(el)		#wird zum index des aktuellen nextknotens in startknotenliste
			del adj[startknoten][indnextinst]		# verknuepfung von nextknoten in startknotenliste  wird geloescht
			del adj[nextknoten][0]				# verknuepfung des startknotens in der nextknotenliste wird geleoscht
			geloeschtegrade +=2

			for elem in adj:	#neuer Starknoten wird gesucht
				
				if len(elem) > 0:	#wenn ein Knoten noch mehr als 1 verknuepfung hat (die liste alsoi mehr als einen eintrag
					hzushang +=1 
					#print(h)
					for elle in elem:	#gehe die verknuepfungen durch
						if elle in kreisliste[kreisanzahl-1]:	#wenn verknuepfungsknoten (elle) bereits besucht wurde			
							#print("asdfasdf")
							startknoten = elle		#neuer Startknoten wird initialisiert     
							nextknoten = startknoten	#naechster Knoten ist der Startknoten
							hneuerkreis = 1			#Hilfsvariable zum überpruefen ob zushaengend
							kreisanzahl += 1
							kreisliste.append([nextknoten])	#Fügt Kreisliste neuen Kreis hinzu mit startknoten a
							break
				if hneuerkreis == 1:					
					break	

			if hneuerkreis != 1 and hzushang > 0:	#überpruefung zusammenhaengend???
				#print("zushang")				
				return False	 	
			else:				#hzushang zuruecksetzen
				hneuerkreis = 0							
				hzushang =0	
			
		for el in adj[nextknoten]:	#gehe verknüpfungen des nextKnoten durch um naechsten Knoten zu finden
			
			if el != startknoten:					#wenn verknuepfung nicht zum Startknoten 
				indelinnext = adj[nextknoten].index(el)		#wird zum index des elementes in nextknoten
				indnextinel = adj[el].index(nextknoten)		#wird zum index von nextknoten in el
				del adj[nextknoten][indelinnext]		#lösche die verknüpfung von nextknoten zu el in nextknotenliste
				del adj[el][indnextinel]			#loesche die verknuepfung von el zu nextknoten in elliste
				geloeschtegrade +=2
				nextknoten = el					
				kreisliste[kreisanzahl-1].append(nextknoten)	#naechster knoten (el) wird der Kreisliste hinzugefuegt
				break

	while len(kreisliste)>1:	#zusammenfügen der Kreislisten zu einer Eulertour [[a...a],[b...b],[c...c]...] wird zu

		startkreisb = kreisliste[1][0]		 # wird zum Startknoten der b liste also b
		indbinkreisa = kreisliste[0].index(startkreisb)		 #wird zum index von b in der a liste
		kreisliste[0] =kreisliste[0][0:indbinkreisa]+kreisliste[1]+kreisliste[0][indbinkreisa+1:] #einfügen von kreisb in kreisa [[a...b...b...a],[b..b],[c...c],...]
		del kreisliste[1]	 #löschen von kreisb da bereits eingefuegt [[a...b...b...a],[c...c],...]
	eulertour = kreisliste[0]

	return eulertour                 #eulertour fertig (alle kanten benutzt)

def test():
	
	a=[[1,2,3],[0,2,3],[0,1,3,4],[0,1,2,4],[2,3]]
	b=[[1,1],[0,0]]
	c=[[1,2,3,4],[0,2,3,4],[0,1,3,4],[0,1,2,4],[0,1,2,3]]
	d=[[1,3],[0,3],[],[0,1]]
	e=[[1,1],[0,0],[3,3],[2,2]]
	f=[[],[2,4],[1,3,6,12],[2,4],[1,3,5,7],[4,8],[2,8],[4,8,10,9],[7,9,10,11,6,5],[7,8,10,11],[7,8,9,11],[8,9,10,12],[2,11]]
	g=[[1,3],[0,3],[4],[0,1],[2],[],[]]
	h=[[1,3],[0,3],[4],[0,1],[2]]
	i=[[],[],[]]
	j=[[7,9],[2,4],[1,3],[2,4,5,6],[1,3],[3,10,7,11],[7,3],[0,8,5,6],[7,9],[8,0],[12,5],[5,12],[10,11]]
	k=[[7,9,13,4],[2,4],[1,3],[2,4,5,6],[1,3,13,0],[3,10,7,11],[7,3],[0,8,5,6],[7,9],[8,0],[12,5],[5,12],[10,11],[4,0]]
	l=[[]]

	List=[a,b,c,d,e,f,g,h,i,j,k,l]

	for list in List:
		print(list)
		print(eulertour(list), "\n")
		
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@nadrosch: wie hast Du denn getestet, dass die einzelnen Teile Deiner Funktion funktionieren? Gar nicht, denn zum Testen muß die Funktionalität isoliert werden, ergo einzelne Funktionen geschrieben werden.

Dein Algorithmus ist auf einer Ebene viel zu detailiert:
1. solange Kreise isolieren, bis keine Kante mehr übrig ist (falls ein Kreis nicht geschlossen werden kann -> Fehler)
2. Kreise zu einem Kreis zusammenfügen.

Jetzt kannst Du Dich um Punkt 1 isoliert kümmern: einen Kreis isolieren. So entstehen ganz automatisch einzelne Funktionen.
Antworten