学习A*算法所需的前置知识
-
Dijkstra算法
-
BFS优先搜索算法
为什么要学习A*算法?
为了弥补之前简陋的JAVA大作业,所以我决定学习一下A* 算法,据说A* 算法的效率 > Dijkstra算法 和 优先搜索(BFS)算法,此BFS非彼BFS希望看到这篇文章的人,能分清楚,当然A*算法的高效之处也是继承了此BFS的高效,但为什么不用此BFS呢,下文会讲解,并且学习这个算法也用于实习找工作用。
A*算法
首先我们来引入Dijkstra算法,首先我们明白Dijkstra算法的时间复杂度是的 O ( n l o g n ) O(nlogn) O(nlogn),如果了解Dijkstra算法的,是知道的Dijkstra是盲目的寻找图中的顶点,直到对所有点做了松弛操作后,才算完成搜索,但这样就会利用很多完全不可能的点,这样虽然可以找到最短路,但是效率略低,时间复杂度稳定在 O ( n l o g n ) O(nlogn) O(nlogn)。
接下来们引入优先搜索算法(BFS)这个算法的时间复杂度也是 O ( n l o g n ) O(nlogn) O(nlogn)(指最坏时间复杂度),这个算法其实改良一下就成为A* 了,但是我们为了方便理解我们先讲他的简版,在讲它之前我们引入一个启发函数。
f ( x ) = g ( x ) + h ( x ) f(x)=g(x)+h(x) f(x)=g(x)+h(x)
f ( n ) f(n) f(n) 代表 从起点到终点的综合预估花费, g ( n ) g(n) g(n) 代表起点到 n n n 节点的实际花费, h ( n ) h(n) h(n) 代表从 n n n 节点到终点的预估花费。由于 f ( n ) f(n) f(n)代表是当前状态最理想的花费,所以我们通过这种启发函数搜索到终点后得到的答案一定是最优的,那么BFS的从广度优先搜索成为优先搜索只需要将队列边为优先队列,每次取预估花费最小的即可,对于网格图的 h ( n ) h(n) h(n) 我们只需要利用欧几里得求出贡献即可。那么这种写法就会避免使用完全没有用的节点,呢么为什么我们不采取呢,主要原因是它的路径不一定是最短的。所以我们不采用这种方法。那么A* 是如何实现的呢,其实很简单,我们将Dijkstra小改一下,按照优先搜索的写法,并且如果遇到终点直接结束即可,这就是A* 算法,下面我们来比较一下其高效成度。
展示一下A* 网格图寻路的效果图,并且含有遍历点的图,含未被使用的点的概率。
#include <bits/stdc++.h>
using namespace ::std;
const int N = 2e3 + 5;
const int dir[4][2] = {
1, 0, 0, 1, -1, 0, 0, -1};
struct Node {
int f, x, y;
bool operator<(const Node& b) const {
return f > b.f; }
};
class A_Star {
public:
A_Star(){
};
A_Star(int sx, int sy, int ex, int ey, int n, int m)
: sx(sx), sy(sy), ex(ex), ey(ey), n(n), m(m) {
}
void scan();
void Ashow();
private:
int sx, sy, ex, ey, n, m;
static int vis[N][N];
static int Map[N][N];
static int dis[N][N];
static int path[N][N][2];
void Dijkstra();
int Dis(int dx, int dy);
};
int A_Star::vis[N][N] = {
0};
int A_Star::Map[N][N] = {
0};
int A_Star::dis[N][N] = {
0};
int A_Star::path[N][N][2] = {
0};
int A_Star::Dis(int dx, int dy) {
return dis[dx][dy] + abs(dx - ex) + abs(dy - ey);
}
void A_Star::scan() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
Map[i][j] = 0;
dis[i][j] = 0x3f3f3f3f;
}
}
}
void A_Star::Ashow() {
Dijkstra();
vis[ex][ey] = 3;
int ans = 0;
for (int i = path[ex][ey][0], j = path[ex][ey][1]; i && j;) {
vis[i][j] = 3;
int k1 = path[i][j][0];
int k2 = path[i][j][1];
i = k1, j = k2;
}
// cout << vis[1][3] << endl;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (vis[i][j] == 3)
cout << "*";
else
cout << ".";
}
puts("");
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (vis[i][j])
ans++;
cout << vis[i][j];
}
puts("");
}
cout << endl;
int x = __gcd(n * n, n*n - ans);
cout << (n * n - ans) / x << '/' << n * n / x << endl;
cout << (1.0 * (n * n - ans)) / (n * n * 1.0) << endl;
}
void A_Star::Dijkstra() {
priority_queue<Node> q;
while (!q.empty()) {
q.pop();
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dis[i][j] = 0x3f3f3f3f;
}
}
Node st, en;
dis[sx][sy] = 0;
st.x = sx, st.y = sy;
st.f = Dis(sx, sy);
q.push(st);
while (!q.empty()) {
st = q.top();
q.pop();
if (vis[st.x][st.y])
continue;
vis[st.x][st.y] = 1;
if (st.x == ex && st.y == ey)
return;
for (int i = 0; i < 4; i++) {
int xx = st.x + dir[i][0];
int yy = st.y + dir[i][1];
if (xx <= 0 || yy <= 0 || xx > n || yy > m || Map[xx][yy])
continue;
if (dis[xx][yy] > dis[st.x][st.y] + 1) {
dis[xx][yy] = dis[st.x][st.y] + 1;
path[xx][yy][0] = st.x, path[xx][yy][1] = st.y;
if (!vis[xx][yy]) {
en.x = xx, en.y = yy;
en.f = Dis(xx, yy);
q.push(en);
}
}
}
}
}
int main() {
int n;
cin >> n;
A_Star T(1, 1, n, n, n, n);
T.scan();
T.Ashow();
return 0;
}
以下是利用pygame制成的可动态显示A*算法过程的图和代码:
如果随机生成的地图没有终点,呢么程序会报错,即不会执行
import pygame
import sys
import queue as Q
import random
import time
class Node(object):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def __lt__(self, other):
return self.a < other.a
class NS(object):
def __init__(self, x, y):
self.x = x
self.y = y
class pair(object):
def __init__(self, x, y):
self.x = x
self.y = y
ex = 29
ey = 39
q = Q.Queue()
qs = Q.Queue()
Map = [[0 for i in range(40)] for _ in range(30)]
vis = [[0 for i in range(40)] for _ in range(30)]
pre = [[0 for i in range(40)] for _ in range(30)]
dir = [[1, 0], [-1, 0], [0, 1], [0, -1]]
dis = [[100000 for i in range(40)] for _ in range(30)]
def calc(x, y):
return (ex-x)*(ex-x)+(ey-y)*(ey-y)
def start():
time.sleep(1)
def A_star():
que = Q.PriorityQueue()
dis[0][0] = 0
que.put(Node(calc(0, 0)+dis[0][0], 0, 0))
while not que.empty():
st = que.get()
if vis[st.b][st.c] == 1:
continue
vis[st.b][st.c] = 1
q.put(NS(st.b, st.c))
if st.b == ex and st.c == ey:
break
for i in range(4):
xx = st.b+dir[i][0]
yy = st.c+dir[i][1]
if (xx >= 0) and (yy >= 0) and (xx <= ex) and (yy <= ey) and Map[xx][yy] == 0:
if(dis[xx][yy] > dis[st.b][st.c]+1):
dis[xx][yy] = dis[st.b][st.c]+1
pre[xx][yy] = (st.b, st.c)
que.put(Node(dis[xx][yy]+calc(xx, yy), xx, yy))
stack = []
def path():
if(dis[ex][ey] == 100000):
return
i = ex
j = ey
stack.append((i, j))
# print(i, j)
while i != 0 or j != 0:
k1 = pre[i][j][0]
k2 = pre[i][j][1]
i = k1
j = k2
# print(i, j)
stack.append((i, j))
def createMap():
T = 700
while T >= 0:
T -= 1
if Map[random.randint(1, 29)][random.randint(1, 39)] == 1:
Map[random.randint(1, 29)][random.randint(1, 39)] = 0
else:
Map[random.randint(1, 29)][random.randint(1, 39)] = 1
Map[29][39] = 0
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("py")
Black = pygame.Color("black")
Green = pygame.Color("green")
Blue = pygame.Color("blue")
Red = pygame.Color("red")
fps = 20
fclock = pygame.time.Clock()
createMap()
screen.fill("grey")
for i in range(0, 30):
for j in range(0, 40):
pygame.draw.rect(screen, Black, (j*20, i*20, 20, 20), 1)
for i in range(30):
for j in range(40):
if(Map[i][j] == 1):
pygame.draw.rect(screen, Red, (j*20, i*20, 20, 20), 0)
A_star()
path()
start()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if not q.empty():
st = q.get()
pygame.draw.rect(screen, Blue, (st.y*20, st.x*20, 20, 20), 0)
elif len(stack):
s = stack.pop()
pygame.draw.rect(screen, Green, (s[1]*20, s[0]*20, 20, 20), 0)
fclock.tick(fps)
pygame.display.update()ru
动态图: