MPI實現A-star算法(六角格地圖)
A-star算法
A星算法這裏就不再贅述了,可以參考這篇文章:
https://blog.csdn.net/qq_36946274/article/details/81982691
六角格
六角格的入門可以參考這篇:
https://www.cnblogs.com/hoodlum1980/archive/2009/08/10/1542629.html
這裏要補充的一點是六角格的距離估計:
這裏的六角格表示爲採用上述鏈接中使用的六角格劃分方式。
六角格格子間的距離估計
這裏計算的是最短距離,類似曼哈頓距離(不考慮障礙物的情況下),用於在A*算法中進行六角格間距離的估計。其中有幾個關鍵點:
- 六角格交錯排列,並且偶數行的橫座標要小於奇數行的橫座標。同時,偶數行相臨奇數行的橫座標是x(x是橫座標)和x-1,而奇數行相鄰偶數行的橫座標是x(x是橫座標)和x+1。
- 每兩行是一個循環,並且每兩行可以讓橫座標變化1而不需要額外的步數(我稱其爲縱座標帶給橫座標的補償)。
- 不同的方向和行數是奇數和偶數決定了以相差奇數行開始計橫座標補償還是偶數行。例如對於(0,0)和(1,1)點,沒有引起橫座標補償。但是對於(1,1)和(2,2)點。僅相差一行卻可以讓橫座標補償1。這實際上是由於第一點引起的。
- 距離計算公式爲dis = max(|x2-x1|-橫座標補償,0) + |y2-y1|
- 用C語言編程表示爲:
int calcDistance(int x1, int y1, int x2, int y2)
{
int y_dis = abs(y2 - y1);
int y_com;
if ((y1 % 2 == 0 && x2 - x1 <= 0) || (y1 % 2 == 1 && x2 - x1 >= 0))
{
y_com = (int)(y_dis + 1) / 2;
}
if ((y1 % 2 == 0 && x2 - x1 >= 0) || (y1 % 2 == 1 && x2 - x1 <= 0))
{
y_com = (int)(y_dis) / 2;
}
int x_dis = abs(x2 - x1);
x_dis = x_dis - y_com > 0 ? x_dis - y_com : 0;
return x_dis + y_com;
};
其中的y_com即爲橫座標補償。
MPI實現A-star算法
一些實現要點:
- 對於openlist,closelist用鏈表實現
- 每個MPI進程維護一個自己的openlist和closelist。
- MPI進程間用一種結構體來封裝操作,這裏我用的結構體如下:
struct MyOperation {
// 0:添加,1刪除,2修改,3不進行操作,當添加時,x,y爲待添加的值,修改時,x,y爲待修改的值,pos爲修改位置
int op;
int x;
int y;
int pos;
};
具體代碼如下:
mylist.h,用來定義自己定義的鏈表
#pragma once
#include<stdio.h>
struct Node {
int x;
int y;
int g;
int h;
int f;
int pre_x;
int pre_y;
Node* next;
};
struct NodeList {
Node* head;
int length;
};
void nodeListCreate(NodeList* node_list);
Node* nodeDelete(NodeList* node_list, int pos);
bool nodeAdd(NodeList* node_list, Node* node);
Node* findNode(NodeList* node_list, int x, int y, int& pos);
Node* findNode(NodeList* node_list, int x, int y);
void modifyNode(NodeList* node_list, int x, int y, int h, int g, int f, int pre_x, int pre_y, int pos);
bool judgeNodeEqual(Node* node1, Node* node2);
void printNodeList(NodeList* node_list);
mylist.cpp 鏈表的實現
#include<stdio.h>
#include "mylist.h"
/*
創建一個鏈表
*/
void nodeListCreate(NodeList* node_list)
{
Node a;
Node* head = NULL;
head = &a;
head->x = -1;
head->y = -1;
node_list->head = head;
node_list->length = 0;
}
/*
刪除一個節點並返回
*/
Node* nodeDelete(NodeList* node_list, int pos)
{
if (pos < 0 || pos >= node_list->length)
{
return NULL;
}
Node* temp = node_list->head;
Node* result = NULL;
int count = 0;
while (count < pos)
{
temp = temp->next;
count++;
}
// 待刪除節點
result = temp->next;
// 後面還有節點
if (node_list->length>1)
{
temp->next = temp->next->next;
}
// 最後一個
else
{
temp->next = NULL;
}
result->next = NULL;
node_list->length--;
return result;
}
/*
添加一個節點到最前面
*/
bool nodeAdd(NodeList* node_list, Node* node)
{
if (node_list->length > 0)
{
Node* temp = node_list->head->next;
node_list->head->next = node;
node->next = temp;
node_list->length++;
return true;
}
else
{
node_list->length++;
node_list->head->next = node;
node->next = NULL;
return true;
}
return false;
}
/*
查找符合條件的節點
*/
Node* findNode(NodeList* node_list, int x, int y, int& pos)
{
if (node_list->length == 0)
{
return NULL;
}
Node* temp = node_list->head;
pos = -1;
do {
temp = temp->next;
pos++;
if (temp->x == x && temp->y == y)
{
return temp;
}
} while (temp->next);
return NULL;
}
/*
查找符合條件的節點
*/
Node* findNode(NodeList* node_list, int x, int y)
{
if (node_list->length == 0)
{
return NULL;
}
Node* temp = node_list->head;
do {
temp = temp->next;
if (temp->x == x && temp->y == y)
{
return temp;
}
} while (temp->next!=NULL);
return NULL;
}
void modifyNode(NodeList* node_list, int x, int y,int h,int g,int f,int pre_x,int pre_y, int pos)
{
int count = 0;
Node* temp = node_list->head;
while (count <= pos)
{
temp = temp->next;
count++;
}
temp->x = x;
temp->y = y;
temp->h = h;
temp->g = g;
temp->f = f;
temp->pre_x = pre_x;
temp->pre_y = pre_y;
}
bool judgeNodeEqual(Node* node1, Node* node2)
{
if (node1->x == node2->x && node1->y == node2->y)
{
return true;
}
return false;
}
void printNodeList(NodeList* node_list)
{
if (node_list->length == 0)
{
fprintf(stderr, "NODELIST IS EMPTY");
}
else
{
Node* temp = node_list->head;
do {
temp = temp->next;
fprintf(stdout, "RESULT %d %d\n", temp->x, temp->y);
} while (temp->next != NULL);
}
}
main.c MPI實現的A-star算法的核心程序
#include <stdio.h>
#include "mpi.h"
#include "myList.h"
#include <stdlib.h>
#include <math.h>
#define cols 10
#define rows 10
#define inf 999999
struct MyOperation {
// 0:添加,1刪除,2修改,3不進行操作,當添加時,x,y爲待添加的值,修改時,x,y爲待修改的值,pos爲修改位置
int op;
int x;
int y;
int pos;
};
int calcDistance(int x1, int y1, int x2, int y2)
{
int y_dis = abs(y2 - y1);
int y_com;
if ((y1 % 2 == 0 && x2 - x1 <= 0) || (y1 % 2 == 1 && x2 - x1 >= 0))
{
y_com = (int)(y_dis + 1) / 2;
}
if ((y1 % 2 == 0 && x2 - x1 >= 0) || (y1 % 2 == 1 && x2 - x1 <= 0))
{
y_com = (int)(y_dis) / 2;
}
int x_dis = abs(x2 - x1);
x_dis = x_dis - y_com > 0 ? x_dis - y_com : 0;
return x_dis + y_com;
};
void find(int(*map)[cols], NodeList* closeList, NodeList* openList, Node* input_node, int x_change, int y_change, int x_end, int y_end, MyOperation& my_operation)
{
//路不可達
if (map[input_node->y + y_change][input_node->x + x_change] == 1 || input_node->y + y_change < 0 || input_node->y + y_change >= rows || input_node->x + x_change < 0 || input_node->x + x_change >= cols)
{
my_operation = { 3,-1,-1,-1 };
return;
}
//在closelist裏面
if (findNode(closeList, input_node->x + x_change, input_node->y + y_change) != NULL)
{
my_operation = { 3,-1,-1,-1 };
return;
}
//在openlist裏面,需要修改
int modifyPos;
if (findNode(openList, input_node->x + x_change, input_node->y + y_change, modifyPos) != NULL)
{
my_operation = { 2,input_node->x + x_change,input_node->y + y_change,modifyPos };
return;
}
//不在openlist裏面,添加進openlist
else {
my_operation = { 0,input_node->x + x_change,input_node->y + y_change };
return;
}
}
int main()
{
// 創建openlist和closelist
NodeList* openList, * closeList;
openList = (NodeList*)malloc(sizeof(NodeList));
closeList = (NodeList*)malloc(sizeof(NodeList));
nodeListCreate(openList);
nodeListCreate(closeList);
//MPI初始化
MPI_Init(NULL, NULL);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
//生成MAP,0代表可通信,1代表不可通行
int map[rows][cols];
if (rank == 0)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
int temp = rand() % 100;
// 比例取1:2
if (temp < 33)
{
map[i][j] = 1;
}
else
{
map[i][j] = 0;
}
fprintf(stdout, "%d ", map[i][j]);
}
fprintf(stdout, "\n");
}
}
MPI_Bcast(map, cols * rows, MPI_INT, 0, MPI_COMM_WORLD);
// 生成起點和終點,把起點加入openlist集
int position[4];//starty,startx,endy,endx
Node* start = (Node*)malloc(sizeof(Node));
if (rank == 0)
{
do {
position[0] = rand() % rows;
position[1] = rand() % cols;
position[2] = rand() % rows;
position[3] = rand() % cols;
} while (map[position[0]][position[1]] != 0 || map[position[2]][position[3]] != 0);
fprintf(stdout, "Start End%d %d %d %d\n", position[1], position[0], position[3], position[2]);
}
MPI_Bcast(position, 4, MPI_INT, 0, MPI_COMM_WORLD);
start->x = position[1];
start->y = position[0];
start->pre_x = -1;
start->pre_y = -1;
start->g = 0;
start->h = calcDistance(position[1], position[0], position[3], position[2]);
start->f = start->g + start->h;
start->next = NULL;
nodeAdd(openList, start);
int nonsense = 0;
int count = 0;
while (openList->head->next!=NULL && findNode(openList, position[3], position[2]) == NULL)
{
//找openlist裏F的最小值,這裏沒有用並行,這部分要並行的話如果我用現在鏈表提升可能不是很明顯,因爲鏈表不管怎麼樣都需要遍歷和比較一次
//想要提升的話需要那種連續分配空間的存儲形式
count++;
int pos;
if (rank == 0)
{
int min = inf;
Node* temp;
Node* result;
if (openList->length == 0)
{
printf("無法找到路徑");
return 1;
}
else
{
pos = -1;
temp = openList->head;
do {
pos++;
temp = temp->next;
if (temp->f < min)
{
min = temp->f;
result = temp;
}
} while (temp->next != NULL);
}
}
MPI_Bcast(&pos, 1, MPI_INT, 0, MPI_COMM_WORLD);
Node* deletedNode = nodeDelete(openList, pos);
nodeAdd(closeList, deletedNode);
MyOperation rankOperation;
MyOperation allOperation[6];
//每個點找相鄰點
if (rank == 0)// x-1
{
fprintf(stdout, "MIN_F %d %d\n", deletedNode->x, deletedNode->y);
find(map, closeList, openList, deletedNode, -1, 0, position[3], position[2], rankOperation);
allOperation[rank] = rankOperation;
for (int i = 1; i < 6; i++)
{
MPI_Status status;
MPI_Recv(&allOperation[i], 4, MPI_INT, i, 0, MPI_COMM_WORLD, &status);
}
}
else if (rank == 1)// x+1
{
find(map, closeList, openList, deletedNode, +1, 0, position[3], position[2], rankOperation);
MPI_Send(&rankOperation, 4, MPI_INT, 0, 0, MPI_COMM_WORLD);
}
else if (rank == 2)// y-1
{
find(map, closeList, openList, deletedNode, 0, -1, position[3], position[2], rankOperation);
MPI_Send(&rankOperation, 4, MPI_INT, 0, 0, MPI_COMM_WORLD);
}
else if (rank == 3)// y+1
{
find(map, closeList, openList, deletedNode, 0, +1, position[3], position[2], rankOperation);
MPI_Send(&rankOperation, 4, MPI_INT, 0, 0, MPI_COMM_WORLD);
}
else if (rank == 4)// y-1方向
{
int x_change, y_change = -1;
if (deletedNode->y % 2 == 0)// 偶數
{
x_change = -1;
}
else {
x_change = 1;
}
find(map, closeList, openList, deletedNode, x_change, y_change, position[3], position[2], rankOperation);
MPI_Send(&rankOperation, 4, MPI_INT, 0, 0, MPI_COMM_WORLD);
}
else if (rank == 5)// x-1
{
int x_change, y_change = 1;
if (deletedNode->y % 2 == 0)// 偶數
{
x_change = -1;
}
else {
x_change = 1;
}
find(map, closeList, openList, deletedNode, x_change, y_change, position[3], position[2], rankOperation);
MPI_Send(&rankOperation, 4, MPI_INT, 0, 0, MPI_COMM_WORLD);
}
MPI_Bcast(&allOperation, 24, MPI_INT, 0, MPI_COMM_WORLD);
for (int i = 0; i < 6; i++)
{
if (allOperation[i].op == 0)// 添加
{
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->x = allOperation[i].x;
newNode->y = allOperation[i].y;
newNode->g = deletedNode->g + 1;
newNode->h = calcDistance(newNode->x, newNode->y, position[3], position[2]);
newNode->f = newNode->g + newNode->h;
newNode->pre_x = deletedNode->x;
newNode->pre_y = deletedNode->y;
newNode->next = NULL;
nodeAdd(openList, newNode);
}
else if (allOperation[i].op == 2)//修改
{
int h = calcDistance(allOperation[i].x, allOperation[i].y, position[3], position[2]);
int g = deletedNode->g + 1;
int f = h + g;
modifyNode(openList, allOperation[i].x, allOperation[i].y, h, g, f, deletedNode->x, deletedNode->y, allOperation[i].pos);
}
}
}
MPI_Barrier(MPI_COMM_WORLD);
//回溯
if (openList->length == 0) {
fprintf(stderr, "Can not find");
return 0;
}
Node* temp = findNode(openList, position[3], position[2]);
NodeList* result = (NodeList*)malloc(sizeof(NodeList));
nodeListCreate(result);
Node* result_temp = (Node*)malloc(sizeof(Node));
result_temp->x = temp->x;
result_temp->y = temp->y;
result_temp->next = NULL;
nodeAdd(result, result_temp);
while (temp->pre_x != -1)
{
temp = findNode(closeList, temp->pre_x, temp->pre_y);
result_temp = (Node*)malloc(sizeof(Node));
result_temp->x = temp->x;
result_temp->y = temp->y;
result_temp->next = NULL;
nodeAdd(result, result_temp);
}
if (rank == 0)
{
fprintf(stdout, "length %d\n", result->length);
printNodeList(result);
}
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
運行指定的MPI進程數爲6。
總結
簡單歸納下我實現的MPI的A-star算法原理:
地圖初始化和起點終點初始化等由rank 0進程完成,並通過廣播分發。
然後每個進程查找一個方向,並將查找的結果封裝爲操作(自己定義的結構體來描述操作)發送給rank 0進程,rank 0並將其廣播出去。
每個進程根據操作來更新自己的openlist和closelist,從而保證數據一致性。
對於單任務而言(即僅查找一對起點和終點),MPI並不能很顯著的加速(特別是地圖小的情況下),其更適合於多對任務點,即每個MPI進程分到若干對任務,而不是所有MPI同時執行同一對。