2017-08-21 2 views
0

Ich versuche den Algorithmus der Tiefensuche (DFS) für gerichtete Graphen zu implementieren, wie in Cormen et al., Introduction to Algorithms (3rd ed.) beschrieben. Hier ist meine Umsetzung so weit:Python "RuntimeError: maximale Rekursionstiefe überschritten" in der Tiefe - erste Suche

import pytest 
from collections import OrderedDict 
import copy 


class Node(object): 
    def __init__(self, color='white', parent=None, d=None, f=None): 
     self.color = color 
     self.parent = parent 
     self.d = d   # Discovery time 
     self.f = f   # Finishing time 


class Graph(object): 
    def __init__(self, edges, node_indices=None): 
     self.edges = edges 
     self.nodes = self.initialize_nodes(node_indices) 
     self.adj = self.initialize_adjacency_list() 

    def initialize_nodes(self, node_indices=None): 
     if node_indices is None: 
      node_indices = sorted(list(set(node for edge in self.edges for node in edge))) 
     return OrderedDict([(node_index, Node()) for node_index in node_indices]) 

    def initialize_adjacency_list(self): 
     A = {node: [] for node in self.nodes} 
     for edge in self.edges: 
      u, v = edge 
      A[u].append(v) 
     return A 

    def dfs(self): 
     self.time = 0 
     for u, node in self.nodes.items(): 
      if node.color == 'white': 
       self.dfs_visit(u) 

    def dfs_visit(self, u): 
     self.time += 1 
     self.nodes[u].d = self.time 
     self.nodes[u].color = 'gray' 
     for v in self.adj[u]: 
      if self.nodes[v].color == 'white': 
       self.nodes[v].parent = u 
       self.dfs_visit(v) 
     self.nodes[u].color = 'black' 
     self.time += 1 
     self.nodes[u].f = self.time 

    @staticmethod 
    def transpose(edges): 
     return [(v,u) for (u,v) in edges] 

    def strongly_connected_components(self): 
     self.dfs() 
     finishing_times = {u: node.f for u, node in self.nodes.items()} 
     self.__init__(self.transpose(self.edges)) 
     node_indices = sorted(finishing_times, key=finishing_times.get, reverse=True) 
     self.nodes = self.initialize_nodes(node_indices) 
     self.dfs() 
     return self.trees() 

    def trees(self): 
     _trees = [] 
     nodes = copy.deepcopy(self.nodes) 
     while nodes: 
      for u, node in nodes.items(): 
       if node.parent is None: 
        _trees.append([u]) 
        nodes.pop(u) 
       else: 
        for tree in _trees: 
         if node.parent in tree: 
          tree.append(u) 
          nodes.pop(u) 
     return _trees 

Um zu testen, dass es funktioniert, habe ich ein Beispiel aus Abbildung 22.9 aus dem Buch:

enter image description here

Nach der Umbenennung der Knoten ein zu h1-8 jeweils lief ich den folgenden Test:

def test_strongly_connected_components(): 
    edges = [(1,2), (5,1), (2,5), (5,6), (2,6), (6,7), (7,6), (2,3), (3,7), (3,4), (4,3), (4,8), (7,8), (8,8)] 
    graph = Graph(edges) 
    assert graph.strongly_connected_components() == [[1, 5, 2], [3, 4], [6, 7], [8]] 


if __name__ == "__main__": 
    pytest.main([__file__+"::test_strongly_connected_components", "-s"]) 

Dieser Test besteht und bestätigt die grau schattierten SCCs in der Abbildung.

Für die "echte" Übung muss ich jedoch eine Eingabedatei verwenden, die 875.714 Linien enthält, die Kanten darstellen (als Kopf-Schwanz-Paar von ganzen Zahlen) und die Größe der fünf größten SCCs ausgeben. Zu diesem Zweck habe ich versucht, den folgenden Test:

@pytest.fixture 
def edges(): 
    with open('SCC.txt') as f: 
     return [tuple(map(int, line.split())) for line in f.read().splitlines()] 

def test_SCC_on_full_graph(edges): 
    graph = Graph(edges) 
    SCCs = graph.strongly_connected_components() 
    print([map(len, SCCs)].sort(reverse=True))  # Read off the size of the largest SCCs 


if __name__ == "__main__": 
    pytest.main([__file__+"::test_SCC_on_full_graph", "-s"]) 

Allerdings laufe ich in eine RuntimeError: maximum recursion depth exceeded in cmp:

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <scc.Graph object at 0x103253690>, u = 209099 

    def dfs_visit(self, u): 
      self.time += 1 
      self.nodes[u].d = self.time 
      self.nodes[u].color = 'gray' 
      for v in self.adj[u]: 
>     if self.nodes[v].color == 'white': 
E     RuntimeError: maximum recursion depth exceeded in cmp 

scc.py:53: RuntimeError 
========================== 1 failed in 21.79 seconds =========================== 

ich gelesen habe über sys.setrecursionlimit zunimmt, dies scheint aber nicht empfohlen werden . Abgesehen davon bin ich mir nicht sicher, wie ich den Code verbessern könnte, da er den im Buch angegebenen Pseudocode ziemlich wörtlich implementiert. Irgendwelche Ideen, wie ich diesen Fehler überwinden kann?

Antwort

1

Ich habe das Problem mit der threading-Bibliothek mit einem erhöhten stack_size und Rekursionsbegrenzung gelöst. Hier ist der Code der Lösung:

import sys 
import pytest 
from collections import OrderedDict 
import copy 
import threading 


class Node(object): 
    def __init__(self, color='white', parent=None, d=None, f=None): 
     self.color = color 
     self.parent = parent 
     self.d = d   # Discovery time 
     self.f = f   # Finishing time 


class Graph(object): 
    def __init__(self, edges, node_indices=None): 
     self.edges = edges 
     self.nodes = self.initialize_nodes(node_indices) 
     self.adj = self.initialize_adjacency_list() 
     self.trees = dict() 

    def initialize_nodes(self, node_indices=None): 
     if node_indices is None: 
      node_indices = sorted(list(set(node for edge in self.edges for node in edge))) 
     return OrderedDict([(node_index, Node()) for node_index in node_indices]) 

    def initialize_adjacency_list(self): 
     A = {node: [] for node in self.nodes} 
     for edge in self.edges: 
      u, v = edge 
      A[u].append(v) 
     return A 

    def dfs(self): 
     self.time = 0 
     for u, node in self.nodes.items(): 
      if node.color == 'white': 
       self.dfs_visit(u, root=u) 

    def dfs_visit(self, u, root=None): 
     if u == root: 
      self.trees[root] = set() 
     self.time += 1 
     self.nodes[u].d = self.time 
     self.nodes[u].color = 'gray' 
     for v in self.adj[u]: 
      if self.nodes[v].color == 'white': 
       self.nodes[v].parent = u 
       self.trees[root].add(v) 
       self.dfs_visit(v, root=root) 
     self.nodes[u].color = 'black' 
     self.time += 1 
     self.nodes[u].f = self.time 

    @staticmethod 
    def transpose(edges): 
     return [(v,u) for (u,v) in edges] 

    def strongly_connected_components(self): 
     self.dfs() 
     finishing_times = {u: node.f for u, node in self.nodes.items()} 
     self.__init__(self.transpose(self.edges)) 
     node_indices = sorted(finishing_times, key=finishing_times.get, reverse=True) 
     self.nodes = self.initialize_nodes(node_indices) 
     self.dfs() 

     trees = copy.deepcopy(self.trees) 
     for k, v in trees.items(): 
      v.add(k) 
     return trees.values() 


@pytest.fixture 
def edges(): 
    with open('SCC.txt') as f: 
     return [tuple(map(int, line.split())) for line in f.read().splitlines()] 

def SCC_on_full_graph(): 
    E = edges() 
    graph = Graph(E) 
    SCCs = graph.strongly_connected_components() 

    SCC_sizes = sorted(list(map(len, SCCs)), reverse=True) 
    print(SCC_sizes[:5])       # Read off the size of the 5 largest SCCs 


if __name__ == "__main__": 
    threading.stack_size(67108864) 
    sys.setrecursionlimit(2**20) 
    thread = threading.Thread(target=SCC_on_full_graph) 
    thread.start() 
1

Das DFS muss logisch DFS sein, aber programmgesteuert können Sie versuchen, ein Problem zu umgehen.

  1. die DFS in einer solchen Art und Weise zu schreiben, dass Sie es von einem der Top-Funktionen wiederholen kann, wenn es sich um eine in der Nähe der Rekursion Grenze erreicht.

  2. Versuchen Sie Multiprocessing zu verwenden.

PS: Ist es möglich, dass eine unendliche Rekursion für die größere Datenmenge auftritt? logischer Fehler, der bei Verwendung eines größeren Datensatzes auftritt. Wenn Sie Datensätze in inkrementellen Größen haben, können Sie auch die Grenze der Implementierung des Algorithmus in Python identifizieren.

Verwandte Themen