BGL Dijkstra最短路径算法与捆绑属性

3
我正在尝试在BGL中使用Dijkstra最短路径算法来计算无权无向图上的简单ST路径。将来可能会关注边权重,但现在我只想将边遍历视为统一成本。
我还跟踪多个边缘和顶点属性,因此我所做的基于bundled properties example,该示例似乎是我要尝试的最接近的示例。
现在我正在尝试弄清楚如何使dijkstra工作,以便我可以进行我的ST搜索,但我卡在了为其设置正确参数上。
这是我目前拥有的代码的简化示例:
#include <iostream>
#include <vector>

#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>

// Create a struct to hold properties for each vertex
typedef struct VertexProperties
{
  int p1;
} VertexProperties;

// Create a struct to hold properties for each edge
typedef struct EdgeProperties
{
  int   p1;
} EdgeProperties;

// Define the type of the graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties> Graph;


int main(int,char*[])
{
  // Create a graph object
  Graph g;

  // Add vertices
  Graph::vertex_descriptor v0 = boost::add_vertex(g);
  Graph::vertex_descriptor v1 = boost::add_vertex(g);
  Graph::vertex_descriptor v2 = boost::add_vertex(g);

  // Set vertex properties
  g[v0].p1 = 1;
  g[v1].p1 = 2;
  g[v2].p1 = 3;

  // Add edges
  std::pair<Graph::edge_descriptor, bool> e01 = boost::add_edge(v0, v1, g);
  std::pair<Graph::edge_descriptor, bool> e02 = boost::add_edge(v1, v2, g);

  // Set edge properties
  g[e01.first].p1 = 1;
  g[e02.first].p1 = 2;

  std::cout << "num_verts: " << boost::num_vertices(g) << std::endl;
  std::cout << "num_edges: " << boost::num_edges(g) << std::endl;

  // compute ST shortest paths here...

  return 0;
}

我在调用Dijkstra算法时,对正确的参数感到困惑。它们需要图形、起始顶点以及前任映射和距离映射。到目前为止,我看到的例子,比如this one,只设置了边权重而没有捆绑边属性,这简化了事情。
最终,我需要ST最短路径,因此需要从S到T恢复路径。从事情的外观来看,我们需要设置一个前任映射,然后我们可以使用它从特定的T回到S中提取路径?
我还应该注意,我所处的环境不允许使用C++11语言功能。:(
非常感谢您在这里提供的任何帮助!

有趣的是询问如何调用dijkstra函数(几乎完整引用文档:)但不展示你所拥有的。我仍然很好奇。无论如何,希望我的答案能给你一些启示。 - sehe
谢谢提供的示例。它的确切形式对我来说无法编译,但我认为它可以给我一些想法。关于“展示我所拥有的”的意思,我不确定是否指展示答案?最终,我正在尝试进行ST搜索,并且我想从S到T恢复最短路径。根据我所了解的,BGL使用前任映射来实现这一点。我忘记提到我也不在使用C++11的环境中,因此使用auto会遗漏我需要前进的许多信息:( - TxAG98
我是说,你的代码实际上并没有尝试调用dijkstra :) 恢复显式类型也不太难:https://www.livecoding.tv/video/making-my-boost-graph-answer-c03-compliant/(在答案中发布了代码) - sehe
啊啊啊...我只是在注释中标记了我想要计算的位置,因为我想留下一些可以编译的东西,而我的之前尝试并没有产生可编译的代码。我想公司这里的防火墙对你上传的流不太满意。我回家后会看一下 :) 谢谢! - TxAG98
2个回答

6
所以问题是“如何使用捆绑属性作为Boost Graph Library的权重映射?”。很好,你可以使用属性映射。可以使用文档页面上记录的一些奇怪的语法访问捆绑属性:“Bundled Properties”页面: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/bundles.html,请参阅标题“从捆绑属性创建属性映射”。现在进行一个快速演示:
// set up a weight map:
auto weights = boost::get(&EdgeProperties::p1, g);

传递最少数量的参数到迪杰斯特拉算法:
// you can pass it to dijkstra using direct or named params. Let's do the simplest
boost::dijkstra_shortest_paths(g, v0, boost::no_named_parameters() .weight_map(weights));

你可能需要添加更多的参数,但是嘿,这是你的开始 :) 在Coliru上实时运行
#include <iostream>
#include <vector>

#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_utility.hpp>

// Create a struct to hold properties for each vertex
struct VertexProperties { int p1; };

// Create a struct to hold properties for each edge
struct EdgeProperties { int p1; };

// Define the type of the graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties> Graph;

int main() {
    // Create a graph object
    Graph g;

    // Add vertices
    auto v0 = boost::add_vertex({1}, g),
         v1 = boost::add_vertex({2}, g),
         v2 = boost::add_vertex({3}, g);

    // Add edges
    boost::add_edge(v0, v1, EdgeProperties{1}, g);
    boost::add_edge(v1, v2, EdgeProperties{2}, g);

    boost::print_graph(g, boost::get(&VertexProperties::p1, g));

    // set up a weight map:
    auto weights = boost::get(&EdgeProperties::p1, g);

    // you can pass itprint_graph`enter code here` to dijkstra using direct or named params. Let's do the simplest
    boost::dijkstra_shortest_paths(g, v0, boost::no_named_parameters() .weight_map(weights));
}

您会注意到,我简化了顶点/边属性的初始化。如果您想要了解图形的“外观”(除了使用Graphviz之外),则print_graph工具非常方便。

在Coliru上的输出如下:

1 <--> 2 
2 <--> 1 3 
3 <--> 2 

一个 C++03 证明版本的演示代码:**在 Coliru 上运行**。 - sehe
谢谢!这真的帮了很多。我添加了一个答案,列出了我试图组合的搜索的最终版本。希望对某人有用...可能有更好的方法来调用dijkstra_shortest_paths,但它已经完成了工作,所以对我有效... - TxAG98

3

我正在添加一个“完成”的版本的Dijkstra最短路径搜索,用于档案目的,计算从S到T的最短路径。

我确信有更好的“boost”方法来做到这一点,但它在我的端上运行良好。

http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/bundles.html是一个非常有帮助的链接。

///
/// @file bgl_st_example.cpp
///
/// @brief bundled property example
///
/// @ref http://programmingexamples.net/wiki/CPP/Boost/BGL/BundledProperties
///
#include <iostream>
#include <vector>

#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_utility.hpp>

// Create a struct to hold properties for each vertex
typedef struct vertex_properties
{
  std::string label;
  int p1;
} vertex_properties_t;


// Create a struct to hold properties for each edge
typedef struct edge_properties
{
  std::string label;
  int   p1;
  int   weight;
} edge_properties_t;


// Define the type of the graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, vertex_properties_t, edge_properties_t> graph_t;
typedef graph_t::vertex_descriptor vertex_descriptor_t;
typedef graph_t::edge_descriptor   edge_descriptor_t;
typedef boost::property_map<graph_t, boost::vertex_index_t>::type index_map_t;
typedef boost::iterator_property_map<vertex_descriptor_t*, index_map_t*, vertex_descriptor_t, vertex_descriptor_t&> predecessor_map_t;

//  The graph, with edge weights labeled.
//
//   v1  --(1)--  v2
//   |  \_        |
//   |    \       |
//  (1)    (3)   (2)
//   |        \_  |
//   |          \ |
//   v4  --(1)--  v3
//
//

int main(int,char*[])
{
  // Create a graph object
  graph_t g;

  // Add vertices
  vertex_descriptor_t v1 = boost::add_vertex(g);
  vertex_descriptor_t v2 = boost::add_vertex(g);
  vertex_descriptor_t v3 = boost::add_vertex(g);
  vertex_descriptor_t v4 = boost::add_vertex(g);

  // Set vertex properties
  g[v1].p1 = 1;  g[v1].label = "v1";
  g[v2].p1 = 2;  g[v2].label = "v2";
  g[v3].p1 = 3;  g[v3].label = "v3";
  g[v4].p1 = 4;  g[v4].label = "v4";

  // Add edges
  std::pair<edge_descriptor_t, bool> e01 = boost::add_edge(v1, v2, g);
  std::pair<edge_descriptor_t, bool> e02 = boost::add_edge(v2, v3, g);
  std::pair<edge_descriptor_t, bool> e03 = boost::add_edge(v3, v4, g);
  std::pair<edge_descriptor_t, bool> e04 = boost::add_edge(v4, v1, g);
  std::pair<edge_descriptor_t, bool> e05 = boost::add_edge(v1, v3, g);

  // Set edge properties
  g[e01.first].p1 = 1;     g[e01.first].weight = 1;     g[e01.first].label = "v1-v2";
  g[e02.first].p1 = 2;     g[e02.first].weight = 2;     g[e02.first].label = "v2-v3";
  g[e03.first].p1 = 3;     g[e03.first].weight = 1;     g[e03.first].label = "v3-v4";
  g[e04.first].p1 = 4;     g[e04.first].weight = 1;     g[e04.first].label = "v4-v1";
  g[e05.first].p1 = 5;     g[e05.first].weight = 3;     g[e05.first].label = "v1-v3";

  // Print out some useful information
  std::cout << "Graph:" << std::endl;
  boost::print_graph(g, boost::get(&vertex_properties_t::label,g));
  std::cout << "num_verts: " << boost::num_vertices(g) << std::endl;
  std::cout << "num_edges: " << boost::num_edges(g) << std::endl;


  // BGL Dijkstra's Shortest Paths here...
  std::vector<int> distances( boost::num_vertices(g));
  std::vector<vertex_descriptor_t> predecessors(boost::num_vertices(g));

  boost::dijkstra_shortest_paths(g, v1,
                                 boost::weight_map(boost::get(&edge_properties_t::weight,g))
                                 .distance_map(boost::make_iterator_property_map(distances.begin(), boost::get(boost::vertex_index,g)))
                                 .predecessor_map(boost::make_iterator_property_map(predecessors.begin(), boost::get(boost::vertex_index,g)))
                                 );

  // Extract the shortest path from v1 to v3.
  typedef std::vector<edge_descriptor_t> path_t;
  path_t path;

  vertex_descriptor_t v = v3;
  for(vertex_descriptor_t u = predecessors[v]; u != v; v=u, u=predecessors[v])
  {
    std::pair<edge_descriptor_t,bool> edge_pair = boost::edge(u,v,g);
    path.push_back( edge_pair.first );
  }

  std::cout << std::endl;
  std::cout << "Shortest Path from v1 to v3:" << std::endl;
  for(path_t::reverse_iterator riter = path.rbegin(); riter != path.rend(); ++riter)
  {
    vertex_descriptor_t u_tmp = boost::source(*riter, g);
    vertex_descriptor_t v_tmp = boost::target(*riter, g);
    edge_descriptor_t   e_tmp = boost::edge(u_tmp, v_tmp, g).first;

    std::cout << "  " << g[u_tmp].label << " -> " << g[v_tmp].label << "    (weight: " << g[e_tmp].weight << ")" << std::endl;
  }

  return 0;
}

这是一个适用于我的CMakeLists.txt文件:

cmake_minimum_required(VERSION 2.8)

project ( bgl_example )

find_package( Boost REQUIRED COMPONENTS  )

include_directories( ${Boost_INCLUDE_DIR} )

add_executable( bgl_st_example  bgl_st_example.cpp)
target_link_libraries( bgl_st_example ${Boost_LIBRARIES} )

我看到的输出结果是:
Graph:
v1 <--> v2 v4 v3
v2 <--> v1 v3
v3 <--> v2 v4 v1
v4 <--> v3 v1
num_verts: 4
num_edges: 5

Shortest Path from v1 to v3:
  v1 -> v4    (weight: 1)
  v4 -> v3    (weight: 1)

我对你的示例进行了重构,使之更符合现代C++的写法。请注意,在选择图模型时,它可能会更简单: https://godbolt.org/z/P5hvGjPh3。这也避免了从前置节点到边描述符(代价高昂)的转换,并在打印循环中再次执行此操作(其中已经具有边描述符)。 - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接