搜索算法
Ø 利用计算机的高性能来有目的的穷举一个问题解空间的部分或者所有的
可能情况,从而求出问题的解的一种计算机算法
启发式搜索算法
Ø 利用启发式信息引导算法搜索,达到减少搜索范围,降低问题复杂度的
目的
Ø 将人类求解问题的经验固化到求解算法
估价函数
基于一般图搜索算法,定义一个评价函数
f
f
f,对当前的搜索状态进行评估,从Open表中找出一个最有希望的节点来扩展。
f的一般定义
f
(
x
)
=
g
(
x
)
+
h
(
x
)
f(x)=g(x)+h(x)
f(x)=g(x)+h(x)
g
(
x
)
g(x)
g(x):从根节点(起始节点)到当前节点的估计路径长,
g
∗
(
x
)
的
估
计
g^*(x)的估计
g∗(x)的估计
h
(
x
)
h(x)
h(x):从当前节点到目标节点的估计路径长,
h
∗
(
x
)
的
估
计
h^*(x)的估计
h∗(x)的估计
g
∗
(
x
)
g^*(x)
g∗(x):从根节点(起始节点)到当前节点的最短路径长
h
∗
(
x
)
h^*(x)
h∗(x):从当前节点到目标节点的最短路径长
爬山法
Ø只考虑与目标之间的差距,即
g
(
n
)
=
0
g(n)=0
g(n)=0
分支界限法
Ø 总是扩展具有最小耗散值的路径
Ø 使用g(n),h(n)=0
** 动态规划法**
Ø 总是扩展具有最小耗散值的路径
Ø 总是删除明显不可能的路径
Ø 使用g(n),h(n)=0
A算法伪代码
1 G=G0 (G0=s), Open := (s), 计算f(s) := g(s) + h(s), Closed=();
2 Loop: If Open = ( ) Then Exit(Fail);
3 n := First(open), Remove(n, Open), Add(n, Closed);
4 If Goal(n) Then Exit(Success);
6 Expand(n) →{mi}, 计算f(n, mi) := g(n, mi) + h(mi), G:=Add(mi, G);
7 标记和修改指针:
Add(mj, Open), 标记mj到n的指针; //新节点
If f(n, mk) < f(mk) Then f(mk) := f(n, mk), //Open表中未扩展节点
标记mk到n的指针;
If f(n, ml)<f(ml,) Then f(ml):=f(n, ml), //Closed表中已扩展节点
标记ml到n的指针, Add(ml, Open);
8 Open中的节点按f值从
A*算法
在A算法中,如果满足条件:
h
(
n
)
≤
h
∗
(
n
)
h(n)≤h^*(n)
h(n)≤h∗(n)
则A算法称为A*算法。
f
∗
(
s
t
a
r
t
)
=
f
∗
(
a
i
m
)
=
h
∗
(
s
t
a
r
t
)
=
g
∗
(
a
i
m
)
=
f
∗
(
n
)
f*(start) = f*(aim) = h*(start) = g*(aim) = f*(n)
f∗(start)=f∗(aim)=h∗(start)=g∗(aim)=f∗(n)
start为起始点 aim为终点,n为最佳路径start到aim的任意节点
A*定理和推论
定理1:对于有限图,A*算法一定终止。
算法每次循环从OPEN表里去掉一个节点,而有限图的OPEN表内只有有限个节点加入。所以无论算法是否找到解都会正常停止。
定理2:若算法不终止,则OPEN表上的点的f值将越来越大。
定理3:A*结束前,Open表中必存在 f ( n ) ≤ f ∗ ( s ) f(n)≤f^*(s) f(n)≤f∗(s)
定理4:若问题有解,算法终止时一定找到最优解,即可达。
定理5:OPEN表上任一具有 f ( n ) ≤ f ∗ ( s ) f(n)≤f^*(s) f(n)≤f∗(s) 的节点n,最终都将被算法选作扩展的节点。
定理6:A*选作扩展的任一节点n,有 f ( n ) ≤ f ∗ ( s ) f(n)≤f^*(s) f(n)≤f∗(s)
定理7:设对同一个问题定义了两个A*算法A1和A2,若A2比A1有较多的启发信息,即对所有非目标节点有 h 2 ( n ) > h 1 ( n ) h_2(n) > h_1(n) h2(n)>h1(n),则在具有一条从s到t的路径的隐含图上,搜索结束时,由A2所扩展的每一个节点,也必定由A1所扩展,即A1扩展的节点数至少和A2一样多。
算法改进
因A算法第6步对 m 1 m_1 m1 类节点可能要重新放回到OPEN表中,因此可能会导致多次重复扩展同一个节点,导致搜索效率下降。
解决的途径:
对h加以限制:对h增加适当的限制,使得第一次扩展一个节点时,就找到了从S到该节点的最短路径。
对算法加以改进:对算法加以改进,避免或减少节点的多次扩展。
改进的条件:
可采纳性不变
不多扩展节点
不增加算法的复杂性
实践
模板代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define maxn 1000
int n,m,s,t;
double x[maxn],y[maxn]; //点的坐标
bool edge[maxn][maxn]; //二维数组存边
int pos[maxn]; //每个节点的前驱结点,记录路径
double length; //最短路径长度
vector<int> path; //最短路径
double gi[maxn],f[maxn]; //gi 耗散值 f评价值
double distance(int A,int B){ //点A与点B的距离
return sqrt((x[A]-x[B])*(x[A]-x[B])+(y[A]-y[B])*(y[A]-y[B]));
}
double A_star(int s,int t); //A_star算法主体过程,pos数组记录每个节点的前驱结点
int main(){
cin>>n>>m>>s>>t;
for(int i=0;i<n;i++){
cin>>x[i]>>y[i]; //保存点的坐标
}
for(int i=0;i<m;i++){
int A,B;
cin>>A>>B; //保存边
edge[A][B]=edge[B][A]=true;
}
memset(f,-1,sizeof(f));
length=A_star(s,t); //算法主体
int tmp=t;
while(tmp!=s){
path.push_back(tmp);
tmp=pos[tmp];
}path.push_back(tmp); //路径记录
cout<<length<<endl; //输出答案
for(int i=path.size()-1;i>0;i--) cout<<path[i]<<" ";
cout<<path[0]<<endl;
return 0;
}
/* 你的代码将会被嵌入在这个部分 */
思路:
先写出评估函数f(n)
在本题中不难发现理想的g(x)就是从起点到x点的最佳路径(这时候就需要图算法来进行g(x)的迭代),而h(x)根据下界的定义,就是两点之间的直线距离
利用priority_queue作为储存的容器,这样我们可以对储存的点以f[s]进行上升排序,从而减少排序时间的损耗
后续将起始点s的f(s)和s存入pq中,进行后续的
while循环
//需要改变的量
从open中pop f(p)最小的点
判断是不是目标点t
是返回 路径(t)
遍历临界点 v
得到从起始点到p再到v的g(x)
若小于现有的g(x)
进行g(x)更新,并得到f(x)存放进入open表中
//此时p相当于已经从open表中放到close表中了
需要注意的是,close表直接使用gi[x]来进行判断,因为我们所需要的最小g(x)是会根据不同路径产生不同的情况,我们需要抽出来最小的g(x)进行迭代。
double h(int point)//离终点的预计最小状态
{
return distance(t, point);
}
// double g(int point)//离起点的确切距离
// {
// return gi[point];
// }
#include<climits>
priority_queue<pair<double,int>, vector<pair<double, int>>, greater_equal<pair<double, int>> > pq;
double A_star(int s,int t)
{
//初始化部分
for(int i = 0; i < n; i++)
gi[i] = INT_MAX;
gi[s] = 0;
f[s] = gi[s] + h(s);
pq.push(make_pair(f[s], s));
while(!pq.empty())
{
int point = pq.top().second;
pq.pop();
if(point == t)//找到对应的点
return f[t];
for(int i = 0; i < n; i++)//遍历相邻点
{
if(edge[point][i])
{
double g_new = gi[point] + distance(point, i);
if(g_new < gi[i])
{
gi[i] = g_new;
f[i] = gi[i] + h(i);
pos[i] = point; //i的前一个点为point
pq.push(make_pair(f[i], i));
}
}
}
}
return f[t];
}