1 / 45

Grafalgoritmer I

Grafalgoritmer I. Plan. Grafer - definition - anvendelser - terminologi - eksempler på grafproblemer Grafgennemgang - dybde-først-gennemgang - bredde-først-gennemgang Sammenhæng - 2-sammenhæng - (foren-og-find). A.

manchu
Télécharger la présentation

Grafalgoritmer I

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Grafalgoritmer I

  2. Plan • Grafer- definition - anvendelser - terminologi - eksempler på grafproblemer • Grafgennemgang - dybde-først-gennemgang - bredde-først-gennemgang • Sammenhæng - 2-sammenhæng - (foren-og-find)

  3. A I H B G C D E F V = {A, B, C, D, E, F, G, H, I} E = {(A,B),(A,C),(A,F),(A,G),(D,E),(D,F),(E,F),(E,G),(H,I)} Grafer • En graf er et nyttigt abstrakt begreb. • Intuitiv definition: En graf er en mængde af objekter og forbindelser imellem disse. • Matematisk definition: En graf G = (V,E) består af en endelig mængde af knuder, V, og en endelig mængde af kanter, E, hvor hver kant forbinder to af knuderne (E VxV).

  4. Anvendelsesområder • Alt, hvad der involverer relationer imellem • objekter, kan modelleres ved hjælp af en graf Trafiknetværk: Knuder: byer, vejkryds Kanter: veje Elektriske kredsløb: Knuder: komponenter Kanter: ledninger Organiske molekyler: Knuder: atomer Kanter: bindinger Programsystemer: Knuder: rutiner Kanter: rutine A kan kalde rutine B Objektorienteret design (UML-diagrammering): Knuder: klasser/objekter Kanter: nedarvning, aggregering eller associering Projektplanlægning: Knuder: delopgaver Kanter: præcedenser (A skal udføres før B)

  5. En kants 2 knuder kaldes endeknuder for kanten. I H I H Terminologi Hvis rækkefølgen af en kants endeknuder har betydning, kaldes kanten for orienteret. Dette angives på den grafiske repræsentation ved at kanten forsynes med en pil. Endeknuderne kaldes da for henholdsvis begyndelsesknuden og slutknuden. En orienteret graf er en graf, hvor alle kanter er orienterede. En ikke-orienteret graf er en graf, hvor ingen kanter er orienterede.

  6. A B G C D E F Cykler: FDEF, AFEGA og AFDEGA. Terminologi (fortsat) • En vej er en liste af knuder, hvor successive knuder er forbundet med en kant. • En vej kaldes simpel, hvis ingen knude gentages. • En cykel er en vej, der er simpel, bortset fra at den første og sidste knude er den samme.

  7. A I H B G C D E F Terminologi (fortsat) • En graf G’ = (V’,E’) er en delgraf af G = (V,E), hvis V’ Vog E’ E. • En graf kaldes sammenhængende, hvis der for enhver knude findes en vej til enhver anden knude. • En graf, der ikke er sammenhængende, består af sammenhængende delgrafer, også kaldet komponenter. 2 komponenter

  8. Udspændende træ for G Graf G Terminologi (fortsat) • Et træ er en sammenhængende graf uden cykler. • En mængde af disjunkte træer kaldes en skov. • Et udspændende træ for en graf G er en delgraf af G, der indeholder alle grafens knuder, og som udgør et træ.

  9. Terminologi (fortsat) • En graf, hvor enhver knude er forbundet med enhver anden knude, kaldes for komplet. • [ for en ikke-orienteret graf: E = V*(V-1)/2) ] • En tyndgraf er en graf med relativt få kanter. • En tætgraf er en graf med relativt mange kanter. • En vægtet graf er en graf, hvor kanterne er forsynet med talværdier, kaldet vægte. [ vægtene repræsenterer normalt omkostninger ]

  10. Basale grafproblemer • Veje: Er der en vej fra knude A til knude B? • Cykler: Indeholder grafen en cykel? • Sammenhæng (udspændende træ): Er der for hver knude en vej til enhver anden knude? • 2-sammenhæng: Vil grafen blive usammenhængende, hvis en af knuderne (og de tilstødende kanter) fjernes? • Planaritet: Kan grafen tegnes, uden at to kanter krydser hinanden?

  11. Basale grafproblemer(fortsat) • Korteste vej: Hvilken vej er den korteste mellem knude A og knude B? • Længste vej: Hvilken vej er den længste mellem knude A og knude B? • Minimalt udspændende træ: Hvad er den billigste måde at forbinde alle knuder? • Hamilton-cykel: Er der en cykel, som indeholder samtlige knuder? • Den rejsende sælgers problem: Hvilken Hamilton-cykel er den billigste? • Isomorfi: Symboliserer to grafrepræsentationer den samme graf?

  12. Repræsentation af grafer • Grafer er abstrakte matematiske objekter. Algoritmer må arbejde med konkrete repræsentationer. • Mange mulige repræsentationer. Valget er bestemt af algoritmer og graftyper (tynde/tætte, vægtede/uvægtede, orienterede/ikke-orienterede). • I det følgende gennemgås 3 repræsentationer: (1) kantmængde (2) nabomatrix (3) nabolister

  13. A I H B G C E D F (1) Kantmængde-repræsentation endNode[0][0] = A, endNode[0][1] = G endNode[1][0] = A, endNode[1][1] = B endNode[2][0] = A, endNode[2][1] = C endNode[3][0] = E, endNode[3][1] = D endNode[4][0] = F, endNode[4][1] = D endNode[5][0] = H, endNode[5][1] = I endNode[6][0] = F, endNode[6][1] = E endNode[7][0] = A, endNode[7][1] = F endNode[8][0] = G, endNode[8][1] = E

  14. Oprettelse af kantmængde • class Graph { • int V, E, endNode[][]; • void read() { • V = IO.readInt(); E = IO.readInt(); • endNode = new int[E][2]; • for (int i = 0; i < E; i++) { • endNode[i][0] = IO.readInt(); • endNode[i][1] = IO.readInt(); • } • } • } Her identificeres knuderne ved heltal. Hvis knuderne er navngivet ved tegnstrenge kan metoderne indexog namedefineres int index(String nodeName) String name(int nodeIndex)

  15. class Edge { • int v1, v2; • Edge(int a, int b) { v1 = a; v2 = b; } • } • class Graph { • int V, E; • Vector edges; • void read() { • V = IO.readInt(); E = IO.readInt(); • edges = new Vector(E); • for (int i = 1; i <= E; i++) • edges.addElement( • new Edge(IO. readInt(), • IO. readInt()); • } • } Kantmængde-repræsentationved hjælp af kant-objekter

  16. A I H B G C D E F (2) Nabomatrix-repræsentation • A B C D E F G H I • A 1 1 1 0 0 1 1 0 0 • B 1 1 0 0 0 0 0 0 0 • C 1 0 1 0 0 0 0 0 0 • D 0 0 0 1 1 1 0 0 0 • E 0 0 0 1 1 1 1 0 0 • F 1 0 0 1 1 1 0 0 0 • G 1 0 0 0 1 0 1 0 0 • H 0 0 0 0 0 0 0 1 1 • I 0 0 0 0 0 0 0 1 1 Bemærk. Dobbelt repræsentation af hver kant, hvis grafen er ikke-orienteret.

  17. Oprettelse af nabomatrix • class Graph { • int V, E; • boolean a[][]; • void read() { • V = IO.readInt(); E = IO.readInt(); • a = new boolean[V+1][V+1]; • for (int x = 1; x <= V; x++) • a[x][x] = true; • for (int i = 1; i <= E; i++) { • int x = IO.readInt(); • int y = IO.readInt(); • a[x][y] = a[y][x] = true; • } • } • }

  18. Repræsentation ved hjælp af Javas class BitSet • class Graph { • int V, E; • BitSet a[]; • void read() { • V = IO.readInt(); E = IO.readInt(); • a = new BitSet[V+1]; • for (int x = 1; x <= V; x++) { • a[x] = new BitSet(V+1); • a[x].set(x); • } • for (int i = 1; i <= E; i++) { • int x = IO.readInt(); • int y = IO.readInt(); • a[x].set(y); a[y].set(x); • } • } • } a[x].get(y): Er (x,y) kant i grafen?

  19. A I H B G C D E F G F C B A A F E G F D A E D A E I H (3) Naboliste-repræsentation • A: • B: • C: • D: • E: • F: • G: • H: • I: Bemærk. Dobbelt repræsentation af hver kant, hvis grafen er ikke-orienteret.

  20. class Node { int v; Node next; Node(int vv; Node nn) { v = vv; next = nn; } } class Graph { int V, E; Node adj[], z; void read() { V = IO.readInt(); E = IO.readInt(); adj = new Node[V+1]; z = new Node(0, null); z.next = z; for (int i = 1; i <= V; i++) adj[i] = z; for (int i = 1; i <= E; i++) { int x = IO.readInt(); int y = IO.readInt(); adj[x] = new Node(y, adj[x]); adj[y] = new Node(x, adj[y]); } } } Oprettelse af nabolister

  21. Sammenligning af repræsentationer • Pladskrav: • Kantmængde: O(E (+V)) • Nabomatrix: O(V2) • Nabolister: O(V + E) • Repræsentation påvirker algoritmers effektivitet. • Værste tilfælde: • Er der en kant fra knude A til knude B? • Kantmængde: O(E) • Nabomatrix: O(1) • Nabolister: O(V) • Er der en kant fra knude A? • Kantmængde: O(E) • Nabomatrix O(V) • Nabolister: O(1)

  22. Systematisk gennemgangaf grafer • Mål: at besøge enhver knude i grafen. • Dybde-først-gennemgang: • Rekursiv algoritme: • * Mærk alle knuder “ubesøgt” (unseen). • * Ved besøg af en knude k: Mærk knuden “besøgt” Besøg (rekursivt) alle ubesøgte knuder, der er forbundet med k. • * Besøg knude 1. Besøg derefter en ubesøgt knude. Fortsæt indtil alle knuder er besøgt. Løser nogle simple grafproblemer: sammenhæng, cykler (hvordan?) Basis for løsning af nogle vanskelige grafproblemer: 2-sammenhæng, planaritet

  23. void search() { int k; for (k = 1; k <= V; k++) val[k] = unseen; id = 0; // number of visited nodes for (k = 1; k <= V; k++) if (val[k] == unseen) visit(k); } void visit(int k) { // adjacency lists val[k] = ++id; for (Node t = adj[k]; t != z; t = t.next) if (val[t.v] == unseen) visit(t.v); } void visit(int k) { // adjacency matrix val[k] = ++id; for (int t = 1; t <= V; t++) if (a[k][t] && val[t] == unseen) visit(t); } Implementering af dybde-først-gennemgang

  24. A A B G B G C C A: F C B G B: A C: A D: F E E: G F D F: A E D G: E A D D E E F F A A B G B G C C D D E E F F A A B G B G C C D D E E F F Dybde-først-gennemgang af en komponent

  25. 1 A 2 6 7 F B C 3 E 4 5 G D Dybde-først-gennemgang af en graf repræsenteret ved nabolister kræver tid proportional med V + E Dybde-først-gennemgang af en graf repræsenteret ved en nabomatrix kræver tid proportional med V2 Dybde-først-gennemgang af en komponent udgør et dybde-først-træ

  26. Ikke-rekursiv dybde-først-gennemgang Stack stack = new Stack(V); void visit(int k) { // adjacency lists stack.push(k); while (!stack.empty()) { k = stack.pop(); val[k] = ++id; for (Node t = adj[k]; t != z; t = t.next) if (val[t.v] == unseen) { stack.push(t.v); val[t.v] = -1; } } } Arrayet val benyttes til nummerering af knuderne i den rækkefølge, de besøges. Knuder med val-værdi -1 er på stakken (og dermed set), men er endnu ikke blevet besøgt. Knuder med val-værdi unseen (= 0) er endnu ikke set.

  27. A A B G B G C C A: F C B G B: A C: A D: F E E: G F D F: A E D G: E A D D E E G B C F E B C F F F A A B G B G C C D D E E D B C F B C F F F A A A B G C B G B G C C D E D D E E C F F F F F Dybde-først-gennemgang medeksplicit stak

  28. 1 A 2 5 6 7 1 G F B C A 3 E 2 6 7 F B C 4 D 3 E 4 5 G D Stak-baseret dybde-først-træ • Er ikke det samme træ som ved rekursiv • dybde-først-gennemgang. Hvorfor? Svar: Algoritmerne adskiller sig ved deres behandling af knuder, der støder op til besøgte knuder: Rekursiv: besøg knuden straks Stakbaseret: behandl knuden som besøgt, men besøg den først, når den afstakkes

  29. void visit(int k) { // adjacency lists stack.push(k); val[k] = ++id; while (!stack.empty()) { k = stack.peek(); Node t; for (t = adj[k]; t != z && val[t.v] != unseen; t = t.next) ; if (t == z) stack.pop(); else { k = t.v; stack.push(k); val[k] = ++id; } } } Alternativ ikke-rekursiv dybde-først-gennemgang Stakkens benyttes til at huske de knuder, der har ført til den aktuelle knude. Besøgsrækkefølgen er den samme som ved den rekursive udgave af visit.

  30. Bredde-først-gennemgang • Hvis stakken erstattes med en kø, fremkommer • bredde-først-gennemgang. • void visit(int k) { // adjacency lists • queue.put(k); • while (!queue.empty()) { • k = queue.get(); val[k] = ++id; • for (Node t = adj[k]; t != z; • t = t.next) • if (val[t.v] == unseen) { • queue.put(t.v); • val[t.v] = -1; • } • } • }

  31. A A B G B G C C A: F C B G B: A C: A D: F E E: G F D F: A E D G: E A D D E E F F C B G E D F C B G A A B G B G C C D D E E F F G E D B G E D A A A B G C B G B G C C D E D D E E F F D F E D Bredde-først-gennemgang

  32. 1 A 2 3 4 5 C B G F 6 7 E D Bredde-først-gennemgang af en komponent udgør et bredde-først-træ

  33. aktuel start Dybde-først aktuel start Bredde-først Dybde-først versus bredde-først

  34. En 2-sammenhængende graf er en graf uden artikulationspunkter. Sammenhæng • Dybde-først-gennemgang kan benyttes til at finde grafens sammenhængende komponenter. Hver gang visit kaldes fra search, besøges samtlige knuder i en sammen-hængende komponent. • Dybde-først-gennemgang kan også benyttes til at afgøre om en graf er 2-sammenhængende. En graf siges at være 2-sammenhængende, hvis der ikke findes nogen knude, hvor en sletning medfører en usammenhængende graf. • Et knude i en sammenhængende graf kaldes et artikulationspunkt, hvis en sletning af knuden medfører, at grafen opdeles i 2 eller flere sammenhængende komponenter.

  35. A I H C B G K J D E M L F Eksempel på en ikke-2-sammenhængende graf • Artikulationspunkter: • A, G, H og J. • En 2-sammenhængende komponent af en graf er en maksimal delmængde af kanter, for hvilken den tilsvarende delgraf er 2-sammenhængende. • 2-sammenhængende komponenter: {(A,F),(A,C),(A,G),(C,G),(E,G),(D,E),(E,F),(D,F)} {(G,L),(G,J),(J,L),(J,M),(L,M)} {(A,B)} {(G,H)} {(H,I)} {(J,K)}

  36. A F B E G D C L H J I K M En knude er et artikulationspunkt, hvis blot én af dens sønner og alle dennes efterkommere ikke er forbundet (via en stiplet linie) med en knude højere oppe i træet. Roden er et artikulationspunkt, hvis den har to eller flere sønner. En grafs artikulationspunkterkan bestemmesved dybde-først-gennemgang

  37. Ændring af metoden visit • Metoden returnerer det mindste val-værdi for sete og besøgte knuder (knuden nærmest roden). • Artikulationspunkterne udskrives undervejs. int visit(int k) { Node t; val[k] = ++id; int min = id; for (t = adj[k]; t != z; t = t.next) if (val[t.v] == unseen) { int m = visit(t.v); if (m < min) min = m; if (m >= val[k]) IO.println(name(k));} else if (val[t.v] < min) min = val[t.v]; return min; } Test på rod udeladt

  38. Bestemmelse af 2-sammenhængende komponenter(opgave 30.4 i lærebogen) • Klassen Edge indføres: class Edge { int v1, v2; Edge(int a, int b) { v1 = a; v2 = b; } public boolean equals(Object o) { Edge e = (Edge) o; return (v1 == e.v1 && v2 == e.v2) || (v1 == e.v2 && v2 == e.v1); } public String toString() { return "(" + name(v1) + ", " + name(v2) + ")"; } } • Dernæst ændres metoden visit. En stak benyttes til at bestemme de 2-sammenhængende komponenter.

  39. int visit(int k) { val[k] = ++id; int min = id; for (Node t = adj[k]; t != z; t = t.next) { Edge e = new Edge(k, t.v); if (!stack.contains(e)) stack.push(e); if (val[t.v] == unseen) { int m = visit(t.v); if (m < min) min = m; if (m >= val[k]) { Edge x; do { x = (Edge) stack.pop(); IO.print(x + " "); } while (!x.equals(e)); IO.println(); } } else if (val[t.v] < min) min = val[t.v]; } return min; }

  40. int visit(int k,Vector components) { . . . if (m >= val[k]) { Vector component = new Vector(); Edge x; do { x = (Edge) stack.pop(); component.addElement(x); } while (!x.equals(e)); components.addElement(component); } . . . } Repræsentation af 2-sammenhængende komponenter

  41. Foren-og-find(ækvivalensklasser, komponenter) • public class EQ { • private int dad[]; • EQ(int size) { dad = new int[1+size]; } • public boolean find(int x, int y, • boolean doit) { • int i = x, j = y; • while (dad[i] > 0) i = dad[i]; • while (dad[j] > 0) j = dad[j]; • if (doit && i != j) dad[j] = i; • return (i != j); • } • } • Princip: hver ækvivalensklasse repræsenteres som et træ.

  42. bedst eller find løber op igennem et eller to træer (tester om den samme rod nås). Hvis doit == true, og der er tale om to forskellige træer, sammenkædes disse:

  43. Forbedret find • public boolean find(int x, int y, • boolean doit) { • int t, i = x, j = y; • while (dad[i] > 0) i = dad[i]; • while (dad[j] > 0) j = dad[j]; • // path compression • while (dad[x] > 0) • { t = x; x = dad[x]; dad[t] = i; } • while (dad[y] > 0) • { t = y; y = dad[y]; dad[t] = j; } • if (doit && i != j) • // weight balancing • if (dad[j] < dad[i]) { • dad[j] += dad[i] - 1; • dad[i] = j; • } • else { • dad[i] += dad[j] - 1; • dad[j] = i; • } • return (i != j); • }

  44. int find(int p) { return dad[p] == p ? p : (dad[p] = find(dad[p])); } void union(int p, int q) { int i = find(p), j = find(q); if (i == j) return; if (size[i] < size[j]) { dad[i] = dad[p] = j; size[j] += size[i]; } else { dad[j] = dad[q] = i; size[i] += size[j]; } } Alternativ implementering

  45. Ugeseddel 911. november - 17. november • • Læs kapitel 31 og 32 i lærebogen (side 451-481) • • Løs følgende opgaver • 1. Opgave 29.1 og 29.5. • 2. Opgave 30.1 og 30.2.

More Related