 -----------------------------------------------------------------------------



			    Programmierpraktikum

				 C L I Q U E

		    Modul zum Loesen des Cliquen Problems

			   Torsten Bachmann  #8527

		    Bearbeitungszeit:  Sep, Okt, Nov `89
		  
		   		 
		   		 
-----------------------------------------------------------------------------
CLIQUE:

ist ein Modul,  dass eine maximale Clique in einem Graphen bestimmt.  Als Da-
tenstruktur fuer den Graphen wird Sgraph zugrundegelegt.

	L_node_list C_find_clique (graf);
	Sgraph graf;

	L_node_list C_find_clique_in_list(list);
	L_node_list list;

C_find_clique berechnet eine Clique in einem Graphen.  Uebergeben wird dieser
Graph als  Sgraph-Struktur.  In diesem Graphen darf zwischen zwei Knoten hoe-
chstens eine Kante existieren. Zurueckgeliefert wird eine Liste der ermittel-
ten Knoten (siehe Modul LISTE).
C_find_clique_in_list arbeitet analog zu C_find_clique,  jedoch wird als Ein-
gabe kein gesamter Graph erwartet, sondern nur eine Liste von Knoten.  So ist
es moeglich, eine Clique in einem Teilgraph zu suchen.


----------------------------------------------------------------------------
KCLIQUE: (Knotenclique)

	L_node_list K_knoten_clique( node, min, max, nodebase )
	Snode		node;
	int		min;
	int		max;
	L_node_list	nodebase;

K_knoten_clique berechnet eine maximale Clique, die den Knoten node enthaelt.
min und max sind untere und obere Schranken fuer die Groesse der Clique.  Die
Clique  wird  auf dem von den Knoten aus nodebase induzierten Teilgraphen er-
rechnet.  Falls keine Clique mit mehr als min Knoten ermittelt  werden  kann,
wird eine leere Liste zurueckgegeben.  Falls eine Clique mit  mindestens  max
Knoten gefunden wurde,  wird nicht mehr versucht, eine bessere Loesung zu su-
chen.

-----------------------------------------------------------------------------
LISTE:

Dient der Verwaltung eigener Knotenmengen. Hierbei werden Zeiger auf die Kno-
ten in einem Array abgelegt,  welches dynamisch angelegt wird.  Benutzt  wird
hierzu folgender Datentyp:

	typedef struct L_node_list
	{       int	curlen;
		int	maxlen;
		Snode	*nodes;
	}
		 *L_Node_list;

Das Feld curlen enthaelt die Anzahl der aktuell belegten Feldeintraege;  max-
len enthaelt die Zahl der Eintraege,  fuer die Speicher reserviert ist; nodes
ist ein Zeiger auf ein Array mit Knotenzeigern.


	L_Node_list L_create_liste (size)
	int size;

Legt eine neue Liste an und reserviert Speicher fuer size Eintraege.


	void L_append (list, node)
	L_Node_list list;
	Snode node;

	void L_delete_entry (list, nr)
	L_nodelist list;
	int nr;

	void L_move(list1, list2, nr)
	L_nodelist list1, list2;
	int nr;

L_append haengt den Knoten node an list an.  Falls der  reservierte  Speicher
nicht ausreicht, wird weiterer Speicher dazugenommen (mittels realloc).
L_delete_entry loescht den nr-ten Knoten. L_move verschiebt den nr-ten Knoten
von list1 nach list2 ( wird in list1 geloescht ).  Wenn  L_delete_entry  oder
L_move aus einer L_for_node_list Schleife aufgerufen werden,  muss die  Lauf-
variable nr anschliessend um 1 verringert werden.


Mit Hilfe der nachfolgenden zwei Makros koennen saemtliche Knoten dieser Lis-
te durchlaufen werden.  Waehrend dieses Durchlaufens angefuegte Knoten werden
wie bei einer FIFO-Schlange behandelt.  Die Integer-Laufvariable nr gibt  die
aktuelle Position im Feld (list->nodes[ ]) an. Mittels des 3. Makros kann ein
Feldelement angesprochen werden.

	L_for_node_list(list, nr)
	{ ....
	} L_end_for_node_list(list, nr)

	Snode L_list_entry(list, nr)
	
Zum Loeschen einer Liste dient die naechste Funktion,  die  den  reservierten
Speicher wieder freigibt. Mit der uebernaechsten wird der benoetigte Speicher
moeglichst klein gehalten.

	void L_close_list (list)
	L_node_list list;

	void L_minimize_mem (list)
	L_node_list list;

-----------------------------------------------------------------------------
GFUNC:

stellt allgemeine Routinen auf Graphen zur Verfuegung:


	G_Test_Stack_Overflow (pointer)

Bricht das Programm mit der Meldung "Speicher voll" ab, falls der uebergebene
Zeiger den Wert NULL hat.


	int G_knotenanzahl(g)
	Sgraph g;

Ermittelt die Anzahl der Knoten in einem Graphen.


	int G_kanten_anzahl(n)
	Snode n;

Bestimmt die Anzahl der Kanten an einem Knoten.


	bool G_exist_edge(knoten1,knoten2)
	Snode knoten1;
	Snode knoten2;

Liefert TRUE, falls zwischen Knoten1 und Knoten2 eine Kante existiert.


Zur Ueberpruefung, ob ein Graph eine erlaubte Eingabe fuer C_find_clique dar-
stellt,  d.h.  zwischen je zwei Knoten hoechstens eine Kante existiert, steht
folgende Funktion zur Verfuegung:

	Sedge C_has_double_edges (graf);
	Sgraph graf;
	
Diese liefert bei einer korrekten Eingabe NULL zurueck. Sonst einen Zeiger 
auf diese Kante.
	


-----------------------------------------------------------------------------
Zur Vorgehensweise:

CLIQUE:  Sgraph g      --> L_node_list,   bzw
         L_node_list l --> L_node_list
 
   Ermittelt eine maximale Clique, die im Graphen g, bzw in der Knotenmenge
   l enthalten ist. Ergebnis ist eine Liste dieser Knoten.

1. Falls leerer Graph: STOP! Fertig.
2. Bestimmen einer oberen Schranke max fuer die erreichbare Cliquengroesse:
   Idee:  Um eine max-Clique zu finden, muessen mindestens max Knoten mit je-
          weils mindestens max-1 Kanten vorhanden sein.  
   Setze max = #g (Anzahl der Knoten im Graphen g);
   Erniedrige max solange um 1, bis die obige Bedingung zutrifft (da der Gra-
   ph mindestens 1 Knoten ohne Kanten enthalten muss, terminiert dies immer).
   Falls max = 1 STOP! Rueckgabe 1-Clique.  
3. Aufstellen einer Liste (list) zum Absuchen der Knoten.  Dazu werden zuerst
   alle Knoten mit max Kanten in die Liste eingetragen,  dann alle mit max-1,
   ..., bis zu allem mit 2 Kanten, dann noch ein Eintrag mit nur einer Kante.
3. Fuer jeden Knoten list[i] dieser Knotenliste bestimme die groesste Clique,
   die den Knoten n enthaelt (KCLIQUE(list[i], min, max)). min steht fuer die
   Mindestgroesse, die die Clique haben muss;  wurde z.B. schon eine 3-Clique
   gefunden, erhaelt min diesen Wert.  Ist in KCLILQUE abzusehen,  dass keine
   groessere CLique ermittelt werden kann, wird die Ueberpruefung an dem ent-
   sprechenden Knoten vorzeitig abgebrochen.
   STOP! Rueckgabe der groessten gefundenen Knotenmenge.   


KCLIQUE:  Snode n  X  int min  X  int max  -->  L_node_list

   (Knoten-clique) Ermittelt eine maximale Clique, die den Knoten n enthaelt,
   wenn diese Clique  mindestens min Knoten hat.  Wird vorzeitig  eine Clique
   mit  max Knoten gefunden,  so wird die Suche sofort abgebrochen.  Ergebnis
   ist die Liste der gefundenen Knoten. 

1. Initialisierung: Erstellen von zwei Knotenmengen (Listen):  eine (LD) fuer
   die Knoten,  die garantiert zur Clique gehoeren  und die andere  (LU) fuer
   die Knoten,  von denen die Zugehoerigkeit zur Liste  noch nicht feststeht.
   In die Menge LD wird der Knoten n aufgenommen, in LU alle Knoten, die eine
   Kante zu n haben.
   Falls  #LU <= min  STOP! Rueckgabe einer leeren Liste (keine Verbesserung
   moeglich).
2. Diejenigen Knoten aus LU,  die zu allen anderen Knoten aus  LU  eine Kante
   besitzen,  werden ebenfalls nach LD uebernommen,  da diese auch zur Clique
   gehoeren.
   Falls LU die leere Menge ist, STOP! Rueckgabe von LD
3. In LU  muessen an dieser Stelle noch mindestens  2 Knoten  enthalten sein.
   Loesche alle Knoten aus LU, die zu keinem anderen Knoten aus LU eine Kante
   besitzen.
   Falls LU leer, nimm einen dieser geloeschten Knoten in LD auf.
		  STOP! Rueckgabe von LD
4. Bestimme die die maximale Clique der uebriggebliebenen Knotenmenge LU:
   L = CLIQUE ( LU );
   FERTIG! Rueckgabe von LD vereinigt L.
   
   
-----------------------------------------------------------------------------
Bemerkung zur Komplexitaet:

Speicher:
   wird nur benoetigt, um die diversen Knotenmengen (Listen) zu speichern.
   Jede dieser Mengen belegt ein ARRAY von maximal #(Knoten im graph) Zeigern
   auf Knoten.

Zeit:
   Eine schlechte Laufzeit wird erzielt,  wenn es  viele Knoten  mit gleicher
   Kantenzahl gibt, wobei an keinem dieser Knoten eine Clique der Groesse max
   haengt.  Dieser Fall  tritt  z.B.  bei einem Gitter auf,  bei dem nur eine
   2-Clique existiert,  jedoch jeder innere Knoten  ein potentieller Kandidat
   fuer eine 5-Clique ist. In diesem Fall muss die Clique an jedem Knoten des
   Graphen bestimmt werden,  was zu einer langen Laufzeit der Schleife (3) in
   der Funktion CLIQUE fuehrt, ein rekursiver Aufruf wird jedoch nicht benoe-
   tigt.
   
   Gute Laufzeiten werden erreicht fuer:
     - einen leeren Graph
     - Graphen ohne Kanten
     - vollstaendige Graphen
     - Graphen, die eine Clique der groesse max (siehe obigen Algorithmus) 
       enthalten
     -  .
        .
        .
        .
           

	
-----------------------------------------------------------------------------
Verfuegbarkeit:

	Die Dateien sind verfuegbar unter  /home/b/bachmant/clique/*  auf den
	CIP-Sun's. Beipielgraphen haben alle die Endung *.g
	


-----------------------------------------------------------------------------
Sonstiges:

	Das Beispielprogramm MAIN liest einen Graphen im (GraphEd Format) von
	stdin. Als Ergebnis wird ein Graph, ebenfalls im GraphEd Format, nach
	stdout wieder ausgegeben. In diesem Graphen erhalten alle Knoten, die
	zur Clique gehoeren, den Labelnamen "**". Alle sonstigen Ausgaben des
	Programms werden nach stderr geschrieben.

	Der dynamisch belegte Speicher,  bis auf die Ergebnisliste,  ist nach
	Abarbeitung des Programms wieder freigegeben.


-----------------------------------------------------------------------------
