20230327

疑似20220723的一篇不知所云的笔记,remake到这po出来看看

Implementing Search Algorithms(DFS BFS&UCS)

In this notebook we will go through implementing depth-first search, breadth-first search, and uniform cost search. We will use the graph given in the last activity.

One way of implementing a graph is to use a python dictionary (https://docs.python.org/3/tutorial/datastructures.html#dictionaries). The dictionary will have nodes as keys, and a list of nodes that each attaches to as values. In the cell below, have a go at writing down the graph given in the last activity.

the value for the key is actually the list which includes all the nodes connected to the key node

graph = {
            'h':['c','a','b']
            # Your code here
}

Let's think about how we implement depth-first search. This code runs - you don't need to add anything to it. Let's talk through it and see if we can understand all the parts.

def dfs(graph, start, goal):
    # We set up lists to store the explored nodes 
    # and the nodes in the frontier. At the start we have 
    # no explored nodes, and the only node in the frontier 
    # is the start node
    explored = []
    frontier=[start] 

    # We set up a loop which runs while there are still nodes
    # to explore
    while frontier:
        current = frontier.pop() #what does this do? Try looking it up
        print(f'current: {current}')
        explored.append(current) #what does this do?
        print(f'explored: {explored}')
        # if we have found the goal, we break out of the loop
        if current==goal:
            break
        
        # If we haven't yet found the goal, we will add the neighbours of 
        # our current node to the frontier
        for neighbour in graph[current][::-1]: #does anyone know what this does?
            # We only add the neighbours to the frontier if they are not
            # already explored and if they are not already in the frontier
            if neighbour not in explored and neighbour not in frontier:
                frontier.append(neighbour)
        print(f'frontier: {frontier}')
    # when we break out of the loop, we will print the nodes we have explored in order
    print(explored)
# Try running the code
dfs(graph, 'h', 'g')

It is hard to create a whole available graph so I just copied one from CSDN

graph = {
    'A':['B','C'],
    'B':['A','C','D'],
    'C':['A','B','D','E'],
    'D':['B','C','E','F'],
    'E':['C','D'],
    'F':['D'],
}

Finally I got this

current: A
explored: ['A']
frontier: ['C', 'B']
current: B
explored: ['A', 'B']
frontier: ['C', 'D']
current: D
explored: ['A', 'B', 'D']

If you don't need a node as the goal then this one is OK

def DFS(graph,s):       
    stack = []          
    stack.append(s)      
    seen = set()       
    seen.add(s)
    while (len(stack) > 0):
        vertex = stack.pop()     
        nodes = graph[vertex]     #the list of the key'vertex'
        for w in nodes:           #first judge 'w' is in the nodes linked with the last node or not
            if w not in seen:     #then judge 'w' has been seen or not
                stack.append(w) 
                seen.add(w)
        print(vertex)             #print this node and search 'w' in the next loop
DFS(graph,'A')
#A
#C
#E
#D
#F
#B

Breadth-first search has a very similar structure to depth-first search. We simply have to change the order in which we explore the nodes. Let's try to put together some code to implement breadth-first search.

def bfs(graph, start, goal):
    # We set up lists to store the explored nodes 
    # and the nodes in the frontier. At the start we have 
    # no explored nodes, and the only node in the frontier 
    # is the start node

    # Set up lists for the explored nodes and the frontier nodes
    
    # Your code here

    # We set up a loop which runs while there are still nodes
    # to explore
    while frontier:
        # Take a node from the frontier. Which node should you take?

        # Your code here 

        print(f'current: {current}')
        # Append this node to the list of explored nodes

        # Your code here  

        print(f'explored: {explored}')
        # If the current node is the goal node, break out of the loop

        # Your code here 

        # If we haven't yet found the goal, we will add the neighbours of 
        # our current node to the frontier. 
        # NB: this time we do not need to reverse the order of the nodes

        # Your code here

        print(f'frontier: {frontier}')
    # when we break out of the loop, we will print the nodes we have explored in order
    print(explored)
# Try running your code. Does it work? if not, can you work out how to solve the errors?
bfs(graph, 'h', 'g')

If use the graph created in DFS

graph = {
    'A':['B','C'],
    'B':['A','C','D'],
    'C':['A','B','D','E'],
    'D':['B','C','E','F'],
    'E':['C','D'],
    'F':['D'],
}
def BFS(graph,s):       #graph表示图表关系,s表示开始的节点
    queue = []          #新建队列
    queue.append(s)       #将初始节点加入到队列中
    seen = set()       #建立一个集合,后续判断是否重复
    seen.add(s)
    while (len(queue) > 0):
        vertex = queue.pop(0)     #移出队列的第一个元素
        nodes = graph[vertex]     #找到那个元素的相关节点并保存起来
        for w in nodes:
            if w not in seen:      #如果节点不重复,就添加到队列中
                queue.append(w)
                seen.add(w)
        print(vertex)
BFS(graph,'A')
#A
#B
#C
#D
#E
#F

The only difference between them is vertex = queue.pop(0) inBFSand ***vertex = stack.pop() ***in DFS

In uniform cost search, we always expand the path that has the lowest cost. Can we do this with the graph representation we currently have?

No - we have not attached any costs to the edges in the graph. Your first task for implementing USC is to change the graph representation so that we have costs attached to the edges. Any suggestions on how to do this?

# We need to add costs to each edge.
ucs_graph = {

}

Implementing UCS

UCS has similarities to breadth-first search, but we need to weight the nodes in the frontier with the cost of getting to those nodes.

def ucs(graph, start, goal):
    # We will again set up lists for the explored and frontier nodes. 
    # In the frontier, we need to give the cost of getting to that node.
    # The cost of getting to the start node from the start node is 0

    # Your code here

    while frontier:
        # We are implementing a variant of BFS, so how should we pick the node to expand?
        
        # Your code here

        print(f'current: {current}')
        # We again add the current node to the explored list
        
        # Your code here

        print(f'explored: {explored}')
        # We need to check whether the current node is actually the goal
        
        # Your code here
        
        # We look at the neighbours of the current node.
        # If it's not explored or in the frontier, we add it to the frontier.
        # When we add it to the frontier, we must also add the cost of getting there from the start node.
        # How should we calculate that?
        for neighbour in graph[current[0]]:
            # we use a little trick to get the first element of each tuple in the frontier
            if neighbour not in explored and neighbour not in dict(frontier).keys():
                # To get the cost of getting to the neighbouring node, note that we 
                # already have the cost of getting to the current node, so we just have to add 
                # on how much it takes to get to the neightbouring node from the current node
                
                # Your code here

                # We need to sort the frontier by the cost of getting to each node
                # We can also sort alphabetically to break any ties
                frontier.sort(key=lambda x:(x[1], x[0]))
        print(f'frontier: {frontier}')
    print(explored)
posted @ 2023-03-27 16:09  noobwei  阅读(32)  评论(0)    收藏  举报