基于python中networkx包的传教士和野人深度优先搜索算法及可视化实现
按《人工智能原理》书上的思路进行建模。只是记录一下自己的作业,感觉之前好多用心写的代码没利用起来,发在博客整理一下,不喜勿喷。
在这个问题中,(3,3,1)为初始状态,(0,0,0) 为目标状态。全部的可能状态共有32个,如表所示, m为左岸传教士人数, c为左岸野人人数,b 左岸的船数目。

可用状态16种,考虑到任何一条从S0到达S31的路径都是该问题的解,最短路径共有4条。

下面是算法实现的思路:根据条件:右岸->左岸之间可去1人,右岸->左岸可去2人,同时两个可逆,遍历出符合条件的状态空间,并对这些状态空间进行图的建立,事实上比起上题中列举所有可能情景按条件删除,以上这个条件进行点的列举不会考虑绿色, 蓝色中提到的结点,当c和m的值进行改变时,具有更高的普适性。在图建立后用DFS深度优先遍历搜索算法进行搜索遍历,即最优解法。当探索最短路径时,不需要确认已经重复的节点,若确认已经重复的节点,探索的路径就不是最短路径。关于深度优先遍历搜索算法来自https://blog.csdn.net/zhangphil/article/details/121269870。下面是实验结果:

图5 m=3,c=3,b=2实验结果

图6 m=7,c=6,b=2实验结果
从图5上可发现除找出最优路径之外,还有3种同等的最优路径,图 6主要是修改m和c的实验结果拓展,某些情况无最短路径生成,已经加以限制。由于b修改会导致先前设定的条件(右岸->左岸之间可去1人,右岸->左岸可去2人,同时两个可逆)改变,这种情况加入判断语句进行控制即可,暂时没有进一步完善。
下面是代码~可以在这里下载https://github.com/DobbySquirrel/The-missionaries-and-cannibals-problem
本来想传在github上的,好慢。。。。就先写在这里啦~
# -*- coding: utf-8 -*- """ Created on Sat Mar 12 18:54:11 2022 @author: dobby """ import networkx as nx import matplotlib.pyplot as plt WALKABLE = 'walkable' PARENT = 'parent' VISITED = 'visited' #3维网格图 def my_graph(m,c,b): plt.subplots(1,1,figsize=(15,7)) G=nx.Graph() #若b发生变化则以下结点的生成规则需要更改 G.add_edges_from([ ((x,y,0), (x,y+1,1)) for x in range(m+1) for y in range(c) ] + [ ((x,y,0), (x+1,y,1)) for x in range(m) for y in range(c+1) ])#右岸->左岸之间可去1人 G.add_edges_from([ ((x,y,0), (x+1,y+1,1)) for x in range(m) for y in range(c) ] + [ ((x,y,0), (x+2,y,1)) for x in range(m-1) for y in range(c+1) ] + [ ((x,y,0), (x,y+2,1)) for x in range(m+1) for y in range(c-1) ])#右岸->左岸可去2人 pos = nx.spring_layout(G, iterations=100) nx.draw_networkx(G, pos=pos, font_size=7, font_color='white', node_size=500, width=1) START = (m,c,1) GOAL = (0,0,0) road_closed_nodes = dummy_nodes(G,m,c,b) nx.draw_networkx_nodes( G, pos, nodelist=road_closed_nodes, node_size=500, node_color="red", node_shape="x", label='x' ) dfs(G, START, GOAL)# 基于栈实深度优先遍历搜索 try: path = find_path_by_parent(G, START, GOAL) except: print("该种情况没有可行路径") return; print('path', path) nx.draw_networkx_nodes( G, pos, nodelist=path, node_size=400, node_color="green", node_shape='o', ) path_edges = [] for i in range(len(path)): if (i + 1) == len(path): break path_edges.append((path[i], path[i + 1])) print('path_edges', path_edges) G2=G.to_directed() nx.draw_networkx_edges(G2, pos, edgelist=path_edges, width=4, alpha=0.5, edge_color="g") plt.axis('off') plt.show() # 基于栈的深度优先遍历搜索 def dfs(G, START, GOAL): for n in G.nodes(): G.nodes[n]['visited'] = False#初始所有点都是未被访问的 stack = [] # 用列表当作一个栈,只在栈顶操作(数组的第1个位置) stack.append(START) close_list = [] while True: if len(stack) == 0: break print('-----') print('stack-', stack) visit_node = stack[0] G.nodes[visit_node]['visited'] = True print('访问', visit_node) if visit_node == GOAL: break close_list.append(visit_node)#已访问的节点 count = 0 neighbors = nx.neighbors(G, visit_node)#寻找visit_node周围的结点 for node in neighbors:#依次遍历visit_node周围的节点 visited = G.nodes[node][VISITED] try: walkable = G.nodes[node][WALKABLE]#如果存在之前设定的WALKABLE则为阻碍点 except: walkable = True if (visited) or (node in stack) or (node in close_list) or (not walkable): continue #如果结点被访问,或者在将要访问的stack中,或者在已被记录的最短路径close_list中,或者是阻碍点 则跳过 G.nodes[node][PARENT] = visit_node#设定该找到的节点node的父节点为visit_node,改变G stack.append(node)#将node压入stack count = count + 1#计数 if count == 0:#该节点下没有节点被压入 print(visit_node, '尽头') del (stack[0]) print('弹出', visit_node) print('stack--', stack)#显示进入下次循环的stack return stack def find_path_by_parent(G, START, GOAL): t = GOAL path = [t] is_find = False while not is_find: for n in G.nodes(data=True):#G.nodes第一个是结点的数据,第二个是字典存储的结点属性 if n[0] == t: parent = n[1][PARENT]#找Node中parent属性存储的信息 path.append(parent) if parent == START:#找到开始结点 is_find = True break t = parent path.reverse()#从GOAL->START需要翻转 return path def dummy_nodes(G,m,c,b): list1=[]; #list1.append((m,c,0))#去右岸时,船未携带人 不需要考虑本不在生成点中 #list1.append((0,0,1))#左岸无人却有船 不需要考虑本不在生成点中 for i in range(m+1): for j in range(c+1): if(i<j and i!=0):#左岸野人会袭击传教士的情况 p=(i,j,0) q=(i,j,1) list1.append(p) list1.append(q) if(m-i<c-j and m-i!=0):#右岸野人会袭击传教士的情况 p=(i,j,0) q=(i,j,1) list1.append(p) list1.append(q) for i in list1: G.nodes[i][WALKABLE] = False return list1 if __name__ == '__main__': m=3#自定义修道士数 c=3#自定义野人数 b=2 my_graph(m,c,b)

浙公网安备 33010602011771号