概览
一个寻路问题主要包括以下几个属性
- state space 状态空间
- successor function 后继函数
- start state 起始状态
- goal test 目标检测函数
- step cost
状态并不一定指位置,也可能是位置与其他性质的一个列表。
在寻路问题中,Agent处于可预测的、信息完全的环境中。将每个状态看成一个节点,可以将寻路问题抽象成图的搜索问题。
搜索算法
- 搜索算法通常基于优先队列
priority_queue(每次取出成本最低的状态)- priority_func 权重函数:用于优先队列的排序
- Uniform Cost Search (UCS):基于$g(n)$:从起点沿最优路径到达节点$n$的成本1
- Greedy search:基于$h(n)$:从节点$n$到达终点的预估成本(heuristic 启发式函数)
- A* search:基于$f(n)=g(n)+h(n)$
- priority_func 权重函数:用于优先队列的排序
- close_set:记录已访问过的状态
- parent_map:记录每个状态的前驱状态和cost
过程
- 从队列中取出状态
- 检查是否在
close_set中(若不在则添加进去) - goal test 如果达到目标状态,进行路径回溯并返回
- 利用
successor_func求出当前状态的后继状态列表 - 遍历后继状态列表,更新
parent_map和priority_queue重复以上过程直至priority_queue为空(说明不存在路径),返回空路径
算法实现
void search()(
std::map<State, std::pair<int, State>> parent_map;
std::set<State> close_set;
/*优先队列
@param:compare_func为比较函数.
可以直接使用std::greater(C++的std::pair按照元素从左到右的顺序依次比较)
*/
std::priority_queue<std::pair<int, State>, std::vector<std::pair<int, State>>, compare_func> visit_queue;
visit_queue.push({0, start_state}); // 初始状态,代价为0
while (!visit_queue.empty()) {
auto [current_cost, current_state] = visit_queue.top();
visit_queue.pop();
// 如果已经访问过,跳过
if (close_set.contains(current_state)) continue;
close_set.insert(current_state); // 标记为已访问
// Goal found:回溯路径
if (current == goal_state) {
std::vector<State> path;
path.push_back(current_state);
while (parent.contains(current_state)) {
current = parent[current_state].second;
path.push_back(current);
}
return {path.rbegin(), path.rend()}; // 反转路径
}
auto next = successor_func(current_state);
for (auto [stepcost, successor_state] : next) {
if (close_set.contains(successor_state)) continue;
int cost = current_cost + stepcost;
if(!parent.contains(successor_state) || cost < parent[successor_state].first){
parent[state] = {cost, current_state}; // 记录当前状态的父状态
//UCS
visit_queue.push({cost, successor_state});
//A*
visit_queue.push({cost + heuristic(current_state), state});
}
}
}
return {}; // 如果没有找到路径,返回空向量
}
def search():
close_set = set()
state_queue = []
state_queue.heappush((0, start_state))
parent_map = {start_state: (0, None)}
result = []
while state_queue:
(current_cost,current_state) = state_queue.heappop()
if current_state in close_set:continue
close_set.add(current_state)
if current_state == goal_state:
state = current_state
while parent_map[state][1]:
result.insert(0, parent_map[state][1])
state = parent_map[state][0]
return result
for (successor_state, stepCost) in successor_func(current_state):
if successor_state in close_set: continue
cost = current_cost + step_cost
if successor_state not in parent_map or cost<parent_map[successor_state][0]:
parent_map[successor_state] = (cost, current_state)
state_queue.push((cost, successor_state)) # UCS
state_queue.push((cost + heuristic(successor_state), successor_state)) # A*
return list()
-
由于我们事先并不知道最优路径,因此每次找到一个节点时都需要关注对应的cost,通过比较替换找到g(n) ↩