Kreise in Graph finden

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Kreise in Graph finden

    Hallo,

    ich versuche momentan gerade, in einem (gerichteten) Graphen die Kreise zu finden.

    Momentan ist mein Ansatz der, dass ich rekursiv Knoten aus dem Graph entferne und überprüfe, ob der resultierende Teilgraph ein Kreis ist. Die Überprüfung der Kreiseigenschaft nehme ich durch eine beschränkte Tiefensuche vor, die durch n = Anzahl der Knoten im Graph beschränkt ist. Wenn der Walker nach n+1 Schritten alle Knoten besucht hat und wieder beim Ursprung ankommt, dann ist ein Kreis gefunden.

    Dabei gibt es zwei Probleme:
    • Zum einen hat ein Graph mit n Knoten 2^n Teilgraphen (wenn man die Kanten außer Acht lässt), was zunächst sehr viel ist. Man kann es dadurch verringern, dass bei einem Teilgraph, der ein Kreis ist, die Rekursion nicht weiter betrachtet werden muss (das ist sinnvoll, wenn man nicht alle Kreise, sondern nur die größtmöglichen finden will). Das ändert aber nichts am worst-case-Verhalten.
    • Zum anderen ist das Überprüfen, ob ein Graph ein Hamiltonkreis ist, bekanntermaßen ein NP-vollständiges Problem. D.h., die Frage lässt sich auch nicht in polynomieller Laufzeit lösen.
    Habt ihr irgendwelche Ideen, wie man effizient Kreise in einem Graphen finden kann? Kennt ihr irgendwelche Algorithmen?
  • Ich bin jetzt noch mal anders herangegangen:

    Alle n Knoten werden in einer durch n beschränkten Tiefensuche durchschritten. Wichtig ist festzustellen, ob dabei ein Kreis gefunden wurde. Rückgabewert ist aber nicht der Kreis, sondern der längste Pfad, der innerhalb der Tiefensuche gefunden wurde (sonst würde evtl. ein recht kurzer Kreis zurückgegeben werden).

    Im zweiten Schritt werden dann die Tiefensuchen noch einmal betrachtet, in denen ein Kreis gefunden wurde. Die Pfade werden vom letzten Knoten ausgehend daraufhin untersucht, ob ein Pfad zum ersten Knoten des Pfads, also ein Kreis, existiert. Wenn ja, dann haben wir einen Kreis gefunden und können ihn speichern. Wenn nein, dann wird der letzte Knoten des Pfads gelöscht und der vorletzte betrachtet usw.

    Nun hat man eine Menge an Kreisen, die man noch daraufhin untersuchen muss, ob einige Kreise von anderen überlagert werden.

    Dieser Algorithmus ist zumindest etwas stack-freundlicher als der vorherige und terminiert auf jeden Fall. Allerdings ist er noch zu langsam. Schon für Graphen mit n > 20 Knoten kann es leicht 5 Minuten dauern, bis die Kreise gefunden wurden. Interessanterweise ist dabei der erste Schritt, die n-fache Tiefensuche, schon sehr rechenlastig. Ich hätte gedacht, dass eher der zweite Schritt der rechenlastige ist, weil hier für k Pfade der (Durchschnitts-)Länge l k*l Tiefensuchen durchgeführt werden.

    Wie kommt das? In der beschränkten Tiefensuche ist die Rekursionstiefe maximal n, und ein Knoten hat maximal n-1 Nachbarn, somit wird die Schleife in einem Methodenaufruf auch maximal n-1-mal durchlaufen. Das führt doch zu einer Komplexität von O(n * (n-1)) = O(n^2), oder? Die Tiefensuche wird n-mal ausgeführt, also O(n * n^2) = O(n^3). Das sollte doch relativ schnell erledigt sein. Dauer aber irgendwie sehr lange.

    Hier mal der Quelltext für den Walker bei der Tiefensuche im ersten Schritt, falls es jemanden interessiert. Die Methode getHeads() gibt die Nachfolger eines Knotens zurück. pointsTo(GraphNode node) ermittelt, ob ein Knoten einen Nachfolger node hat oder nicht. Beim ersten Aufruf von buildWalker() ist step = 0 und visitedList enthält genau einen Knoten.

    Quellcode

    1. public class GraphWalker {
    2. List<GraphNode> nodeList;
    3. boolean hasCycle;
    4. public GraphWalker(List<GraphNode> nodeList) {
    5. this.nodeList = nodeList;
    6. hasCycle = false;
    7. }
    8. @SuppressWarnings("unchecked")
    9. public List<GraphNode> buildWalker(int step, List<GraphNode> visitedList) {
    10. if(step > nodeList.size()-2) return visitedList;
    11. if(visitedList.get(step).getHeads() == null) return visitedList;
    12. List<GraphNode>[] newVisitedList = new ArrayList[visitedList.get(step).getHeads().size()];
    13. int i=0;
    14. for(GraphNode headNode : visitedList.get(step).getHeads()) {
    15. if(headNode == null) {
    16. i++;
    17. continue;
    18. }
    19. if(!nodeList.contains(headNode)) {
    20. i++;
    21. continue;
    22. }
    23. if(visitedList.contains(headNode)) {
    24. hasCycle = true;
    25. i++;
    26. continue;
    27. }
    28. newVisitedList[i] = new ArrayList<GraphNode>(nodeList.size());
    29. newVisitedList[i].addAll(0, visitedList);
    30. newVisitedList[i].add(step+1, headNode);
    31. // Check if the path is complete and whether it goes full circle
    32. if(newVisitedList[i].size() == nodeList.size()) {
    33. GraphNode finalNode = newVisitedList[i].get(newVisitedList[i].size()-1);
    34. if(finalNode != null && finalNode.pointsTo(visitedList.get(0))) {
    35. visitedList = newVisitedList[i];
    36. hasCycle = true;
    37. break;
    38. }
    39. }
    40. List<GraphNode> recVisitedList = buildWalker(step+1, newVisitedList[i]);
    41. // Check if this is a dead end. If so, perform backtracking.
    42. if(recVisitedList.equals(newVisitedList)) {
    43. if(newVisitedList[i].size() >= step) {
    44. newVisitedList[i].remove(step+1);
    45. }
    46. }
    47. else {
    48. newVisitedList[i] = recVisitedList;
    49. }
    50. i++;
    51. }
    52. int maxSize = 0;
    53. int index = 0;
    54. for(i=0; i < newVisitedList.length; i++) {
    55. if(newVisitedList[i] != null && newVisitedList[i].size() >= maxSize) {
    56. index = i;
    57. maxSize = newVisitedList[i].size();
    58. }
    59. }
    60. if (index >= newVisitedList.length || newVisitedList[index] == null) {
    61. return visitedList;
    62. }
    63. else {
    64. return newVisitedList[index];
    65. }
    66. }
    67. public boolean getHasCycle() {
    68. return hasCycle;
    69. }
    70. }
    Alles anzeigen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von wulfgang ()