使用Dijkstra算法找到最佳路径的不可预测的结果
Unpredictable results finding optimal path with Dijkstra's Algorithm
我正在尝试实现Dirjkstra的算法,为我提供代表2d矩阵的加权图中2个节点之间的最优路径。
概要:
-
如果我为图使用相等的边权,则最佳路径(据我所知)总是正确返回
-
如果我在路径中引入"墙",大多数情况下搜索结果将无效,通常会丢失边缘或无效跳跃
-
有效的动作是上下/左/右
错误示例:
-- matrix nodeIds: --
[ 0][ 1][ 2][ 3][ 4]
[ 5][ 6][ 7][ 8][ 9]
[10][11][12][13][14]
[15][16][17][18][19]
[20][21][22][23][24]
-- matrix node Weights: --
[ 1][99][ 1][ 1][ 1]
[ 1][99][ 1][99][ 1]
[ 1][99][ 1][99][ 1]
[ 1][99][ 1][99][ 1]
[ 1][ 1][ 1][99][ 1]
-- Optimal Path Taken --
[*][ ][*][ ][*]
[*][ ][*][ ][*]
[ ][ ][*][ ][*]
[*][*][*][ ][*]
[ ][*][*][ ][*]
-- Optimal Path String --
-- NodeId->(Weight) --
24->(1)->19->(1)->22->(1)->9->(1)->16->(99)->7->(1)->12->(1)->17->(1)->14->(1)->21->(1)->4->(1)->15->(1)->2->(1)->5->(1)->0->(1)->
-- all paths searched: --
start->end(weight) 0->1(99)
start->end(weight) 5->2(1)
start->end(weight) 15->4(1)
start->end(weight) 0->5(1)
start->end(weight) 5->6(99)
start->end(weight) 12->7(1)
start->end(weight) 15->8(99)
start->end(weight) 16->9(1)
start->end(weight) 2->11(99)
start->end(weight) 17->12(1)
start->end(weight) 12->13(99)
start->end(weight) 21->14(1)
start->end(weight) 2->15(1)
start->end(weight) 7->16(99)
start->end(weight) 14->17(1)
start->end(weight) 17->18(99)
start->end(weight) 22->19(1)
start->end(weight) 4->21(1)
start->end(weight) 9->22(1)
start->end(weight) 14->23(99)
start->end(weight) 19->24(1)
这是我的代码,你应该能够复制/粘贴和运行,如果你喜欢(c++ 98):
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <map>
#include <queue>
const int BOARD_SIZE = 5;
const int NUM_ELEMENTS = BOARD_SIZE * BOARD_SIZE;
int gMatrix[BOARD_SIZE][BOARD_SIZE];
// The Weighted queue always returns the lowest weight value node from front()
class WeightedQueue {
public:
WeightedQueue();
int get(); // return the item with the lowest weight value and remove it from the map
void push(int weight, int NodeId); // add item
bool empty(); // is it empty
private:
std::map<int, int> mWeightedPositions; // weightValue, NodeId
};
WeightedQueue::WeightedQueue()
{
}
void WeightedQueue::push(int weight, int NodeId)
{
mWeightedPositions[weight] = NodeId;
}
bool WeightedQueue::empty()
{
return mWeightedPositions.empty();
}
int WeightedQueue::get()
{
std::map<int, int>::iterator iter = mWeightedPositions.begin();
int nodeId = iter->second; // nodeId
mWeightedPositions.erase(iter);
return nodeId;
}
// Matrix position row,col
struct MatrixPos
{
int row;
int col;
};
// get linear index from row, col
int getLinearIndex(int row, int col)
{
int linearIndex = BOARD_SIZE * col + row;
return linearIndex;
}
// convert linear index to matrix position row, col
MatrixPos matrixPos(int nodeId)
{
MatrixPos matrixPos = { nodeId / BOARD_SIZE, nodeId % BOARD_SIZE };
return matrixPos;
}
// reconstruct the optimal path and print it
void reconstructPath(int start, int goal, std::map<int, int> & cameFrom, std::map<int, int> & weights)
{
std::vector<int> path;
int current = goal;
path.push_back(current);
while (current != start)
{
current = cameFrom[current];
path.push_back(current);
}
printf("n -- Optimal Path Taken -- n");
for (int i = 0; i < NUM_ELEMENTS; i++)
{
if (matrixPos(i).col == 0)
{
printf("n");
}
char tileValue = ' ';
for (int j = 0; j < NUM_ELEMENTS; j++)
{
if (path[j] == i)
{
tileValue = '*';
break;
}
}
printf("[%c]", tileValue);
}
printf("n -- Optimal Path String -- n");
printf("n -- NodeId->(Weight) -- n");
for (int i = 0; (int) i < path.size(); i++)
{
printf("%d->(%d)->", path[i], weights[path[i]]);
}
printf("n");
}
// print all the paths taken by the search + the weight of the destination node
void printPaths(std::map<int, int> & pathMap, std::map<int, int> & weightMap)
{
printf("n -- all paths searched: -- n");
for (std::map<int, int>::iterator it = pathMap.begin(); it != pathMap.end(); ++it)
{
int destX = matrixPos(it->first).row;
int destY = matrixPos(it->first).col;
int startX = matrixPos(it->second).row;
int startY = matrixPos(it->second).col;
int startWeight = weightMap[it->second];
int endWeight = weightMap[it->first];
printf("start->end(weight) %d->%d(%d) n", it->second, it->first, endWeight);
}
}
// populate the Matrix and weights map
void buildMatrix(std::map<int, int> & weightMap)
{
for (int i = 0; i < NUM_ELEMENTS; i++)
{
gMatrix[matrixPos(i).row][matrixPos(i).col] = i;
weightMap[i] = 1;
}
weightMap[1] = 99;
weightMap[6] = 99;
weightMap[11] = 99;
weightMap[16] = 99;
//
weightMap[23] = 99;
weightMap[18] = 99;
weightMap[13] = 99;
weightMap[8] = 99;
}
// print matrix displaying nodeIds and Weights
void printMatrix(std::map<int, int> & weightMap)
{
printf("n -- matrix nodeIds: -- n");
for (int i = 0; i < NUM_ELEMENTS; i++)
{
if (matrixPos(i).col == 0)
{
printf("n");
}
printf("[%2d]", gMatrix[matrixPos(i).row][matrixPos(i).col]);
}
printf("n");
printf("n -- matrix node Weights: -- n");
for (int i = 0; i < NUM_ELEMENTS; i++)
{
if (matrixPos(i).col == 0)
{
printf("n");
}
printf("[%2d]", weightMap[i]);
}
printf("n");
}
// identify the neighboring nodes for nodeId, up down left right
void collectNeighbors(int nodeId, std::vector<int> & neighbors)
{
int curRow = nodeId / BOARD_SIZE;
int curCol = nodeId % BOARD_SIZE;
// left
if (curRow - 1 > 0)
{
int shiftLeft = curRow - 1;
int neighborIndex = getLinearIndex(shiftLeft, curCol);
neighbors.push_back(neighborIndex);
}
// right
if (curRow + 1 < BOARD_SIZE)
{
int shiftRight = curRow + 1;
int neighborIndex = getLinearIndex(shiftRight, curCol);
neighbors.push_back(neighborIndex);
}
// up
if (curCol - 1 > 0)
{
int shiftUp = curCol - 1;
int neighborIndex = getLinearIndex(curRow, shiftUp);
neighbors.push_back(neighborIndex);
}
// down
if (curCol + 1 < BOARD_SIZE)
{
int shiftDown = curCol + 1;
int neighborIndex = getLinearIndex(curRow, shiftDown);
neighbors.push_back(neighborIndex);
}
}
void searchMatrix(int startNodeId, int goalNodeId, std::map<int, int> & weightMap)
{
std::map<int, int> cameFrom; // neighbor NodeId, current NodeId
std::map<int, int> costSoFar; // nodeId, cost
std::vector<int> neighbors; // list of the neighboring NodeIds
WeightedQueue weightedQueue;
weightedQueue.push(0, startNodeId); // the Queue of nodes, the lowest weight node is returned first by front()
costSoFar[startNodeId] = 0;
while (!weightedQueue.empty())
{
// current index we are working with
int currentNode = weightedQueue.get();
// exit if we've reached the goal node
if (currentNode == goalNodeId)
{
break;
}
// get all the neighbors for this position
neighbors.clear();
collectNeighbors(currentNode, neighbors);
for (int i = 0; i < neighbors.size(); i++)
{
int neighborNode = neighbors[i];
int totalCost = costSoFar[currentNode] + weightMap[neighborNode];
if (!costSoFar.count(neighborNode) || totalCost < costSoFar[neighborNode])
{
// if we haven't been here yet, add it to the weightedQueue
weightedQueue.push(weightMap[neighborNode], neighborNode);
cameFrom[neighborNode] = currentNode;
costSoFar[neighborNode] = totalCost;
}
}
}
printMatrix(weightMap);
reconstructPath(startNodeId, goalNodeId, cameFrom, weightMap);
printPaths(cameFrom, weightMap);
}
int main()
{
std::map<int, int> weightMap;
buildMatrix(weightMap);
searchMatrix(0, 24, weightMap);
}
如果这对任何人都有帮助,我能够正确地获得这个工作。有两个问题:
-
正如下面的评论者所指出的,我的WeightedQueue不能作为优先级队列工作,所以我重写了这个,在内部使用一个矢量和一个自定义比较器,当添加新项时,在顶部重新排序最低权重对象。(使用std优先级队列将是一个更明智的选择)
- 我寻找邻居的功能是奇怪跳跃的根源。
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
const int BOARD_SIZE = 5;
const int NUM_ELEMENTS = BOARD_SIZE * BOARD_SIZE;
int gMatrix[BOARD_SIZE][BOARD_SIZE];
struct WeightedNode
{
int nodeId;
int weightValue;
WeightedNode(int weight, int node) : weightValue(weight), nodeId(node)
{
}
};
struct orderLeastWeight
{
inline bool operator()(const WeightedNode& weightedNode1, const WeightedNode& weightedNode2)
{
return (weightedNode1.weightValue < weightedNode2.weightValue);
}
};
// The Weighted queue always returns the lowest weight value node from front()
class WeightedQueue {
public:
WeightedQueue();
int get(); // return the item with the lowest weight value and remove it from the map
void push(int weight, int NodeId); // add item
bool empty(); // is it empty
private:
std::vector <WeightedNode> mNodeVec; // nodeId
};
WeightedQueue::WeightedQueue()
{
}
void WeightedQueue::push(int weightValue, int nodeId)
{
WeightedNode weightedNode = WeightedNode(weightValue, nodeId);
mNodeVec.push_back(weightedNode);
std::sort(mNodeVec.begin(), mNodeVec.end(), orderLeastWeight());
}
bool WeightedQueue::empty()
{
return mNodeVec.empty();
}
int WeightedQueue::get()
{
int nodeId = mNodeVec.begin()->nodeId;
mNodeVec.erase(mNodeVec.begin());
return nodeId;
}
// Matrix position row,col
struct MatrixPos
{
uint row;
uint col;
};
// get linear index from row, col
uint getLinearIndex(uint x, uint y)
{
int linearIndex = BOARD_SIZE * y + x;
return linearIndex;
}
// convert linear index to matrix position row, col
MatrixPos matrixPos(uint nodeId)
{
MatrixPos matrixPos = { nodeId / BOARD_SIZE, nodeId % BOARD_SIZE };
return matrixPos;
}
// reconstruct the optimal path and print it
void reconstructPath(int start, int goal, std::map<int, int> & cameFrom, std::map<int, int> & weights)
{
std::vector<int> path;
int current = goal;
path.push_back(current);
while (current != start)
{
current = cameFrom[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
printf("n -- Optimal Path Taken -- n");
for (int i = 0; i < NUM_ELEMENTS; i++)
{
if (matrixPos(i).col == 0)
{
printf("n");
}
char tileValue = ' ';
for (int j = 0; j < NUM_ELEMENTS; j++)
{
if (path[j] == i)
{
tileValue = '*';
break;
}
}
printf("[%c]", tileValue);
}
printf("n -- Optimal Path String -- n");
printf("n -- NodeId->(Weight) -- n");
for (int i = 0; (int) i < path.size(); i++)
{
printf("%d->(%d)->", path[i], weights[path[i]]);
}
printf("n");
}
// print all the paths taken by the search + the weight of the destination node
void printPaths(std::map<int, int> & pathMap, std::map<int, int> & weightMap)
{
printf("n -- all paths searched: -- n");
for (std::map<int, int>::iterator it = pathMap.begin(); it != pathMap.end(); ++it)
{
int destX = matrixPos(it->first).row;
int destY = matrixPos(it->first).col;
int startX = matrixPos(it->second).row;
int startY = matrixPos(it->second).col;
int startWeight = weightMap[it->second];
int endWeight = weightMap[it->first];
printf("start->end(weight) %d->%d(%d) n", it->second, it->first, endWeight);
}
}
// populate the Matrix and weights map
void buildMatrix(std::map<int, int> & weightMap)
{
for (int i = 0; i < NUM_ELEMENTS; i++)
{
gMatrix[matrixPos(i).row][matrixPos(i).col] = i;
weightMap[i] = 1;
}
weightMap[1] = 99;
weightMap[6] = 99;
weightMap[11] = 93;
weightMap[16] = 94;
//
weightMap[23] = 95;
weightMap[18] = 96;
weightMap[13] = 97;
weightMap[8] = 98;
}
// print matrix displaying nodeIds and Weights
void printMatrix(std::map<int, int> & weightMap)
{
printf("n -- matrix nodeIds: -- n");
for (int i = 0; i < NUM_ELEMENTS; i++)
{
if (matrixPos(i).col == 0)
{
printf("n");
}
printf("[%2d]", gMatrix[matrixPos(i).row][matrixPos(i).col]);
}
printf("n");
printf("n -- matrix node Weights: -- n");
for (int i = 0; i < NUM_ELEMENTS; i++)
{
if (matrixPos(i).col == 0)
{
printf("n");
}
printf("[%2d]", weightMap[i]);
}
printf("n");
}
void collectNeighbors(int nodeId, std::vector<int> & neighbors)
{
// uint getLinearIndex(uint x, uint y)
const MatrixPos tile = matrixPos((uint) nodeId);
const uint x = tile.col;
const uint y = tile.row;
printf("n -- collectNeighbors: -- nodeId %d y: %d x: %dn", nodeId, tile.row, tile.col);
// up
if (y > 0) // otherwise an underflow occurred, so not a neighbour
{
uint up = y - 1;
uint neighborIndex = getLinearIndex(x, up);
neighbors.push_back((int) neighborIndex);
printf("up -- neighborIndex: %d y: %d x: %dn", neighborIndex, y, x);
}
if (y < BOARD_SIZE - 1)
{
uint down = y + 1;
uint neighborIndex = getLinearIndex(x, down);
neighbors.push_back((int) neighborIndex);
printf("down -- neighborIndex: %d y: %d x: %dn", neighborIndex, y, x);
}
if (x > 0)
{
uint left = x - 1;
uint neighborIndex = getLinearIndex(left, y);
neighbors.push_back((int) neighborIndex);
printf("left -- neighborIndex: %d y: %d x: %dn", neighborIndex, y, x);
}
if (x < BOARD_SIZE - 1)
{
uint right = x + 1;
uint neighborIndex = getLinearIndex(right, y);
neighbors.push_back((int) neighborIndex);
printf("right -- neighborIndex: %d y: %d x: %dn", neighborIndex, y, x);
}
}
void searchMatrix(int startNodeId, int goalNodeId, std::map<int, int> & weightMap)
{
std::map<int, int> cameFrom; // neighbor NodeId, current NodeId
std::map<int, int> costSoFar; // nodeId, cost
std::vector<int> neighbors; // list of the neighboring NodeIds
WeightedQueue weightedQueue;
weightedQueue.push(0, startNodeId); // the Queue of nodes, the lowest weight node is returned first by front()
costSoFar[startNodeId] = 0;
cameFrom[startNodeId] = startNodeId;
while (!weightedQueue.empty())
{
// current index we are working with
int currentNode = weightedQueue.get();
// exit if we've reached the goal node
if (currentNode == goalNodeId)
{
break;
}
// get all the neighbors for this position
neighbors.clear();
collectNeighbors(currentNode, neighbors);
for (int i = 0; i < neighbors.size(); i++)
{
int neighborNode = neighbors[i];
int totalCost = costSoFar[currentNode] + weightMap[neighborNode];
if (!costSoFar.count(neighborNode) || totalCost < costSoFar[neighborNode])
{
// if we haven't been here yet, add it to the weightedQueue
weightedQueue.push(weightMap[neighborNode], neighborNode);
cameFrom[neighborNode] = currentNode;
costSoFar[neighborNode] = totalCost;
}
}
}
printMatrix(weightMap);
reconstructPath(startNodeId, goalNodeId, cameFrom, weightMap);
printPaths(cameFrom, weightMap);
}
int main(int argc, char *argv[])
{
// int start = atoi(argv[0]);
// int end = atoi(argv[1]);
std::map<int, int> weightMap;
buildMatrix(weightMap);
searchMatrix(0, 24, weightMap);
}
示例输出:
-- matrix nodeIds: --
[ 0][ 1][ 2][ 3][ 4]
[ 5][ 6][ 7][ 8][ 9]
[10][11][12][13][14]
[15][16][17][18][19]
[20][21][22][23][24]
-- matrix node Weights: --
[ 1][99][ 1][ 1][ 1]
[ 1][99][ 1][98][ 1]
[ 1][93][ 1][97][ 1]
[ 1][94][ 1][96][ 1]
[ 1][ 1][ 1][95][ 1]
-- Optimal Path Taken --
[*][ ][*][*][*]
[*][ ][*][ ][*]
[*][ ][*][ ][*]
[*][ ][*][ ][*]
[*][*][*][ ][*]
-- Optimal Path String --
-- NodeId->(Weight) --
0->(1)->5->(1)->10->(1)->15->(1)->20->(1)->21->(1)->22->(1)->17->(1)->12->(1)->7->(1)->2->(1)->3->(1)->4->(1)->9->(1)->14->(1)->19->(1)->24->(1)->
-- all paths searched: --
start->end(weight) 0->0(1)
start->end(weight) 0->1(99)
start->end(weight) 7->2(1)
start->end(weight) 2->3(1)
start->end(weight) 3->4(1)
start->end(weight) 0->5(1)
start->end(weight) 5->6(99)
start->end(weight) 12->7(1)
start->end(weight) 7->8(98)
start->end(weight) 4->9(1)
start->end(weight) 5->10(1)
start->end(weight) 10->11(93)
start->end(weight) 17->12(1)
start->end(weight) 12->13(97)
start->end(weight) 9->14(1)
start->end(weight) 10->15(1)
start->end(weight) 15->16(94)
start->end(weight) 22->17(1)
start->end(weight) 17->18(96)
start->end(weight) 14->19(1)
start->end(weight) 15->20(1)
start->end(weight) 20->21(1)
start->end(weight) 21->22(1)
start->end(weight) 22->23(95)
start->end(weight) 19->24(1)
在你的代码片段中可能还有其他问题,到目前为止,我注意到你正在使用std::map
,这是一种不寻常的数据结构,因为std::map
将用相同的weight
覆盖以前的nodeId
。您可以使用std::multimap
,但您将需要更多的std::iterator
和东西来循环键值对。使用std::priority_queue
,这将允许您保持代码的精确。
这是一个简单的代码片段解决方案,std::priority_queue
计算从源到目标节点的最短距离。您可以对您的问题进行类似的修改或编写:
#define Max 20010 // MAX is the maximum nodeID possible
vector <pair<int, int>> adj[Max]; // adj[u] contains the adjacent nodes of node u
int dis[Max]; // dis[i] is the distance from source to node i
priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > Q;
int dijkstra(int src, int des) {
memset(dis, MAX, sizeof dis);
Q = priority_queue <pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > ();
dis[src] = 0;
Q.push({dis[src] , src});
while(!Q.empty()) {
int u = Q.top().second;
Q.pop();
if(u == des) {
return dis[des];
}
for(int i = 0; i < (int)adj[u].size(); ++i) {
int v = adj[u][i].first;
int cost = adj[u][i].second;
if(dis[v] > dis[u] + cost) {
dis[v] = dis[u] + cost;
Q.push({dis[v], v)});
}
}
}
return -1; // no path found
}
相关文章:
- 不可预测的C++睡眠/等待行为
- 超过 N 时出现不可预测的位移结果
- DirectShow CSourceStream::FillBuffer 暂停和查找后对第一帧的不可预测的调用数
- 多线程文件 IO 程序在线程数增加时行为不可预测
- 使用 std::find 查找从二进制文件中读取的字符并转换为 std::vector 中的 std::string<string> 会产生这种不可预测的行为?
- std::sleep_for在Windows 10上的不可预测行为
- 不可预测的文件描述符泄漏
- 循环程序的行为是不可预测的
- C++中不可预测的输出
- 当来自外部库的线程不可预测地崩溃我的应用程序时,我该怎么办
- 不可预测的伪RNG
- C++ 自定义运算符(+=) 的行为方式不可预测
- 如何优化间接基数排序?(又名如何优化不可预测的内存访问模式)
- 修复不可预测的崩溃
- 不可预测的指针行为
- wxWidgets 不可预测的 Seg 故障
- 当 cin.getline() 的第二个参数大于数组长度时,不可预测的字符串长度
- 子表达式求值顺序的不可预测性
- 将QString转换为const-char*时的不可预测的运行时行为
- 使用Dijkstra算法找到最佳路径的不可预测的结果