Алгоритм A*: различия между версиями
Перейти к навигации
Перейти к поиску
Ctrlalt (обсуждение | вклад) (Новая страница: «== Ссылки == * [http://www.redblobgames.com/pathfinding/a-star/introduction.html Red Blob Games — Introduction to the A* Algorithm] * [http://www.redblobg…») |
Ctrlalt (обсуждение | вклад) |
||
| (не показано 8 промежуточных версий этого же участника) | |||
| Строка 1: | Строка 1: | ||
== Общая схема перехода от Дейкстры к A* == | |||
* Дейкстра ищет кратчайшие пути от заданной начальной вершины до всех остальных. A* ищет кратчайший путь от заданной начальной вершины до заданной конечной. | |||
* Понадобится эвристическая функция h(v), делающая предположение о том, сколько ещё нужно пройти от вершины v до конечной вершины. Функция h() никогда не должна переоценивать это расстояние (но может недооценивать). | |||
* В Дейкстре мы использовали массив dist[]: dist[v] — кратчайшее расстояние от стартовой вершины до вершины v. Теперь мы дополнительно используем массив distH[]: distH[v] — это оценённая длина пути из стартовой вершины в конечную через вершину v. Очевидно, distH[v] всегда равно dist[v] + h(v). | |||
* Будем сравнивать непосещённые вершины и определять наиболее перспективного соседа не по dist[], а по distH[]. | |||
* Заметим, что если h() всегда возвращает 0, то A* превращается в алгоритм Дейкстры. | |||
{|width=100% | |||
|width=50%| | |||
int dijkstra(vector<vector<pair<int, int>>> &graph, int start, int finish) { | |||
vector<int> dist(graph.size(), 1e9); | |||
set<pair<int, int>> q; | |||
dist[start] = 0; | |||
q.insert({ dist[start], start }); | |||
while (!q.empty()) { | |||
int v = q.begin()->second; | |||
q.erase(q.begin()); | |||
if (v == finish) | |||
return dist[v]; | |||
for (auto &[to, w] : graph[v]) { | |||
if (dist[to] > dist[v] + w) { | |||
q.erase({ dist[to], to }); | |||
dist[to] = dist[v] + w; | |||
q.insert({ dist[to], to }); | |||
} | |||
} | |||
} | |||
return 1e9; | |||
} | |||
|width=50%| | |||
int aStar(vector<vector<pair<int, int>>> &graph, int start, int finish) { | |||
vector<int> dist(graph.size, 1e9); | |||
{{Changed|1=vector<int> distH(graph.size(), 1e9);}} | |||
set<pair<int, int>> q; | |||
dist[start] = 0; | |||
{{Changed|1=distH[start] = dist[start] + h(start);}} | |||
q.insert({ {{Changed|distH}}[start], start }); | |||
while (!q.empty()) { | |||
int v = q.begin()->second; | |||
q.erase(q.begin()); | |||
if (v == finish) | |||
return dist[v]; | |||
for (auto &[to, w] : graph[v]) { | |||
if (dist[to] > dist[v] + w) { | |||
q.erase({ {{Changed|distH}}[to], to }); | |||
dist[to] = dist[v] + w; | |||
{{Changed|1=distH[to] = dist[to] + h(to);}} | |||
q.insert({ {{Changed|distH}}[to], to }); | |||
} | |||
} | |||
} | |||
return 1e9; | |||
} | |||
|} | |||
== 15 Puzzle == | |||
{|width=100% | |||
|colspan=2| | |||
'''Board''' | |||
const int SIDE = 4; | |||
const int SIZE = SIDE * SIDE; | |||
struct Board { | |||
vector<int> a; | |||
int zy, zx, manhattanHeuristic, unsolvable; | |||
void recalculate() { | |||
manhattanHeuristic = 0; | |||
int inv = 0; | |||
for (int y = 0; y < SIDE; y++) { | |||
for (int x = 0; x < SIDE; x++) { | |||
if (a[y * SIDE + x]) { | |||
for (int d = 0; d < y * SIDE + x; d++) | |||
inv += a[d] && a[d] > a[y * SIDE + x]; | |||
} else { | |||
zy = y; | |||
zx = x; | |||
} | |||
int tile = (a[y * SIDE + x] + SIZE - 1) % SIZE; | |||
int ty = tile / SIDE, tx = tile % SIDE; | |||
manhattanHeuristic += abs(y - ty) + abs(x - tx); | |||
} | |||
} | |||
unsolvable = (inv + zy) % 2 == 0; | |||
} | |||
bool operator < (const Board &that) const { | |||
if (manhattanHeuristic != that.manhattanHeuristic) | |||
return manhattanHeuristic < that.manhattanHeuristic; | |||
return a < that.a; | |||
} | |||
bool canMove(int dy, int dx) { | |||
int ty = zy + dy, tx = zx + dx; | |||
return 0 <= ty && ty < SIDE && 0 <= tx && tx < SIDE; | |||
} | |||
Board move(int dy, int dx) { | |||
Board res = *this; | |||
int ty = zy + dy, tx = zx + dx; | |||
swap(res.a[zy * SIDE + zx], res.a[ty * SIDE + tx]); | |||
res.recalculate(); | |||
return res; | |||
} | |||
}; | |||
istream &operator >> (istream &in, Board &board) { | |||
board.a.resize(SIZE); | |||
for (int i = 0; i < SIZE; i++) | |||
in >> board.a[i]; | |||
board.recalculate(); | |||
return in; | |||
} | |||
|- | |||
|width=50%| | |||
'''A*''' | |||
string aStar(Board &board) { | |||
if (board.unsolvable) | |||
return "NO SOLUTION"; | |||
map<Board, int> dist, distH, pred; | |||
set<pair<int, Board>> q; | |||
dist[board] = 0; | |||
distH[board] = dist[board] + board.manhattanHeuristic; | |||
pred[board] = -1; | |||
q.insert({ distH[board], board }); | |||
static vector<int> dy = { -1, 0, 1, 0 }; | |||
static vector<int> dx = { 0, 1, 0, -1 }; | |||
static string dc = "URDL"; | |||
while (!q.empty()) { | |||
auto v = *q.begin()->second; | |||
q.erase(q.begin()); | |||
if (!v.manhattanHeuristic) | |||
break; | |||
for (int d = 0; d < 4; d++) { | |||
if (!v.canMove(dy[d], dx[d])) | |||
continue; | |||
Board to = v.move(dy[d], dx[d]); | |||
if (auto it = dist.find(to); it == dist.end() || it->second > dist[v] + 1) { | |||
q.erase({ distH[to], to }); | |||
dist[to] = dist[v] + 1; | |||
distH[to] = dist[to] + to.manhattanHeuristic; | |||
pred[to] = d; | |||
q.insert({ distH[to], to }); | |||
} | |||
} | |||
} | |||
for (int i = 0; i < SIZE; i++) | |||
board.a[i] = (i + 1) % SIZE; | |||
board.recalculate(); | |||
string path; | |||
while (1) { | |||
int d = pred[board]; | |||
if (d == -1) | |||
break; | |||
path.push_back(dc[d]); | |||
board = board.move(dy[(d + 2) % 4], dx[(d + 2) % 4]); | |||
} | |||
reverse(path.begin(), path.end()); | |||
return path; | |||
} | |||
void solve() { | |||
Board board; | |||
cin >> board; | |||
cout << aStar(board) << "\n"; | |||
} | |||
|width=50%| | |||
'''IDA*''' | |||
struct Solver { | |||
map<Board, int> visited, pred; | |||
static inline vector<int> dy = { -1, 0, 1, 0 }; | |||
static inline vector<int> dx = { 0, 1, 0, -1 }; | |||
static inline string dc = "URDL"; | |||
bool dfs(Board &v, int depth, int limit, int &nextLimit) { | |||
if (depth + v.manhattanHeuristic > limit) { | |||
nextLimit = min(nextLimit, depth + v.manhattanHeuristic); | |||
return 0; | |||
} | |||
if (!v.manhattanHeuristic) | |||
return 1; | |||
if (auto it = visited.find(v); it != visited.end() && it->second <= depth) | |||
return 0; | |||
visited[v] = depth; | |||
for (int d = 0; d < 4; d++) { | |||
if (!v.canMove(dy[d], dx[d])) | |||
continue; | |||
Board to = v.move(dy[d], dx[d]); | |||
if (dfs(to, depth + 1, limit, nextLimit)) { | |||
pred[to] = d; | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
string idaStar(Board &board) { | |||
if (board.unsolvable) | |||
return "NO SOLUTION"; | |||
int limit = 0; | |||
while (1) { | |||
int nextLimit = 1e9; | |||
visited.clear(); | |||
pred = { { board, -1 } }; | |||
if (dfs(board, 0, limit, nextLimit)) | |||
break; | |||
limit = nextLimit; | |||
} | |||
for (int i = 0; i < SIZE; i++) | |||
board.a[i] = (i + 1) % SIZE; | |||
board.recalculate(); | |||
string path; | |||
while (1) { | |||
int d = pred[board]; | |||
if (d == -1) | |||
break; | |||
path.push_back(dc[d]); | |||
board = board.move(dy[(d + 2) % 4], dx[(d + 2) % 4]); | |||
} | |||
reverse(path.begin(), path.end()); | |||
return path; | |||
} | |||
} solver; | |||
void solve() { | |||
Board board; | |||
cin >> board; | |||
cout << solver.idaStar(board) << "\n"; | |||
} | |||
|} | |||
== Knight Path == | |||
int aStar(int size, int sy, int sx, int fy, int fx) { | |||
vector<vector<int>> dist(size, vector<int>(size, 1e9)); | |||
vector<vector<int>> distH(size, vector<int>(size, 1e9)); | |||
set<pair<int, pair<int, int>>> q; | |||
dist[sy][sx] = 0; | |||
distH[sy][sx] = dist[sy][sx] + (abs(sy - fy) + abs(sx - fx)) / 3; | |||
q.insert({ distH[sy][sx], { sy, sx } }); | |||
while (!q.empty()) { | |||
auto [y, x] = q.begin()->second; | |||
q.erase(q.begin()); | |||
if (y == fy && x == fx) | |||
return dist[y][x]; | |||
static int dy[] = { -2, -2, -1, 1, 2, 2, 1, -1 }; | |||
static int dx[] = { -1, 1, 2, 2, 1, -1, -2, -2 }; | |||
for (int d = 0; d < 8; d++) { | |||
int ty = y + dy[d], tx = x + dx[d]; | |||
if (0 <= ty && ty < size && 0 <= tx && tx < size && dist[ty][tx] > dist[y][x] + 1) { | |||
q.erase({ distH[ty][tx], { ty, tx } }); | |||
dist[ty][tx] = dist[y][x] + 1; | |||
distH[ty][tx] = dist[ty][tx] + (abs(ty - fy) + abs(tx - fx)) / 3; | |||
q.insert({ distH[ty][tx], { ty, tx } }); | |||
} | |||
} | |||
} | |||
return 1e9; | |||
} | |||
== Ссылки == | == Ссылки == | ||
* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_A* neerc.ifmo.ru/wiki — Алгоритм A*] | |||
* [http://www.redblobgames.com/pathfinding/a-star/introduction.html Red Blob Games — Introduction to the A* Algorithm] | * [http://www.redblobgames.com/pathfinding/a-star/introduction.html Red Blob Games — Introduction to the A* Algorithm] | ||
* [http://www.redblobgames.com/pathfinding/a-star/implementation.html Red Blob Games — Implementation of A*] | * [http://www.redblobgames.com/pathfinding/a-star/implementation.html Red Blob Games — Implementation of A*] | ||
Текущая версия от 01:08, 12 декабря 2022
Общая схема перехода от Дейкстры к A*
- Дейкстра ищет кратчайшие пути от заданной начальной вершины до всех остальных. A* ищет кратчайший путь от заданной начальной вершины до заданной конечной.
- Понадобится эвристическая функция h(v), делающая предположение о том, сколько ещё нужно пройти от вершины v до конечной вершины. Функция h() никогда не должна переоценивать это расстояние (но может недооценивать).
- В Дейкстре мы использовали массив dist[]: dist[v] — кратчайшее расстояние от стартовой вершины до вершины v. Теперь мы дополнительно используем массив distH[]: distH[v] — это оценённая длина пути из стартовой вершины в конечную через вершину v. Очевидно, distH[v] всегда равно dist[v] + h(v).
- Будем сравнивать непосещённые вершины и определять наиболее перспективного соседа не по dist[], а по distH[].
- Заметим, что если h() всегда возвращает 0, то A* превращается в алгоритм Дейкстры.
int dijkstra(vector<vector<pair<int, int>>> &graph, int start, int finish) {
vector<int> dist(graph.size(), 1e9);
set<pair<int, int>> q;
dist[start] = 0;
q.insert({ dist[start], start });
while (!q.empty()) {
int v = q.begin()->second;
q.erase(q.begin());
if (v == finish)
return dist[v];
for (auto &[to, w] : graph[v]) {
if (dist[to] > dist[v] + w) {
q.erase({ dist[to], to });
dist[to] = dist[v] + w;
q.insert({ dist[to], to });
}
}
}
return 1e9;
}
|
int aStar(vector<vector<pair<int, int>>> &graph, int start, int finish) {
vector<int> dist(graph.size, 1e9);
vector<int> distH(graph.size(), 1e9);
set<pair<int, int>> q;
dist[start] = 0;
distH[start] = dist[start] + h(start);
q.insert({ distH[start], start });
while (!q.empty()) {
int v = q.begin()->second;
q.erase(q.begin());
if (v == finish)
return dist[v];
for (auto &[to, w] : graph[v]) {
if (dist[to] > dist[v] + w) {
q.erase({ distH[to], to });
dist[to] = dist[v] + w;
distH[to] = dist[to] + h(to);
q.insert({ distH[to], to });
}
}
}
return 1e9;
}
|
15 Puzzle
|
Board const int SIDE = 4;
const int SIZE = SIDE * SIDE;
struct Board {
vector<int> a;
int zy, zx, manhattanHeuristic, unsolvable;
void recalculate() {
manhattanHeuristic = 0;
int inv = 0;
for (int y = 0; y < SIDE; y++) {
for (int x = 0; x < SIDE; x++) {
if (a[y * SIDE + x]) {
for (int d = 0; d < y * SIDE + x; d++)
inv += a[d] && a[d] > a[y * SIDE + x];
} else {
zy = y;
zx = x;
}
int tile = (a[y * SIDE + x] + SIZE - 1) % SIZE;
int ty = tile / SIDE, tx = tile % SIDE;
manhattanHeuristic += abs(y - ty) + abs(x - tx);
}
}
unsolvable = (inv + zy) % 2 == 0;
}
bool operator < (const Board &that) const {
if (manhattanHeuristic != that.manhattanHeuristic)
return manhattanHeuristic < that.manhattanHeuristic;
return a < that.a;
}
bool canMove(int dy, int dx) {
int ty = zy + dy, tx = zx + dx;
return 0 <= ty && ty < SIDE && 0 <= tx && tx < SIDE;
}
Board move(int dy, int dx) {
Board res = *this;
int ty = zy + dy, tx = zx + dx;
swap(res.a[zy * SIDE + zx], res.a[ty * SIDE + tx]);
res.recalculate();
return res;
}
};
istream &operator >> (istream &in, Board &board) {
board.a.resize(SIZE);
for (int i = 0; i < SIZE; i++)
in >> board.a[i];
board.recalculate();
return in;
}
| |
|
A* string aStar(Board &board) {
if (board.unsolvable)
return "NO SOLUTION";
map<Board, int> dist, distH, pred;
set<pair<int, Board>> q;
dist[board] = 0;
distH[board] = dist[board] + board.manhattanHeuristic;
pred[board] = -1;
q.insert({ distH[board], board });
static vector<int> dy = { -1, 0, 1, 0 };
static vector<int> dx = { 0, 1, 0, -1 };
static string dc = "URDL";
while (!q.empty()) {
auto v = *q.begin()->second;
q.erase(q.begin());
if (!v.manhattanHeuristic)
break;
for (int d = 0; d < 4; d++) {
if (!v.canMove(dy[d], dx[d]))
continue;
Board to = v.move(dy[d], dx[d]);
if (auto it = dist.find(to); it == dist.end() || it->second > dist[v] + 1) {
q.erase({ distH[to], to });
dist[to] = dist[v] + 1;
distH[to] = dist[to] + to.manhattanHeuristic;
pred[to] = d;
q.insert({ distH[to], to });
}
}
}
for (int i = 0; i < SIZE; i++)
board.a[i] = (i + 1) % SIZE;
board.recalculate();
string path;
while (1) {
int d = pred[board];
if (d == -1)
break;
path.push_back(dc[d]);
board = board.move(dy[(d + 2) % 4], dx[(d + 2) % 4]);
}
reverse(path.begin(), path.end());
return path;
}
void solve() {
Board board;
cin >> board;
cout << aStar(board) << "\n";
}
|
IDA* struct Solver {
map<Board, int> visited, pred;
static inline vector<int> dy = { -1, 0, 1, 0 };
static inline vector<int> dx = { 0, 1, 0, -1 };
static inline string dc = "URDL";
bool dfs(Board &v, int depth, int limit, int &nextLimit) {
if (depth + v.manhattanHeuristic > limit) {
nextLimit = min(nextLimit, depth + v.manhattanHeuristic);
return 0;
}
if (!v.manhattanHeuristic)
return 1;
if (auto it = visited.find(v); it != visited.end() && it->second <= depth)
return 0;
visited[v] = depth;
for (int d = 0; d < 4; d++) {
if (!v.canMove(dy[d], dx[d]))
continue;
Board to = v.move(dy[d], dx[d]);
if (dfs(to, depth + 1, limit, nextLimit)) {
pred[to] = d;
return 1;
}
}
return 0;
}
string idaStar(Board &board) {
if (board.unsolvable)
return "NO SOLUTION";
int limit = 0;
while (1) {
int nextLimit = 1e9;
visited.clear();
pred = { { board, -1 } };
if (dfs(board, 0, limit, nextLimit))
break;
limit = nextLimit;
}
for (int i = 0; i < SIZE; i++)
board.a[i] = (i + 1) % SIZE;
board.recalculate();
string path;
while (1) {
int d = pred[board];
if (d == -1)
break;
path.push_back(dc[d]);
board = board.move(dy[(d + 2) % 4], dx[(d + 2) % 4]);
}
reverse(path.begin(), path.end());
return path;
}
} solver;
void solve() {
Board board;
cin >> board;
cout << solver.idaStar(board) << "\n";
}
|
Knight Path
int aStar(int size, int sy, int sx, int fy, int fx) {
vector<vector<int>> dist(size, vector<int>(size, 1e9));
vector<vector<int>> distH(size, vector<int>(size, 1e9));
set<pair<int, pair<int, int>>> q;
dist[sy][sx] = 0;
distH[sy][sx] = dist[sy][sx] + (abs(sy - fy) + abs(sx - fx)) / 3;
q.insert({ distH[sy][sx], { sy, sx } });
while (!q.empty()) {
auto [y, x] = q.begin()->second;
q.erase(q.begin());
if (y == fy && x == fx)
return dist[y][x];
static int dy[] = { -2, -2, -1, 1, 2, 2, 1, -1 };
static int dx[] = { -1, 1, 2, 2, 1, -1, -2, -2 };
for (int d = 0; d < 8; d++) {
int ty = y + dy[d], tx = x + dx[d];
if (0 <= ty && ty < size && 0 <= tx && tx < size && dist[ty][tx] > dist[y][x] + 1) {
q.erase({ distH[ty][tx], { ty, tx } });
dist[ty][tx] = dist[y][x] + 1;
distH[ty][tx] = dist[ty][tx] + (abs(ty - fy) + abs(tx - fx)) / 3;
q.insert({ distH[ty][tx], { ty, tx } });
}
}
}
return 1e9;
}