最佳优先搜索best-find search

目录

1. 问题

2. 算法

3.代码


1. 问题

考虑下面这个问题:

 我们要找到从Arad到Bucharest的路,最好是最短的路:

2. 算法

这是一个无向有环图,

可以采用最佳优先搜索:

最佳优先搜索的算法可以参考维基百科

伪代码如下:

// Pseudocode for Best First Search
Best-First-Search(Graph g, Node start)
    1) Create an empty PriorityQueue
       PriorityQueue pq;
    2) Insert "start" in pq.
       pq.insert(start)
    3) Until PriorityQueue is empty
          u = PriorityQueue.DeleteMin
          If u is the goal
             Exit
          Else
             Foreach neighbor v of u
                If v "Unvisited"
                    Mark v "Visited"                    
                    pq.insert(v)
             Mark u "Examined"                    
End procedure

如果你熟悉广度优先搜索,那么上面的代码也没什么难度。

该算法最关键的点是如何构造优先队列中的“优先”策略,一种简单的办法就是选取当前所有访问点中最近的相邻点:

procedure GBS(start, target) is:
  mark start as visited
  add start to queue
  while queue is not empty do:
    current_node ← vertex of queue with min distance to target
    remove current_node from queue
    foreach neighbor n of current_node do:
      if n not in visited then:
        if n is target:
          return n
        else:
          mark n as visited
          add n to queue
  return failure

显然上面的BFS是一种贪婪算法,每次选取最优的近邻点是一种“短视”,虽然如此,他仍是一种可行的算法。

该算法最坏的复杂度是 O(n*logn) ,其中n是节点的总数。在最坏的情况我们需要访问所有节点,然后我们构造的堆,每次添加删除元素时复杂度为:O(logn)   

算法的性能显然与我们选择什么样的代价函数有直接关系。

3.代码

首先是节点类

# -*- coding: utf-8 -*-
"""
Created on Wed Sep  4 15:56:35 2024

@author: Paul
"""

class Node:
    def __init__(self,name):
        self._name=name
        self._children=[]
        
    def __repr__(self):
        return 'Node({!r})'.format(self._name)
    
    def add_child(self,node):
        self._children.append(node)
        
    def __iter__(self):
        return iter(self._children)
    
    def __eq__(self, other):
        return self._name==other._name
    
    def Childrens(self):
        return self._children()

优先队列:

# -*- coding: utf-8 -*-
"""
Created on Wed Sep  4 16:03:33 2024

@author: Paul
"""

import heapq

class PriorityQueue:
    def __init__(self):
        self._queue=[]
        self._index=0
        
    def push(self,item,priority):
        heapq.heappush(self._queue, (priority,self._index,item))
        self._index+=1
        
    def pop(self):
        return heapq.heappop(self._queue)[-1]   #弹出item
    
    def empty(self):              #判断是否为空
        return True if not self._queue else False

数据节点:

这里只选取到BuCharest城市的数据,该城市是访问Urziceni和Giurgiu后续的点必经之路。

这些城市:

cities=['Arad','Zerind','Oradea','Sibiu','Fagaras','Bucharest',
        'Pitesti','Rimnicu','Craiova','Drobeta','Mehadia','Lugoj',
        'Timisoara']

cities=['Arad','Zerind','Oradea','Sibiu','Fagaras','Bucharest',
        'Pitesti','Rimnicu','Craiova','Drobeta','Mehadia','Lugoj',
        'Timisoara']

Nodes=[Node(name) for name in cities]     #12个节点
dist_mat=[[] for i in range(len(cities))]

def addedge(start,end,dist):
    x=cities.index(start)
    y=cities.index(end)
    dist_mat[x].append((y,dist))
    dist_mat[y].append((x,dist))

addedge('Arad', 'Zerind', 75)
addedge('Arad', 'Sibiu', 140)
addedge('Arad', 'Timisoara', 118)
addedge('Zerind','Oradea',71)
addedge('Oradea', 'Sibiu', 151)
addedge('Sibiu', 'Fagaras', 99)
addedge('Sibiu', 'Rimnicu',80)
addedge('Fagaras', 'Bucharest', 211)
addedge('Bucharest', 'Pitesti', 101)
addedge('Pitesti', 'Rimnicu', 97)
addedge('Pitesti', 'Craiova', 138)
addedge('Craiova', 'Rimnicu', 146)
addedge('Craiova', 'Drobeta', 120)
addedge('Drobeta', 'Mehadia', 75)
addedge('Mehadia', 'Lugoj', 70)
addedge('Lugoj', 'Timisoara', 111)


for index,city in enumerate(Nodes):
    for t in dist_mat[index]:
        city.add_child(Nodes[t[0]])

每个节点与其相邻节点关系如下:

Node('Arad') =>[Node('Zerind'), Node('Sibiu'), Node('Timisoara')]
Node('Zerind') =>[Node('Arad'), Node('Oradea')]
Node('Oradea') =>[Node('Zerind'), Node('Sibiu')]
Node('Sibiu') =>[Node('Arad'), Node('Oradea'), Node('Fagaras'), Node('Rimnicu')]
Node('Fagaras') =>[Node('Sibiu'), Node('Bucharest')]
Node('Bucharest') =>[Node('Fagaras'), Node('Pitesti')]
Node('Pitesti') =>[Node('Bucharest'), Node('Rimnicu'), Node('Craiova')]
Node('Rimnicu') =>[Node('Sibiu'), Node('Pitesti'), Node('Craiova')]
Node('Craiova') =>[Node('Pitesti'), Node('Rimnicu'), Node('Drobeta')]
Node('Drobeta') =>[Node('Craiova'), Node('Mehadia')]
Node('Mehadia') =>[Node('Drobeta'), Node('Lugoj')]
Node('Lugoj') =>[Node('Mehadia'), Node('Timisoara')]
Node('Timisoara') =>[Node('Arad'), Node('Lugoj')]

最后BFS如下:

# -*- coding: utf-8 -*-
"""
Created on Wed Sep  4 16:37:37 2024

@author: Paul
"""

from node import Node
from priorityqueue import PriorityQueue

def BFS(mat,start,dest):
    q=PriorityQueue()
    q.push(start, 0)
    visited=[start]
    while not q.empty():
        u=q.pop()
        print(u)
        if u==dest:
            return True
        else:
            for city,dist in mat[Nodes.index(u)]:
                child=Nodes[city]
                if child not in visited:
                    visited.append(child)
                    q.push(child, dist)
                    
    return False

运行BFS结果如下:

Node('Arad')
Node('Zerind')
Node('Oradea')
Node('Timisoara')
Node('Lugoj')
Node('Mehadia')
Node('Drobeta')
Node('Craiova')
Node('Pitesti')
Node('Bucharest')

访问路径如下:

     算法每次选取最近的节点,因此我们没有得到最优解,要想得到最优解,需要对算法进行改造,使之称为启发式算法,我会在后续章节介绍一二。

参考链接:

BFS维基百科:Best-first search - Wikipedia

geek for geeks: Best First Search (Informed Search) - GeeksforGeeks

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 贪心最佳优先搜索(Greedy Best-First Search)是一种启发式搜索算法,它通过在每个搜索步骤中优先考虑最有可能导致解决方案的节点,来寻找最优解。与广度优先搜索不同,贪心最佳优先搜索使用一个评估函数来衡量搜索状态的好坏,然后按照评估函数的值来选择下一个要扩展的节点。贪心最佳优先搜索通常用于解决状态空间较大,但具有良好的启发信息的问题。 ### 回答2: 贪婪最优先搜索(GBFS)是一种启发式搜索算法,其基本思想是根据一定的启发函数,尽可能快地向目标状态方向搜索。它的搜索过程类似于最优先搜索,但是它不使用精确的代价函数,而是使用启发函数来估计从当前状态到目标状态的代价。 GBFS通过选择启发函数中具有最小估计代价的节点来扩展搜索树,因此它可能会快速地向目标状态移动。然而,由于它只考虑了估计代价,而没有考虑实际代价,因此它有可能陷入死胡同或者过早的终止搜索。 与最优先搜索类似,GBFS也具有一个开放列表,其中包含了待搜索的节点。通过比较启发函数的值,GBFS选择启发值最小的节点进行扩展。当搜索到达目标状态时,GBFS停止搜索。 与其他搜索算法相比,GBFS具有以下优缺点: 优点: 1. 它可以快速地向目标状态移动。 2. 它在内存和计算资源上比A*搜索更加高效。 缺点: 1. 它可能陷入局部最优解并忽略其他有可能更优的解。 2. 它无法保证找到最优解。 3. 它存在一个问题,即当启发函数不准确时,可能会浪费大量的时间和计算资源搜索不必要的节点。 总的来说,GBFS对于大多数问题而言是一个高效的搜索算法,但是它的表现取决于使用的启发函数的准确性。 ### 回答3: 贪婪最佳优先搜索(Greedy Best-First Search)是一种启发式搜索算法,它选取可行节点中最有希望的节点作为下一个扩展节点。在贪婪最佳优先搜索中,每个节点都被赋予一个估价函数的值,估价函数用于估计从当前节点到目标节点的最小代价或距离,该算法通常用于解决单目标问题,如路径规划或机器人导航。 贪婪最佳优先搜索过程中,节点状态用一个优先队列来维护。节点的优先级由估价函数的值决定,即节点的估价函数值越小,则节点的优先级越高,队列中位置越靠前。这种贪心策略导致该算法的效率很高,但是不能保证它能找到全局最优解,它只能找到靠近目标的局部最优解。 贪婪最佳优先搜索的优点是速度快,但是它有缺陷。该算法只能找到越来越接近终点的节点,但是它不能保证一些没有被考虑的位于路径上的节点,这些节点可能会导致更短或更优的路径。因此,该算法只适用于简单问题,但是在复杂问题上找到最优解的准确性不可靠。 在实际问题中,我们通常使用A*算法代替贪婪最佳优先搜索。A*算法既具备贪婪最佳优先搜索的速度和效率,又能优化贪婪算法的不足之处,它能确保找到最优解,并取得了广泛的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值