深度受限搜索(DLS)简单地说就是深度有限搜索(DFS)+深度限制(limit)
DLS伪代码
实例:狼羊 过河 问题
3只羊和3头狼在河岸A,想要过河抵达河岸B。它们只有一艘船并且船上必须有1-2只生物。当
任意一边的狼的数量大于羊时,羊会被吃光(fail)。初始状态为(3,3,1,0,0,0),意为3头羊
,3头狼和一艘船在河岸A,而河岸B没有羊,没有狼,也没有船。算法程序要达成的目标是(
0,0,0,3,3,1),意为3头羊,3头狼和一艘船都从河岸A抵达了河岸B。请用深度受限搜索(
Depth-Limited-Search)完成这份python程序,受限深度为15。
思路:
action是所有的操作方法,递归的时候在每个节点都要判断从它延伸的其他以下节点是否符合所需要的操作。然后再加上递归的深度限制,就可以解决。
代码1:
# -*- coding: utf-8 -*-
# Python版本:3.6
# 已探索集合
_explored = []
action = [[0, -1, 0], [0, -2, 0], [-1, 0, 0], [-2, 0, 0], [-1, -1, 0], [0, 1, 1], [0, 2, 1], [1, 0, 1], [2, 0, 1], [1, 1, 1]]
# 节点数据结构
class Node:
def __init__(self, state, parent, action):
self.state = state
self.parent = parent
self.action = action
def main():
global _explored
src_state=[3,3,1,0,0,0]
dst_state=[0,0,0,3,3,1]
limit=15
result = depth_limited_search(src_state, dst_state, limit)
if result == "failure" or result == "cutoff":
print('from src_state: %s to dst_state %s search failure' % (src_state, dst_state))
else:
print('from src_state: %s to dst_state %s search success' % (src_state, dst_state))
path = []
while True:
path.append(result.state)
if result.parent is None:
break
result = result.parent
size = len(path)
for i in range(size):
if i < size - 1:
print('%s->' % path.pop(), end='')
else:
print(path.pop())
def depth_limited_search(src_state, dst_state, limit):
global _explored
_explored = []
node = Node(src_state, None, None)
return recursive_dls(node, dst_state, limit)
def recursive_dls(node, dst_state, limit):
"""
:param node:
:param dst_state:
:param limit:
:return: "failure":失败."cutoff":被截至.node:成功
"""
global _explored
if node.parent is not None:
print('node state:%s parent state:%s' % (node.state, node.parent.state))
else:
print('node state:%s parent state:%s' % (node.state, None))
_explored.append(node.state)
# 目标测试
if node.state == dst_state:
print('this node is goal!')
return node
elif limit == 0:
print('this node is cutoff!')
return "cutoff"
else:
cutoff_occurred = False
# 遍历子节点
for state in action:
if node.state[5] == state[2] and (node.state[1] + state[1]) >= 0 and (
node.state[1] + state[1]) <= 3 \
and (node.state[0] + state[0]) >= 0 and (node.state[0] + state[0]) <= 3:
left_sheep = node.state[0] + state[0]
left_wolf = node.state[1] + state[1]
right_sheep = node.state[3] - state[0]
right_wolf = node.state[4] - state[1]
next_state = list(node.state)
if (left_sheep == 0 or (left_sheep > 0 and left_sheep >= left_wolf)) and \
(right_sheep == 0 or (right_sheep > 0 and right_sheep >= right_wolf)):
next_state[0] = left_sheep
next_state[1] = left_wolf
next_state[2] = int(node.state[5])
next_state[3] = right_sheep
next_state[4] = right_wolf
next_state[5] = int(not (node.state[5]))
child = Node(next_state, node, 'do')
# 过滤已探索的点
if child.state in _explored:
continue
print('child node:state:%s ' % (child.state ))
# 递归的方法,把当前child返回,并且limit-1
result = recursive_dls(child, dst_state, limit - 1)
if result == "cutoff":
cutoff_occurred = True
print('search failure, child state: %s parent state: %s limit cutoff' %
(child.state, child.parent.state))
elif result != "failure":
print('search success')
print("At the currnet" ,node.state,"you will take the action",state,"and then,your new state is ",child.state)
return result
if cutoff_occurred:
return "cutoff"
else:
return "failure"
if __name__ == '__main__':
main()
拓展:输出所有的路径
代码2:
# -*- coding: utf-8 -*-
from collections import deque
# 羊 狼 船 羊 狼 船
initial_state = [3, 3, 1, 0, 0, 0] # 初始状态
final_state = [0, 0, 0,3, 3, 1] # 最终状态
action = [[0, -1, 0], [0, -2, 0], [-1, 0, 0], [-2, 0, 0], [-1, -1, 0], [0, 1, 1], [0, 2, 1], [1, 0, 1], [2, 0, 1],
[1, 1, 1]]
# 利用python的deque队列记录状态转移情况,初始化时加入初始状态。deque是可以从头尾插入和删除的队列,在不指定大小时,为一个无边界的队列
record = deque()
record.append(initial_state)
def next_state_lawful(current_state):
# 下一个动作判定
next_action = []
for state in action:
#current_state[5] == state[2]是判断右侧的船和action的船,相同就可以用这个action
#后面的判断狼和羊经过action的操作之后是否是大于0 小于3的
if current_state[5] == state[2] and (current_state[1] + state[1]) >= 0 and (current_state[1] + state[1]) <= 3 \
and (current_state[0] + state[0]) >= 0 and (current_state[0] + state[0]) <= 3:
next_action.append(state)
# 下一个状态
for state in next_action:
left_sheep = current_state[0] + state[0]
left_wolf = current_state[1] + state[1]
right_sheep = current_state[3] - state[0]
right_wolf = current_state[4] - state[1]
next_state = list(current_state)
if (left_sheep == 0 or (left_sheep > 0 and left_sheep >= left_wolf)) and \
(right_sheep == 0 or (right_sheep > 0 and right_sheep >= right_wolf)):
next_state[0] = left_sheep
next_state[1] = left_wolf
next_state[2] = int(current_state[5])
next_state[3] = right_sheep
next_state[4] = right_wolf
next_state[5] = int(not (current_state[5]))
yield next_state
# 记录调试的变量:num表示总共实现方法数,record_list记录所有实现路径
num = 0
record_list = []
def searchResult(record,limit):
if limit==0:
return
global num, record_list
# 由record的末尾元素得到当前状态
current_state = record[-1]
# 得到关于当前状态的下一状态的可迭代生成器,供下一步循环使用
next_state = next_state_lawful(current_state)
# 遍历所有可能的下一状态
for state in next_state:
if state not in record:
# 保证当前状态没在以前出现过。如果状态已经出现还进行搜索就会形成状态环路,陷入死循环。
record.append(state)
# 添加新的状态到列表中
if state == final_state:
print(record)
# 打印出可行方案
# record_list.append(record)这样使用错误,导致加入列表的是record的引用,应该使用下面的式子来进行深复制,得到一个新的队列再加入列表。
record_list.append(deque(record))
num += 1
else:
# 递归搜索
searchResult(record,limit-1)
# 去除当前循环中添加的状态,进入下一个循环,关键步,第一次实现的时候遗漏了
record.pop()
searchResult(record,15)
print("总共实现方式的种类数目:", num)
print("实现方式的最少步骤为:%d 步" % (min([len(i) for i in record_list])-1))