Skip to content
Go back

CS188项目记录

Edit page

Project 1: Search

前四问是依次实现 lecture 中讲过的depthFirstSearchbreadthFirstSearch, uniform cost searchA* search。前两个差别只在于实现边界的数据结构一个是栈(先进后出)一个是队列(先进先出)

def depthFirstSearch(problem: SearchProblem):
    fridge = util.Stack()
    fridge.push((problem.getStartState(), []))
    visited = set()
    while not fridge.isEmpty():
        curr_state, curr_path = fridge.pop()
        if problem.isGoalState(curr_state):
            return curr_path
        if curr_state not in visited:
            visited.add(curr_state)
            for next_state, action, cost in problem.getSuccessors(curr_state):
                new_path = curr_path + [action]
                fridge.push((next_state, new_path))
    return []
def breadthFirstSearch(problem: SearchProblem):
    fridge = util.Queue()
    fridge.push((problem.getStartState(), []))
    visited = set()
    while not fridge.isEmpty():
        curr_state, curr_path = fridge.pop()
        if problem.isGoalState(curr_state):
            return curr_path
        if curr_state not in visited:
            visited.add(curr_state)
            for next_state, action, cost in problem.getSuccessors(curr_state):
                new_path = curr_path + [action]
                fridge.push((next_state, new_path))
    return []

uos 是使用优先队列通过比较 priority 来选择谁先 pop 出队列。

def uniformCostSearch(problem: SearchProblem):
    fridge = util.PriorityQueueWithFunction(lambda a:problem.getCostOfActions(a[1]))
    fridge.push((problem.getStartState(), []))
    visited = set()
    while not fridge.isEmpty():
        curr_state, curr_path = fridge.pop()
        if problem.isGoalState(curr_state):
            return curr_path
        if curr_state not in visited:
            visited.add(curr_state)
            for next_state, action, cost in problem.getSuccessors(curr_state):
                new_path = curr_path + [action]
                fridge.push((next_state, new_path))
    return []

a* 则是在 uos 基础上 把 priority 替换为 f = g + h ,即已经走过路径的成本 加上 预估从此处到达目标路径的成本(通过启发式方法求得)。一个 admissible 的启发式方法,求得的 h 应该小于或等于实际成本,保证找到的解一定是最优解。同时,启发式方法还需要满足一致性,意思是 f 沿着路径应该不变或不断增大而不会减小,也就是每一步的预估成本都要小于或等于实际成本,保证第一次扩展某节点时,它的 g 已经最小。

def aStarSearch(problem: SearchProblem, heuristic=nullHeuristic):
    """Search the node that has the lowest combined cost and heuristic first."""
    fridge = util.PriorityQueueWithFunction(lambda a:problem.getCostOfActions(a[1]) + heuristic(a[0],problem))
    fridge.push((problem.getStartState(), []))
    visited = set()
    while not fridge.isEmpty():
        curr_state, curr_path = fridge.pop()
        if problem.isGoalState(curr_state):
            return curr_path
        if curr_state not in visited:
            visited.add(curr_state)
            for next_state, action, cost in problem.getSuccessors(curr_state):
                new_path = curr_path + [action]
                fridge.push((next_state, new_path))
    return []

后面几问主要考查的是目标状态函数、后继函数和启发式方法的编写,加深对 heuristic 的理解。通过不断接近真实成本,可以减少搜索中扩展的节点数量,提高效率。


Edit page