Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ pop()期间的Boost Fibonacci堆访问冲突 上下文_C++_Boost - Fatal编程技术网

C++ pop()期间的Boost Fibonacci堆访问冲突 上下文

C++ pop()期间的Boost Fibonacci堆访问冲突 上下文,c++,boost,C++,Boost,我目前正在实现某种形式的A*算法。我决定使用boost的fibonacci堆作为底层优先级队列 算法运行时正在生成我的图形。作为顶点对象,我正在使用: 类顶点{ 公众: 顶点(双,双); 双距离=标准::数值限制::最大值(); 双启发式=0; HeapData*fib; 顶点*前置=空ptr; std::向量adj; 双欧几里德常数(顶点*v); } 我的边缘看起来像: 类边缘{ 公众: 边(顶点*,双); 顶点*顶点=nullptr; 双倍重量=1; } 为了使用boosts fibon

我目前正在实现某种形式的A*算法。我决定使用boost的fibonacci堆作为底层优先级队列

算法运行时正在生成我的图形。作为顶点对象,我正在使用:

类顶点{
公众:
顶点(双,双);
双距离=标准::数值限制::最大值();
双启发式=0;
HeapData*fib;
顶点*前置=空ptr;
std::向量adj;
双欧几里德常数(顶点*v);
}
我的边缘看起来像:

类边缘{
公众:
边(顶点*,双);
顶点*顶点=nullptr;
双倍重量=1;
}
为了使用boosts fibonacci heap,我读到应该创建一个heap数据对象,我这样做:

struct HeapData{
顶点*v;
boost::heap::fibonacci\u heap::handle\u类型句柄;
HeapData(顶点*u){
v=u;
}
布尔操作站+rhs.v->启发式距离+v->启发式;
}
};
注意,我在比较器中包含了启发式和实际距离,以获得我想要的A*行为

我的实际A*实现如下所示:

boost::heap::fibonacci\u heap;
HeapData fibs(起始点);
起点->距离=0;
起始点->启发式=获取启发式(起始点);
自动句柄=heap.push(fibs);
(*手柄)。手柄=手柄;
而(!heap.empty()){
HeapData u=heap.top();
heap.pop();
如果(u.v->等于(端点)){
返回;
}
doSomeGraphCreationStuff(u.v);//这仅创建顶点和边
用于(边缘*e:u.v->adj){
双新距离=e->重量+u.v->距离;
如果(e->顶点->距离>新距离){
e->顶点->距离=新距离;
e->顶点->前置=u.v;
如果(!e->顶点->纤维){
如果(!u.v->等于(端点)){
e->顶点->启发式=获取启发式(e->顶点);
}
e->vertex->fib=新HeapData(e->vertex);
e->vertex->fib->handle=heap.push(*(e->vertex->fib));
}
否则{
堆。增加(e->顶点->纤维->手柄);
}
}
}
}
问题 如果我使用一个非常小的启发式算法(将a*退化为Dijkstra),算法运行得很好。但是,如果我引入一些更强的启发式,程序会抛出一个exepction,说明:
0xc000005:访问冲突写入位置0x0000000000000000。
在取消链接的方法中,提升循环_list_algorithm.hpp。出于某种原因,
next
prev
为空。这是调用
heap.pop()
的直接结果。 请注意,
heap.pop()
可以正常工作好几次,并且不会立即崩溃

问题: 是什么原因导致此问题?如何解决此问题?

我试过的
  • 我的第一个想法是,我意外地调用了increase(),即使距离+启发式变得越来越大而不是越来越小(根据文档,这可能会破坏东西)。但是,在我的实现中,这是不可能的,因为只有当距离变小时,我才能更改节点。试探法保持不变。我尝试使用update()而不是increase(),但没有成功
  • 我试图设置几个断点以获得更详细的视图,但我的数据集很大,无法用较小的数据集重现
补充资料
  • Boost版本:1.76.0
  • C++14
  • 增加函数确实是正确的(而不是减少函数),因为所有的提升堆都实现为最大堆。我们通过反转比较器并使用递增/递减反转得到一个最小堆

好的,准备乘车

  • 首先我发现了一只虫子
  • 接下来,我全面审查、重构并简化了代码
  • 当尘埃落定时,我注意到一个行为变化,看起来像代码中潜在的逻辑错误
  • 1.虫子 正如我在问题中所评论的,由于过度依赖没有明确语义的原始指针,代码复杂度很高

    当我审查和重构代码时,我发现这确实导致了一个bug:

    e->vertex->fib = new HeapData(e->vertex);
    e->vertex->fib->handle = heap.push(*(e->vertex->fib));
    
    • 在第一行中创建一个HeapData对象。您使
      fib
      成员指向该对象
    • 第二行插入该对象的副本(意思是,它是一个新对象,具有不同的对象标识,或者实际上是一个不同的地址)
    那么,现在

    • e->vertex->fib
      指向队列中不存在的(泄漏的)HeapData对象,然后
    • 实际排队的
      HeapData
      副本有一个默认构造的
      handle
      成员,这意味着该句柄包装了一个空指针。(检查
      detail/stable_heap.hpp
      中的
      boost::heap::detail::node_handle
      ,以验证这一点)
    这可以很好地解释你所看到的症状

    2.重构、简化 所以,在理解了代码之后,我得出结论

    • HeapData
      Vertex
      应该合并:HeapData仅用于将句柄链接到顶点,但您已经可以使顶点直接包含句柄

      由于这次合并

      • 顶点队列现在实际上包含顶点,表示代码的意图

      • 将所有顶点访问减少一个间接级别(减少冲突)

      • 您可以将推送操作写在一行中,从而消除bug出现的空间。之前:

         target->fib = new HeapData(target);
         target->fib->handle = heap.push(*(target->fib));
        
        之后:

         target->fibhandle = heap.push(target);
        
    • 您的
      Edge
      类实际上并没有对边进行建模,而是对目标进行“邻接” 边缘的一部分,带有
       using Node = Vertex*;
       struct PrioCompare {
           bool operator()(Node a, Node b) const;
       };
      
       namespace bh = boost::heap;
       using Heap   = bh::fibonacci_heap<Node, bh::compare<PrioCompare>>;
       using Handle = Heap::handle_type;
      
       Cost cost() const { return distance + heuristic; }
      
       static constexpr auto INF = std::numeric_limits<Cost>::infinity();
       Cost distance = INF;
      
       assert(target->cost() < previous_cost);
       heap.increase(target->fibhandle);
      
      Cost AStarSearch(Node start, Node destination) {
          Heap heap;
      
          start->distance  = 0;
          start->fibhandle = heap.push(start);
      
          while (!heap.empty()) {
              Node u = heap.top();
              heap.pop();
      
              if (u->equals(destination)) {
                  return u->cost();
              }
              u->heuristic = getHeuristic(start);
      
              doSomeGraphCreationStuff(u);
      
              for (auto& [target, weight] : u->adj) {
                  auto curDistance = weight + u->distance;
      
                  // if cheaper route, queue or update queued
                  if (curDistance < target->distance) {
                      auto cost_prior     = target->cost();
                      target->distance    = curDistance;
                      target->predecessor = u;
      
                      if (target->fibhandle == NOHANDLE) {
                          target->fibhandle = heap.push(target);
                      } else {
                          assert(target->cost() < cost_prior);
                          heap.update(target->fibhandle);
                      }
                  }
              }
          }
      
          return INF;
      }
      
      #include <boost/heap/fibonacci_heap.hpp>
      #include <iostream>
      
      using Cost = double;
      struct Vertex;
      
      Cost getHeuristic(Vertex const*) { return 0; }
      void doSomeGraphCreationStuff(Vertex const*) {
          // this only creates vertices and edges
      }
      
      struct OutEdge { // adjacency from implied source vertex
          Vertex* target = nullptr;
          Cost    weight = 1;
      };
      
      namespace bh = boost::heap;
      using Node   = Vertex*;
      struct PrioCompare {
          bool operator()(Node a, Node b) const;
      };
      using Heap   = bh::fibonacci_heap<Node, bh::compare<PrioCompare>>;
      using Handle = Heap::handle_type;
      static const Handle   NOHANDLE{}; // for expressive comparisons
      static constexpr auto INF = std::numeric_limits<Cost>::infinity();
      
      struct Vertex {
          Vertex(Cost d = INF, Cost h = 0) : distance(d), heuristic(h) {}
      
          Cost    distance  = INF;
          Cost    heuristic = 0;
          Handle  fibhandle{};
          Vertex* predecessor = nullptr;
      
          std::vector<OutEdge> adj;
      
          Cost cost() const { return distance + heuristic; }
          Cost euclideanDistanceTo(Vertex* v);
          bool equals(Vertex const* u) const { return this == u; }
      };
      
      // Now Vertex is a complete type, implement comparison
      bool PrioCompare::operator()(Node a, Node b) const {
          return a->cost() > b->cost();
      }
      
      Cost AStarSearch(Node start, Node destination) {
          Heap heap;
      
          start->distance  = 0;
          start->fibhandle = heap.push(start);
      
          while (!heap.empty()) {
              Node u = heap.top();
              heap.pop();
      
              if (u->equals(destination)) {
                  return u->cost();
              }
              u->heuristic = getHeuristic(start);
      
              doSomeGraphCreationStuff(u);
      
              for (auto& [target, weight] : u->adj) {
                  auto curDistance = weight + u->distance;
      
                  // if cheaper route, queue or update queued
                  if (curDistance < target->distance) {
                      auto cost_prior     = target->cost();
                      target->distance    = curDistance;
                      target->predecessor = u;
      
                      if (target->fibhandle == NOHANDLE) {
                          target->fibhandle = heap.push(target);
                      } else {
                          assert(target->cost() < cost_prior);
                          heap.update(target->fibhandle);
                      }
                  }
              }
          }
      
          return INF;
      }
      
      int main() {
          // a very very simple graph data structure with minimal helpers:
          std::vector<Vertex> graph(10);
          auto node = [&graph](int id)             { return &graph.at(id);       };
          auto id   = [&graph](Vertex const* node) { return node - graph.data(); };
      
          // defining 6 edges
          graph[0].adj = {{node(2), 1.5}, {node(3), 15}};
          graph[2].adj = {{node(4), 2.5}, {node(1), 5}};
          graph[1].adj = {{node(7), 0.5}};
          graph[7].adj = {{node(3), 0.5}};
      
          // do a search
          Node startPoint = node(0);
          Node endPoint   = node(7);
      
          Cost cost = AStarSearch(startPoint, endPoint);
      
          std::cout << "Overall cost: " << cost << ", reverse path: \n";
          for (Node node = endPoint; node != nullptr; node = node->predecessor) {
              std::cout << " - " << id(node) << " distance " << node->distance
                        << "\n";
          }
      }
      
      Overall cost: 7, reverse path: 
       - 7 distance 7
       - 1 distance 6.5
       - 2 distance 1.5
       - 0 distance 0
      
       0   2   1.5
       0   3    15
       2   4   2.5
       2   1     5
       1   7   0.5
       7   3   0.5
      
      #include <boost/heap/fibonacci_heap.hpp>
      #include <iostream>
      #include <deque>
      #include <fstream>
      
      using Cost = double;
      struct Vertex;
      
      struct OutEdge { // adjacency from implied source vertex
          Vertex* target = nullptr;
          Cost    weight = 1;
      };
      
      namespace bh = boost::heap;
      using Node   = Vertex*;
      struct PrioCompare {
          bool operator()(Node a, Node b) const;
      };
      using MutableQueue   = bh::fibonacci_heap<Node, bh::compare<PrioCompare>>;
      using Handle = MutableQueue::handle_type;
      static const Handle   NOHANDLE{}; // for expressive comparisons
      static constexpr auto INF = std::numeric_limits<Cost>::infinity();
      
      struct Vertex {
          Vertex(Cost d = INF, Cost h = 0) : distance(d), cachedHeuristic(h) {}
      
          Cost    distance        = INF;
          Cost    cachedHeuristic = 0;
          Handle  handle{};
          Vertex* predecessor = nullptr;
      
          std::vector<OutEdge> adj;
      
          Cost cost() const { return distance + cachedHeuristic; }
          Cost euclideanDistanceTo(Vertex* v);
      };
      
      // Now Vertex is a complete type, implement comparison
      bool PrioCompare::operator()(Node a, Node b) const {
          return a->cost() > b->cost();
      }
      
      class Graph {
          std::vector<Cost> _heuristics;
      
          Cost getHeuristic(Vertex* v) {
              size_t n = id(v);
              return n < _heuristics.size() ? _heuristics[n] : 0;
          }
          void doSomeGraphCreationStuff(Vertex const*) {
              // this only creates vertices and edges
          }
      
        public:
          Graph(std::string edgeFile, std::string heurFile) {
              {
                  std::ifstream stream(heurFile);
                  _heuristics.assign(std::istream_iterator<Cost>(stream), {});
                  if (!stream.eof())
                      throw std::runtime_error("Unexpected heuristics");
              }
              std::ifstream stream(edgeFile);
      
              size_t src, tgt;
              double weight;
              while (stream >> src >> tgt >> weight) {
                  _nodes.resize(std::max({_nodes.size(), src + 1, tgt + 1}));
                  _nodes[src].adj.push_back({node(tgt), weight});
              }
      
              if (!stream.eof())
                  throw std::runtime_error("Unexpected input");
          }
      
          Node search(size_t from, size_t to) {
              assert(from < _nodes.size());
              assert(to < _nodes.size());
              return AStar(node(from), node(to));
          }
      
          size_t id(Node node) const {
              // ugh, this is just for "pretty output"...
              for (size_t i = 0; i < _nodes.size(); ++i) {
                  if (node == &_nodes[i])
                      return i;
              }
              throw std::out_of_range("id");
          };
          Node node(int id) { return &_nodes.at(id); };
      
        private:
          // simple graph data structure with minimal helpers:
          std::deque<Vertex> _nodes; // reference stable when growing at the back
      
          // search state
          MutableQueue _queue;
      
          void enqueue(Node n) {
              assert(n && (n->handle == NOHANDLE));
              // get heuristic before insertion!
              n->cachedHeuristic = getHeuristic(n);
              n->handle = _queue.push(n);
          }
      
          Node dequeue() {
              Node node = _queue.top();
              node->handle = NOHANDLE;
              _queue.pop();
              return node;
          }
      
          Node AStar(Node start, Node destination) {
              _queue.clear();
      
              start->distance = 0;
              enqueue(start);
      
              while (!_queue.empty()) {
                  Node u = dequeue();
      
                  if (u == destination) {
                      return u;
                  }
      
                  doSomeGraphCreationStuff(u);
      
                  for (auto& [target, weight] : u->adj) {
                      auto curDistance = u->distance + weight;
      
                      // if cheaper route, queue or update queued
                      if (curDistance < target->distance) {
                          auto cost_prior     = target->cost();
                          target->distance    = curDistance;
                          target->predecessor = u;
      
                          if (target->handle == NOHANDLE) {
                              // also caches heuristic
                              enqueue(target);
                          } else {
                              // NOTE: avoid updating heuristic here, because it
                              // breaks the queue invariant if heuristic increased
                              // more than decrease in distance
                              assert(target->cost() < cost_prior);
                              _queue.increase(target->handle);
                          }
                      }
                  }
              }
      
              return nullptr;
          }
      };
      
      int main() {
          Graph graph("input.txt", "heur.txt");
      
          Node arrival = graph.search(0, 7);
      
          std::cout << "reverse path: \n";
          for (Node n = arrival; n != nullptr; n = n->predecessor) {
              std::cout << " - " << graph.id(n) << " cost " << n->cost() << "\n";
          }
      }
      
      reverse path: 
       - 7 cost 7
       - 1 cost 17.5
       - 2 cost 100.5
       - 0 cost 7