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同时执行同一对。