C ++使这个Djikstra实现更快

时间:2018-04-15 07:10:06

标签: c++ algorithm qt search graph

我正在为大图(40k节点,100k弧)实现Djikstra算法。对于较短的路径,对于较大的路径(从一端到另一端),搜索时间不到一秒,这需要相当长的时间才能完成。我也在搜索后绘制路径,所以我使用了一些Qt对象。我怎么能让它更快?当我因为地图结构而搜索邻居时,我觉得我正在浪费时间。

这是班级

class PathFinder {
public:
   void findPath2(const Node & start, Node & finish);

   static PathFinder& getInstance();
   PathFinder();
   PathFinder(Gps gps);
   ~PathFinder();

   unsigned int* getPrev() const;
   void setPrev(unsigned int* prev);
   QVector<unsigned int> makePath(int target);

   GraphicNode* getTo();
   GraphicNode* getFrom();

   void setTo(GraphicNode* node);
   void setFrom(GraphicNode* node);

   class Compare
   {
   public:
      bool operator() (std::pair<Node*, int> a, std::pair<Node*, int> b)
      {
         return a.second > b.second;
      }
   };


private:
   static PathFinder* _pathfinder;
   Gps _gps;
   GraphicNode* _from;
   GraphicNode* _to;

   unsigned int* _prev;
   unsigned int* _dist;
   unsigned int _notVisited;

   bool selectedNode = false;


   Node* getMinNode();
   bool hasNotVisited();
};

这是搜索功能

void PathFinder::findPath2(const Node& start, Node& finish)
{
   QVector<Node> nodes=_gps.graph().nodes();

   std::priority_queue<std::pair<Node*,int>,std::vector<std::pair<Node*, int>>,Compare> q;

   _dist[start.id()] = 0;
   for (int i = 0; i < nodes.size(); i++) {
      std::pair<Node*, int> p = std::make_pair(const_cast<Node*>(&nodes.at(i)), _dist[i]);
      q.push(p);
   }

   while (!q.empty()) {
      std::pair<Node*, int> top = q.top();
      q.pop();
      Node* minNode = top.first;
      QMap<Node*, unsigned short> nextNodes = minNode->nextNodes();

      if (*minNode == finish) {
         return;
      }
      int minNodeId = minNode->id();
      for (QMap<Node*, unsigned short>::iterator iterator=nextNodes.begin(); iterator != nextNodes.end(); iterator++) {
         Node* nextNode = iterator.key();
         int altDist = _dist[minNodeId] + nextNodes.value(nextNode);
         int nextNodeId = nextNode->id();
         if (altDist < _dist[nextNodeId]) {
            _dist[nextNodeId] = altDist;
            _prev[nextNodeId] = minNodeId;
            std::pair<Node*, int> p = std::make_pair(nextNode, _dist[nextNodeId]);
            q.push(p);
         }
      }
   }
}

这是节点的结构,它包含一个映射到它的邻居,其中权重为值,x和y是稍后绘制它的坐标,不介意

class Node {
private:
   unsigned short _id;
   double _y;
   double _x;
   QMap<Node*, unsigned short> _nextNodes;
   bool _visited = false;


public:
   Node();
   Node(unsigned short id, double longitude, double latitude);

   unsigned short id() const;
   double y() const;
   void setY(double y);
   double x() const;
   void setX(double x);

   bool operator==(const Node& other);
   void addNextNode(Node* node, unsigned short length);

   QMap<Node*, unsigned short> nextNodes() const;
};

2 个答案:

答案 0 :(得分:3)

如果您的图表永远不会改变,那么解决方案就是将其缩小为较小的图形。

  1. 计算每个小图的边缘之间的最短距离并存储结果(edge1,edge2,distance,内部最短路径)。
  2. 计算2点之间的距离时,请在到达&#34;小图表时使用存储的结果&#34;边缘。
  3. 我相信这是GoogleMaps和其他路径查找工作的一种技巧。

    缺点是初始成本(如果你的图表真的永远不会改变,你可以存储这些&#34; mini&#34; shortests路径文件或数据库一劳永逸)。您必须仔细选择小图的大小,因为访问存储的结果也会产生(时间)成本(无论是在大内存映射还是数据库中)。必须找到平衡点。

    如果相同路径搜索经常返回的另一个想法是存储搜索次数最多的结果。

答案 1 :(得分:3)

如果您使用优先级队列和邻接列表,则实现的复杂性为O((E + V) log V)。这应该足以在任何体面的CPU上在几毫秒内计算任何最短路径。

您似乎正确完成了优先级队列部分。但为什么要使用地图而不是邻接列表呢?这似乎有点矫枉过正。

您的实施隐藏了一些额外的,不必要的工作:

QMap<Node*, unsigned short> nextNodes = minNode->nextNodes();

这将创建任何nextNodes返回的副本。这意味着对于每个节点,您将复制其已连接的所有节点,O(V^2)。您可能认为QMap包含指向Node*的指针,因此不会进行复制。但是你会复制k指针,每个指针对应一个相邻节点,这仍然很糟糕。

您应该使用(const)引用:

const QMap<Node*, unsigned short>& nextNodes = minNode->nextNodes();

或指针:

QMap<Node*, unsigned short>* nextNodes = minNode->nextNodes();

仅这一点应该有很多帮助。之后,我会切换到一个链表。使用红黑树实现QMap,因此迭代它将比迭代链表慢。

如果你的图表增长很多,那么user6106573的建议非常好(但对你当前的图形尺寸来说完全过分)。另一个建议可能是确定一条不完全 最短路径的最短路径:https://en.wikipedia.org/wiki/A * _ search_algorithm - 检查Bounded relaxation部分。同样,您当前的图表大小不需要这样做。