状态空间搜索或者空间搜索或者迷宫问题总结(递归法、回溯法)Python(递归一般用栈,回溯是循环)

状态空间搜索问题或者称迷宫问题的基本特征是:
1、存在一集合可能状态(位置、情况等。这一集合可能很大)。例:迷宫问题中的可能位置。
2、有一个初始状态S0,一个或者多个结束状态,或者有判断成功结束的方法。例子:迷宫问题中的入口表示初始状态,出口表示结束状态
3、对每一个状态s,有neighbors表示与s相邻的一组状态(一步可达的状态)。例如:迷宫中每个位置的四个相邻位置。
4、有一个判断函数valid判断s是否是可行状态,前面迷宫算法中的函数passable实现这种判断。
5、问题:找出从s0出发到达某个(或全部)结束状态的路径;或者从s0出发,设法找到一个或者全部解(即,一个或者全部结束状态。)
这种问题,可以用递归方法求解,通过函数递归的方式向前探查,通过函数返回的方式回溯。也可以用一个栈保存中间信息,采用非递归方式通过回溯法求解。
需要通过状态空间搜索解决的额问题很多,经典的简单实例如:
I、八皇后问题(在国际象棋棋盘上为八个皇后安排的位置,使之不能相互攻击)
II、骑士周游问题(在国际棋盘上为骑士找到一条路径,使之可以经过棋盘的每个格子恰好一次,骑士的走法和中国象棋中的马一样,走“日”字)。
许多实际问题需要通过空间搜索的方法解决,例如,许多的调度、规划、优化问题(背包问题),数学定理证明(基于一些事实和推理规则)等等。

对于这样的问题,我们采用一个迷宫问题来简单介绍一下,
其中有两个比较重要的是
1、如何表示“东南西北”或者“上下左右”四个方向,
在这里插入图片描述
为能方便计算相邻的位置,这里定义一个二元组(二元序对)的表,其元素是从位置(i,j)得到其四相邻位置应该加的数对:
dirs=[(0,1),(1,0),(0,-1),(-1,0)]
对于任何一个位置(i,j),给他加上dirs[0],dirs[1],dirs[2],dirs[3],就分别得到该位置东南西北四个位置
2、如何记录他已经走过的路径
为了是算法方便描述,我们要提前定义两个函数:

首先是递归解法:
算法:
1、用Mark函数标记当前位置
2、检查当前位置是否为出口,如果是则成功结束
3、逐个检查当前位置的四邻是否可以到达出口(递归调用前身)
4、如果对四邻的探索都失败,报告失败

# coding=gbk
dirs=[(0,1),(1,0),(0,-1),(-1,0)]
'''给迷宫maze的位置标2表示“到过了”'''
def make(pos,maze):
	maze[pos[0]][pos[1]]=2
'''检测maze位置是否可行'''
def passable(maze,pos):
	return maze[pos[0]][pos[1]] == 0
	
'''核心算法'''
def find_path(maze,pos,end):
	make(pos,maze)
	if pos==end:
		print(pos,end=" ")
		return True
	for i in range(4):
		nextp=pos[0]+dirs[i][0],pos[1]+dirs[i][1]
		'''考虑下一个可能的方向'''
		if passable(maze,pos):
			if find_path(maze,nextp,end):
				print(pos,end=" ")
				return True
	return False

栈与回溯法
回溯法:
前进:
条件:当前位置存在尚未探查的四邻位置
操作:选定下一个位置并向前探查。如果还存在其他可能未探查的分支,就记录相关信息,以便将来使用。
如果找到出口,则成功结束
后退:
条件:遇到思路,不存在尚未探查的四邻位置
操作:退回最近记录的那个分支点,检查那里是否还存在未探查分支。如果有,就取一个未探查林位置作为当前位置并前进,没有就将其删除并继续回溯。
已穷尽所有可能:
不能找到出口时以失败结束

#coding=gbk
dirs=[(0,1),(1,0),(0,-1),(-1,0)]
'''给迷宫maze的位置标2表示“到过了”'''
def mark(pos,maze):
	maze[pos[0]][pos[1]]=2
'''检测maze位置是否可行'''
def passable(maze,pos):
	return maze[pos[0]][pos[1]] == 0
	
def maze_slover(maze,start,end):
	if start==end:
		print(start)
		return
	st = []
	mark(start, 0)
	st.append((start,0))
	while not st.empty():
		pos,nxt = st.pop()
		for i in range(nxt,4):
			nextp = (pos[0]+dirs[i][0], pos[1]+dirs[i][1])
			
			if nextp==end:
				print(end,pos,st)
			if passable(maze,nextp):
				st.append((pos,i+1))
				mark(nextp,maze)
				st.apppend((nextp,0))
				break
	print("No path found.")

这个算法有几个问题需要申明:
1、算法发现一个新位置后,总是先标记它,然后再将其压入栈。这个做法保证了栈里保存的都是做过标记的位置,也保证了任何位置不会被压入两次。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值