在提升中更新优先级::d_ary_heap_indirect

Updating priorities in a boost::d_ary_heap_indirect

本文关键字:heap indirect ary 优先级 更新      更新时间:2023-10-16

我使用d_ary_heap_indirect作为优先级队列(首先处理优先级最高的项目),并使用属性映射来存储优先级。然而,当我更改优先级属性映射中的值并将已经在队列中的顶点再次推入队列时,会导致一种无效状态,即顶点在队列中不同的位置出现两次。

这里有一个演示:

#include <iostream>
#include <iomanip>
#include <boost/graph/grid_graph.hpp>
#include <boost/graph/detail/d_ary_heap.hpp>
#include <boost/property_map/property_map.hpp>
#include <cstdlib>
template <typename TQueue>
static void OutputQueue(TQueue queue);
int main(int, char*[])
{
  srand((unsigned int)time(NULL));
  srand48((unsigned int)time(NULL));
  boost::array<std::size_t, 2> lengths = { { 2,2 } };
  typedef boost::grid_graph<2> GraphType;
  GraphType graph(lengths);
  typedef boost::graph_traits<GraphType>::vertex_descriptor Vertex;
  typedef boost::property_map<GraphType, boost::vertex_index_t>::const_type GridIndexMapType;
  GridIndexMapType gridIndexMap(get(boost::vertex_index, graph));
  typedef boost::vector_property_map<std::size_t, GridIndexMapType> IndexInHeapMap;
  IndexInHeapMap index_in_heap(gridIndexMap);
  typedef boost::graph_traits<GraphType>::vertex_iterator VertexIteratorType;
  typedef boost::vector_property_map<float, GridIndexMapType> PriorityMapType;
  PriorityMapType priorityMap(gridIndexMap);
  VertexIteratorType vertexIterator, vertexIteratorEnd;
  typedef std::greater<float> ComparisonFunctor;
  typedef boost::d_ary_heap_indirect<Vertex, 4, IndexInHeapMap, PriorityMapType, ComparisonFunctor > MutableQueueType;
  ComparisonFunctor comparisonFunctor;
  MutableQueueType mutableQueue(priorityMap, index_in_heap, comparisonFunctor);
  std::cout << "There are " << mutableQueue.size() << " items in the queue." << std::endl;
  // Add random values to the vertices and add them to the queue
  for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
  {
    put(priorityMap, *vertexIterator, rand() % 1000);
  }
  for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
  {
    mutableQueue.push(*vertexIterator);
  }
  std::cout << "There are " << mutableQueue.size() << " items in the queue." << std::endl;
  std::cout << "The priority queue is: " << std::endl;
  OutputQueue(mutableQueue);
  // Insert another set of random values for each vertex
  for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
  {
    float newPriority = rand() % 1000;
    std::cout << "New priority for " << vertexIterator->operator[](0) << ", " << vertexIterator->operator[](1) << " " << newPriority << std::endl;
    put(priorityMap, *vertexIterator, newPriority);
  }
  for( tie(vertexIterator, vertexIteratorEnd) = vertices(graph); vertexIterator != vertexIteratorEnd; ++vertexIterator)
  {
    //mutableQueue.push(*vertexIterator); // This makes sense that the queue would not end up sorted
    mutableQueue.push_or_update(*vertexIterator); // I thought this one should work
    //mutableQueue.update(*vertexIterator); // This one actually seems to UNsort the queue?
  }
  std::cout << "There are " << mutableQueue.size() << " items in the queue." << std::endl;
  std::cout << "The priority queue is: " << std::endl;
  OutputQueue(mutableQueue);
  std::cout << std::endl;
  return 0;
}
template <typename TQueue>
static void OutputQueue(TQueue queue)
{
  while( ! queue.empty() )
  {
    typename TQueue::value_type u = queue.top();
    // These two lines are equivalent
    std::cout << "vertex: " << u[0] << " " << u[1] << " priority: " << get(queue.keys(), u) << std::endl;
    queue.pop();
  }
}

还有一个演示输出:

There are 0 items in the queue.
There are 4 items in the queue.
The priority queue is: 
vertex: 1 1 priority: 445
vertex: 0 0 priority: 150
vertex: 0 1 priority: 84
vertex: 1 0 priority: 0
New priority for 0, 0 769
New priority for 1, 0 870
New priority for 0, 1 99
New priority for 1, 1 211
There are 8 items in the queue.
The priority queue is: 
vertex: 0 0 priority: 769
vertex: 1 0 priority: 870
vertex: 1 0 priority: 870
vertex: 0 0 priority: 769
vertex: 1 1 priority: 211
vertex: 1 1 priority: 211
vertex: 0 1 priority: 99
vertex: 0 1 priority: 99

演示只需为每个顶点设置随机优先级值,并将它们全部推入队列。然后它又做了完全相同的事情。您可以在输出中看到,一些项目出现在队列中的不同位置(而不是像我所期望的那样背靠背,因为它们在PriorityMap中引用了相同的优先级值)。

问题是项目(0,0)(具有新优先级769)出现在具有优先级870的顶点(1,0)上方。这将导致项目按错误的顺序处理。

有没有一种方法可以在推送队列中的项目时替换它,而不是添加第二个项目?(比如std::set,而不是像std::multiset那样的当前行为)?

---------编辑------------在"//为每个顶点插入另一组随机值"循环中,我将"mutableQueue.prush(*vertexIterator)"替换为:

mutableQueue.push_or_update(*vertexIterator);

不幸的是,它没有达到我所期望的效果——现在的输出是:

There are 0 items in the queue.
New priority for 0, 0 150
New priority for 1, 0 522
New priority for 0, 1 27
New priority for 1, 1 883
There are 4 items in the queue.
The priority queue is: 
vertex: 1 1 priority: 883
vertex: 1 0 priority: 522
vertex: 0 0 priority: 150
vertex: 0 1 priority: 27
New priority for 0, 0 658
New priority for 1, 0 591
New priority for 0, 1 836
New priority for 1, 1 341
There are 7 items in the queue.
The priority queue is: 
vertex: 0 1 priority: 836
vertex: 0 1 priority: 836
vertex: 0 0 priority: 658
vertex: 0 0 priority: 658
vertex: 1 0 priority: 591
vertex: 1 0 priority: 591
vertex: 1 1 priority: 341

此外,将push()替换为update()将生成:

There are 0 items in the queue.
New priority for 0, 0 806
New priority for 1, 0 413
New priority for 0, 1 592
New priority for 1, 1 861
There are 4 items in the queue.
The priority queue is: 
vertex: 1 1 priority: 861
vertex: 0 0 priority: 806
vertex: 0 1 priority: 592
vertex: 1 0 priority: 413
New priority for 0, 0 175
New priority for 1, 0 642
New priority for 0, 1 991
New priority for 1, 1 462
There are 4 items in the queue.
The priority queue is: 
vertex: 1 1 priority: 462
vertex: 0 1 priority: 991
vertex: 1 0 priority: 642
vertex: 0 0 priority: 175

现在只有4个项目(正如我所期望的),但它们没有排序!

-----------编辑-更多信息--------------我认为index_in_heap映射出了问题。我补充道:

std::cout << "Index added: " << get(index_in_heap, v) << std::endl;

此行之后:

put(index_in_heap,v,index);

在d_ary_heap_indirect::push(值)中。

我还添加了

std::cout << "Index added caller: " << get(index_in_heap, v) << std::endl;

在将值添加到队列的第一轮之后(此行之后:mutableQueue.prush(*vertexIterator);

输出为:

0的原始优先级,0 641添加的索引:0索引添加的调用方:01,0 40的原始优先级添加的索引:1索引添加的调用方:10、1 400的原始优先级添加的索引:2索引添加的调用方:21,1 664的原始优先级添加的索引:3索引添加的调用者:0

我不明白为什么最后一个索引在push()中是3函数,但当我从调用方查询它时为0?

当我在update()函数中看到相同的东西时index_in_heap似乎只是返回了垃圾。也就是说,我看着size_type index的值=get(index_in_heap,v);在update()中,以及当使用顶点(0,0)调用时,"index"的值为4294967295(当我预计它在[0,3]范围内时)。

有人能解释一下吗?也许我设置的index_in_heap映射不正确?

当您只更改节点的优先级时,优先级队列不会更新其结构。插入节点后,需要考虑其优先级常数。如果需要更新优先级,则需要将此情况告知优先级队列。为此,您需要告诉它哪个节点获得了什么新的优先级。

不幸的是,跟踪某种节点标识和优先级会使优先级队列变慢:对于d-heap,必须跟踪节点移动的位置,这使得更新相对昂贵。对于基于节点的堆,例如斐波那契堆,节点保持不变,但维护成本往往更高(斐波那奇堆具有有趣的理论复杂性,然而,这只对不切实际的问题很重要)。尽管我实现了书中描述的所有优先级队列方法,但我还没有找到任何中间立场。

d_ary_heap_indirect设计为只允许增加优先级。如果在update()和push_or_update()函数中更改:

preserve_heap_perty_up(索引);

preserve_heap_perty_up(索引);preserve_heap_property_down();

它似乎允许在保持队列排序的同时增加或减少优先级。