赞
踩
参考资料:
中国大学MOOC的数据结构与算法Python版
骑士周游问题,是算法设计中的经典问题。
其一般的问题描述是:考虑nn大小的国际象棋棋盘上某个位置的一只马,按照马走“日”的规则,它是否可能只走nn-1步,正好走过除起点外的其他n*n-1个位置各一次?如果有一种这样的走法,则称所走的这条路线为一条马的周游路线。试设计一个算法找出这样一条马的周游路线。
采用图的深度优先搜索算法解决。
解决方分为两步:
1、将合法走棋的次序表示为一个图。
2、采用图搜索算法搜寻一个长度为(行*列-1)的路径,路径上包含每个顶点恰一次。
深度优先搜索(Depth First Search,DFS),其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。
深度优先遍历图的方法是,从图中某顶点v出发:
(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
时间复杂度为
O
(
k
n
)
,
n
为
棋
盘
格
数
目
。
O(k^n),n为棋盘格数目。
O(kn),n为棋盘格数目。
例如:
改进:
Warnsdoff算法:也是一种贪婪法,改变了遍历下一个节点的次序,边数少的节点先被搜索,提升了算法的实际性能。
def knightGraph(bdSize):#形成无向图 ktGraph = nx.Graph() for row in range(bdSize): for col in range(bdSize): nodeId = posToNodeId(row,col,bdSize) newPositions = genLegalMoves(row,col,bdSize) for e in newPositions: nid = posToNodeId(e[0],e[1],bdSize) ktGraph.add_edge(nodeId,nid,color='black') return ktGraph def posToNodeId(row,col,bdSize):#节点标签:根据位置定义 return row*bdSize+col def genLegalMoves(x,y,bdSize):#从位置(x,y)出发,可到达的位置 newMoves = [] moveOffsets = [(-1,-2),(-1,2),(-2,-1),(-2,1), ( 1,-2),( 1,2),( 2,-1),( 2,1)] for i in moveOffsets: newX = x + i[0] newY = y + i[1] if legalCoord(newX,bdSize) and legalCoord(newY,bdSize): newMoves.append((newX,newY)) return newMoves def legalCoord(x,bdSize):#是否超出棋盘范围 if x >= 0 and x < bdSize: return True else: return False
def knightTour(n,path,u,limit): kg.node[u]['color']='gray'#已遍历到此节点 #u.setColor('gray') path.append(u)#加入到队列中 if n < limit:#未搜索到一条可行路径 nbrList = list(kg.neighbors(u))#下一个可搜索的节点集合 i = 0 done = False while i < len(nbrList) and not done: if kg.node[nbrList[i]]['color'] == 'white':#节点未经过 done = knightTour(n+1, path, nbrList[i], limit) i = i + 1 if not done: # prepare to backtrack path.pop() kg.node[u]['color']='white'#设置成未搜索 #u.setColor('white') else: done = True return done
def orderByAvail(n):#排序,边少的点优先 resList = [] for v in kg.neighbors(n): if kg.node[v]['color'] == 'white':#计算未走过的节点的所连接边的个数 c = 0#计数 for w in kg.neighbors(v): if kg.node[w]['color'] == 'white': c = c + 1 resList.append((c,v)) resList.sort(key=lambda x: x[0])#按边的个数从小到大排序 return [y[1] for y in resList] def knightTourBetter(n,path,u,limit): #use order by available function kg.node[u]['color']='gray' #u.setColor('gray') path.append(u) if n < limit: nbrList = orderByAvail(u) i = 0 done = False while i < len(nbrList) and not done: if kg.node[nbrList[i]]['color'] == 'white': done = knightTourBetter(n+1, path, nbrList[i], limit) i = i + 1 if not done: # prepare to backtrack path.pop() kg.node[u]['color']='white' else: done = True return done
无向图:
深度优先搜索:
改进算法:
由结果可知:改进后算法运行时间约为原算法的1/3。
import networkx as nx import matplotlib.pyplot as plt import time start1 = time.clock() #print(start) def knightGraph(bdSize):#形成无向图 ktGraph = nx.Graph() for row in range(bdSize): for col in range(bdSize): nodeId = posToNodeId(row,col,bdSize) newPositions = genLegalMoves(row,col,bdSize) for e in newPositions: nid = posToNodeId(e[0],e[1],bdSize) ktGraph.add_edge(nodeId,nid,color='black') return ktGraph def posToNodeId(row,col,bdSize):#节点标签:根据位置定义 return row*bdSize+col def genLegalMoves(x,y,bdSize):#从位置(x,y)出发,可到达的位置 newMoves = [] moveOffsets = [(-1,-2),(-1,2),(-2,-1),(-2,1), ( 1,-2),( 1,2),( 2,-1),( 2,1)] for i in moveOffsets: newX = x + i[0] newY = y + i[1] if legalCoord(newX,bdSize) and legalCoord(newY,bdSize): newMoves.append((newX,newY)) return newMoves def legalCoord(x,bdSize):#是否超出棋盘范围 if x >= 0 and x < bdSize: return True else: return False def knightTour(n,path,u,limit): kg.node[u]['color']='gray'#已遍历到此节点 #u.setColor('gray') path.append(u)#加入到队列中 if n < limit:#未搜索到一条可行路径 nbrList = list(kg.neighbors(u))#下一个可搜索的节点集合 i = 0 done = False while i < len(nbrList) and not done: if kg.node[nbrList[i]]['color'] == 'white':#节点未经过 done = knightTour(n+1, path, nbrList[i], limit) i = i + 1 if not done: # prepare to backtrack path.pop() kg.node[u]['color']='white'#设置成未搜索 #u.setColor('white') else: done = True return done def orderByAvail(n):#排序,边少的点优先 resList = [] for v in kg.neighbors(n): if kg.node[v]['color'] == 'white':#计算未走过的节点的所连接边的个数 c = 0#计数 for w in kg.neighbors(v): if kg.node[w]['color'] == 'white': c = c + 1 resList.append((c,v)) resList.sort(key=lambda x: x[0])#按边的个数从小到大排序 return [y[1] for y in resList] def knightTourBetter(n,path,u,limit): #use order by available function kg.node[u]['color']='gray' #u.setColor('gray') path.append(u) if n < limit: nbrList = orderByAvail(u) i = 0 done = False while i < len(nbrList) and not done: if kg.node[nbrList[i]]['color'] == 'white': done = knightTourBetter(n+1, path, nbrList[i], limit) i = i + 1 if not done: # prepare to backtrack path.pop() kg.node[u]['color']='white' else: done = True return done #n=int(input("棋盘大小:")) n=6 kg = knightGraph(n) #five by five solution pos=[] for i in range(n): for j in range(n): pos.append((i,j)) nx.draw(kg, pos, with_labels=True)#画出无向图 plt.show() thepath = []#路径 for i in range(n*n): kg.node[i]['color'] = 'white' start = 4#开始节点 knightTour(0,thepath,start,n*n-1) print("可行路径为:") for i in range(len(thepath)):#输出路径 if i!=len(thepath)-1: print(thepath[i],end=' ->') kg[thepath[i]][thepath[i+1]]['color'] = 'blue' else: print(thepath[i]) nx.draw(kg,pos,with_labels=True,width=3,edge_color=[(v['color']) for x,y,v in kg.edges(data=True)])# #走过的边为蓝色,没走过为黑色 #nx.draw_networkx(kg,pos,with_labels=True,edge_color='blue') #nx.draw(kg,pos,with_labels=True,width=3,edge_color=(2,255,0)) plt.show() #print(time.clock()) elapsed = (time.clock() - start1) print("Time used:",elapsed)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。