暂停深度优先的递归搜索

问题描述

我有一个基本的递归深度优先搜索程序,可以搜索大树中未探索的路径。
有没有一种方法可以暂停并保存树,以便以后可以继续搜索而不是每次结束程序都重新启动搜索
我正在使用python和一个名为pycharm的python IDE。

解决方法

您必须使用DFS的迭代版本,方法是将堆栈对象保留在内置的递归堆栈中。

然后,我们可以侦听Ctrl+C按钮按下事件,以指示应该暂停DFS。
暂停后,我们可以将dfs状态腌制(序列化)到文件中。
重新启动脚本后,它将检测到该pickle文件的存在并恢复DFS。

import pickle
import os
import multiprocessing
import signal
import time


ctrl_c_pressed_event = multiprocessing.Event()
ctrl_c_pressed_event.clear()

def ctrl_c_handler(*args):
    ctrl_c_pressed_event.set()

signal.signal(signal.SIGINT,ctrl_c_handler)

GRAPH_PICKLE_PATH = 'graph_pickle.pkl'


class DfsState:
    def __init__(self,visited=set(),path=[],dfs_q=[]):
        self.visited = visited
        self.path = path
        self.dfs_q = dfs_q
    def __str__(self):
        return 'visited={}\npath={}\nq={}'.format(self.visited,self.path,self.dfs_q)


class Graph:
    def __init__(self):
        # Graph is stored in a dictionary of <vertex> vs <set of vertices>
        # Each vertex is just an int
        self.neighbours = {}
        self.vertices = set()
        self.dfs_state = DfsState()

    def add_edge(self,src,dst):
        """
        Add an edge from `src` to `dst`.
        """
        self.vertices.add(src)
        self.vertices.add(dst)
        src_neighbours = self.neighbours.get(src,set())
        src_neighbours.add(dst)
        self.neighbours[src] = src_neighbours

    def get_all_edges(self):
        return self.neighbours

    def get_neighbours(self,src):
        return self.neighbours.get(src,set())
    
    def add_vertex(self,src):
        if src not in self.neighbours:
            self.neighbours[src] = set()
        self.vertices.add(src)


def dfs_iterative(graph):
    visited = graph.dfs_state.visited
    path = graph.dfs_state.path
    dfs_q = graph.dfs_state.dfs_q
    while dfs_q:
        if ctrl_c_pressed_event.is_set():
            return 'PAUSED'
        current = dfs_q.pop()
        print(current)
        if current not in visited:
            visited.add(current)
            path.append(current)
            neighbours = graph.get_neighbours(current)
            for neighbour in neighbours:
                if neighbour not in visited:
                    dfs_q.append(neighbour)
            # Just for testing
            time.sleep(1)
    return 'DONE'


def init_graph():
    graph = Graph()
    graph.add_edge(1,2)
    graph.add_edge(1,3)
    graph.add_edge(2,5)
    graph.add_edge(3,2)
    graph.add_edge(3,4)
    graph.add_edge(4,5)
    graph.add_edge(5,6)
    graph.add_edge(5,7)
    graph.add_edge(7,8)
    start_vertex = 1
    graph.dfs_state.dfs_q = [start_vertex]
    return graph


def pickle_graph(graph):
    pickle.dump(graph,open(GRAPH_PICKLE_PATH,'wb'))


def load_pickle_graph():
    return pickle.load(open(GRAPH_PICKLE_PATH,"rb" ))


def main():
    if os.path.exists(GRAPH_PICKLE_PATH):
        graph = load_pickle_graph()
        print('Resuming DFS')
        print(graph.dfs_state)
    else:
        graph = init_graph()
    status = dfs_iterative(graph)
    pickle_graph(graph)
    print('\n' + str(graph.dfs_state))
    if status == 'PAUSED':
        print('DFS paused!')
    else:
        print('DFS complete!')


if __name__ == "__main__":
    main()

输出:

>>> ajaggi-mn1:~/Documents/personal/practice/python$ python a.py
1
3
4
^C
visited=set([1,3,4])
path=[1,4]
q=[2,2,5]
DFS paused!


>>> ajaggi-mn1:~/Documents/personal/practice/python$ python a.py
Resuming DFS
visited=set([1,5]
5
7
8
^C
visited=set([1,4,5,7,8])
path=[1,8]
q=[2,6]
DFS paused!


>>> ajaggi-mn1:~/Documents/personal/practice/python$ python a.py
Resuming DFS
visited=set([1,6]
6
2
2

visited=set([1,6,8,2]
q=[]
DFS complete!


>>> ajaggi-mn1:~/Documents/personal/practice/python$ python a.py
Resuming DFS
visited=set([1,2]
q=[]

visited=set([1,2]
q=[]
DFS complete!