求用A*搜索解决狼羊菜过河问题的有效启发式函数
狼、羊、卷心菜过河问题的A*启发式函数设计
核心要求
A*的启发式函数必须满足可采纳性(即不会高估从当前状态到目标状态的最小步数),同时尽可能紧凑,以减少搜索节点数量。
可行的启发式设计
1. 基于物品数量的基础启发式
计算当前未到达目标岸的物品总数记为n,根据农夫位置分两种情况:
- 如果农夫与所有未过河物品在同侧:最少需要
2*(n-1)+1步(每运输1个物品除最后一次外,需往返1次,最后一次无需返回) - 如果农夫在目标岸,未过河物品在初始岸:需先返回初始岸(1步),再完成运输,总步数为
1 + (2*(n-1)+1)
用伪代码表示:
def h(state): left_items, farmer_pos = state # 计算未到达目标岸的物品数 if farmer_pos == "right": # 目标岸是右,未过河物品是左岸剩余的 n = len(left_items) else: # 目标岸是右,未过河物品是未到右岸的(即3 - 左岸数量) n = 3 - len(left_items) if n == 0: return 0 if farmer_pos == "right" and n > 0: # 农夫在对岸,需先返回再运输 return 1 + (2*(n-1) + 1) else: # 农夫与未过河物品同侧,直接运输 return 2*(n-1) + 1
这个启发式完全满足可采纳性,因为它计算的是无约束下的最小运输步数,而实际问题中的约束只会增加步数,不会减少。
2. 考虑危险约束的紧凑启发式
在基础启发式的基础上,加入对“必须带回物品”的判断,进一步缩小估值:
- 若当前状态下,农夫在目标岸,且初始岸存在危险组合(羊+卷心菜,或狼+羊),说明农夫必须返回初始岸带回其中一个物品,此时需在基础估值上额外加2步(往返一次)
- 若当前状态下,农夫在初始岸,目标岸存在危险组合,同理需额外加2步
比如,当状态为「左岸:狼、卷心菜;右岸:羊;农夫在右岸」时,左岸无危险组合,基础估值为4,实际需要6步,估值小于实际步数,符合可采纳性;若状态为「左岸:羊;右岸:狼、卷心菜;农夫在右岸」,此时右岸无危险组合,基础估值为2,实际需要2步(农夫回左带羊到右),估值等于实际步数,非常紧凑。
验证要点
- 所有启发式的估值必须≤实际最小步数,否则A*无法保证找到最优解
- 越紧凑的启发式,搜索时扩展的节点越少,效率越高
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

