Boost Graph Library 学习笔记
2009-05-22 15:34
495 查看
最近研究了一下boost graph library(简称BGL)。由于文档的过于简略和使用者数量较少,一时半会弄明白还不是那么简单的一件事情。网上也有不少入门文章,这些我就不再说了,主要说一下我遇到的一些问题。
1. descriptor 和 iterator的区别
BGL里有2种迭代器性质的东西,分别是descriptor和iterator。在STL里只有一种迭代器,就是iterator。但是在BGL里,多出了一种叫descriptor的东西。原因在于,图是关系型的数据结构,是非线性的,要比STL里的线性容器复杂。在STL设计中,iterator就是作用于线性容器。在BGL里保留了iterator可以说是一种习惯吧。但是光有iterator的图结构是很复杂的,原因就在于iterator失效这么一个问题。在STL里,删除一个vector中的一个元素会使此元素后面的iterator失效。当然,这种失效在线性容器中还算可以容忍。但是在图里就不行了,考虑如下例子:3个点3条边的一个图的构建,按照BGL的方式首先定义出图类型G,然后定义G的对象g。首先我们加2个点进去:1,3两个点。然后通过add_edge加边。比如在1,3两点之间加边就是add_edge(1,3)。然后再加点2,再在2,3两点之间加边add_edge(2,3)。这里的参数都是vertex_descriptor,而不是vertex_iterator。如果是iterator,在你调用add_edge(2,3)时,这个3所代表的iterator可能是已经失效的!(因为在加点2的时候,3的位置往后移动了(假设是vector))这就是descriptor存在的意义。实际上,在BGL实现里面,descriptor的类型是size_t。
另外,iterator和descriptor是无法相互转换的。
2. 算法的输出
最短路算法的输出是顶点序列。但如果这个图含有平行边,顶点序列是不够的。如何让算法输出边序列?下面是源代码,具体细节就不细说了,看代码一般能看出来。
template <class PredecessorMap>
class record_edge_predecessors : public dijkstra_visitor<>
{
public:
record_edge_predecessors(PredecessorMap p)
: m_predecessor(p){ }
template <class Edge, class Graph>
void edge_relaxed(Edge e, Graph& g) {
put(m_predecessor, target(e, g), e);
}
protected:
PredecessorMap m_predecessor;
};
template <class PredecessorMap>
record_edge_predecessors<PredecessorMap> make_edge_predecessor_recorder(PredecessorMap p)
{
return record_edge_predecessors<PredecessorMap>(p);
}
template<typename EdgePredecessorMap, typename VertexPredecessorMap, typename EdgeIndexMap, typename VertexDescriptor>
std::vector<typename property_traits<EdgeIndexMap>::value_type> translate_to_edges_index(const EdgePredecessorMap& em, const VertexPredecessorMap& vm, const EdgeIndexMap& im, VertexDescriptor dest)
{
std::vector<typename property_traits<EdgeIndexMap>::value_type> res;
while(vm[dest] != dest)
{
res.push_back(im[em[dest]]);
dest = vm[dest];
}
return res;
}
template<typename EdgePredecessorMap, typename VertexPredecessorMap, typename VertexDescriptor, typename EdgeDescriptor>
std::vector<EdgeDescriptor> translate_to_edges_descriptor(const EdgePredecessorMap& em, const VertexPredecessorMap& vm, VertexDescriptor dest, EdgeDescriptor para_deduce_helper)
{
std::vector<EdgeDescriptor> res;
while(vm[dest] != dest)
{
res.push_back(em[dest]);
dest = vm[dest];
}
return res;
}
make_edge_predecessor_recorder是一个helper函数。另外2个函数转换函数,因为通过visitor记录的是顶点和边的关系。如何通过visitor直接得到边序列我没有写出来。translate_to_edges_descriptor最后一个参数是一个帮助推导descriptor类型的。
下面是一个使用例子:
typedef boost::adjacency_list<vecS, vecS, bidirectionalS, no_property, property<edge_weight_t, int, property<edge_index_t, int> > > G;
G g(4);
graph_traits<G>::edge_iterator vi, vi_end;
add_edge(1,2,g); // 1
add_edge(1,3,g); // 2
add_edge(1,4,g); // 3
add_edge(2,4,g); // 4
add_edge(3,4,g); // 5
add_edge(2,1,g); // 6
property_map<G, edge_weight_t>::type weightmap = get(edge_weight, g);
property_map<G, edge_index_t>::type indexmap = get(edge_index, g);
tie(vi, vi_end) = edges(g);
weightmap[*vi] = 1;
indexmap[*vi] = 1;
++vi;
weightmap[*vi] = 1;
indexmap[*vi] = 2;
++vi;
weightmap[*vi] = 3;
indexmap[*vi] = 3;
++vi;
weightmap[*vi] = 5;
indexmap[*vi] = 4;
++vi;
weightmap[*vi] = 1;
indexmap[*vi] = 5;
++vi;
weightmap[*vi] = 1;
indexmap[*vi] = 6;
std::vector<G::vertex_descriptor> p(num_vertices(g));
std::vector<G::edge_descriptor> q(num_edges(g));
graph_traits<G>::vertex_descriptor s = vertex(2, g);
dijkstra_shortest_paths(g, s, predecessor_map(&p[0]).visitor(make_edge_predecessor_recorder(&q[0])));
std::vector<G::edge_descriptor> eis = translate_to_edges_descriptor(q, p, 4, G::edge_descriptor());
for(int i = 0; i < eis.size(); ++i)
{
std::cout<<eis[i];
}
光写着几个函数就花了我不少时间,开源代码的通病,文档不详细造成的。
3. BGL是否通用
很遗憾,BGL只适合纯粹的图论问题。如果你需要算法在找路时根据属性和规则去定义dijkstra中的特定步骤,BGL是无能为力的。虽然BGL提供了完善的visitor但是注意这些visitor并不能改变算法的行为,这和STL的functor是不一样的。所有visitor的返回值都是void,它们不能给算法提供任何信息。
所以BGL不适合一些较为复杂的场合,但是通过入一种能够修改算法特定步骤行为的visitor也是有可能的做到的。只是BGL很久都没有更新了,估计不会有大的变化了。还有不得不提的是BGL这种框架过于复杂,比STL要复杂多了,对C++模板不熟悉的人可能根本就摸不到头脑。很多的可定制属性必然会使的自定义扩展在类型推导上过于复杂(在我写的那几个小函数里就可以说明这一点)。总之,BGL还是小众的东西,不会像STL那样。第一,图这种结构需求量少。第二,小场合的应用还用不着使用BGL,否则学习它的时间比自己写一个还要多。当然如果你会用那是很好的。第三,上手难度过大。
1. descriptor 和 iterator的区别
BGL里有2种迭代器性质的东西,分别是descriptor和iterator。在STL里只有一种迭代器,就是iterator。但是在BGL里,多出了一种叫descriptor的东西。原因在于,图是关系型的数据结构,是非线性的,要比STL里的线性容器复杂。在STL设计中,iterator就是作用于线性容器。在BGL里保留了iterator可以说是一种习惯吧。但是光有iterator的图结构是很复杂的,原因就在于iterator失效这么一个问题。在STL里,删除一个vector中的一个元素会使此元素后面的iterator失效。当然,这种失效在线性容器中还算可以容忍。但是在图里就不行了,考虑如下例子:3个点3条边的一个图的构建,按照BGL的方式首先定义出图类型G,然后定义G的对象g。首先我们加2个点进去:1,3两个点。然后通过add_edge加边。比如在1,3两点之间加边就是add_edge(1,3)。然后再加点2,再在2,3两点之间加边add_edge(2,3)。这里的参数都是vertex_descriptor,而不是vertex_iterator。如果是iterator,在你调用add_edge(2,3)时,这个3所代表的iterator可能是已经失效的!(因为在加点2的时候,3的位置往后移动了(假设是vector))这就是descriptor存在的意义。实际上,在BGL实现里面,descriptor的类型是size_t。
另外,iterator和descriptor是无法相互转换的。
2. 算法的输出
最短路算法的输出是顶点序列。但如果这个图含有平行边,顶点序列是不够的。如何让算法输出边序列?下面是源代码,具体细节就不细说了,看代码一般能看出来。
template <class PredecessorMap>
class record_edge_predecessors : public dijkstra_visitor<>
{
public:
record_edge_predecessors(PredecessorMap p)
: m_predecessor(p){ }
template <class Edge, class Graph>
void edge_relaxed(Edge e, Graph& g) {
put(m_predecessor, target(e, g), e);
}
protected:
PredecessorMap m_predecessor;
};
template <class PredecessorMap>
record_edge_predecessors<PredecessorMap> make_edge_predecessor_recorder(PredecessorMap p)
{
return record_edge_predecessors<PredecessorMap>(p);
}
template<typename EdgePredecessorMap, typename VertexPredecessorMap, typename EdgeIndexMap, typename VertexDescriptor>
std::vector<typename property_traits<EdgeIndexMap>::value_type> translate_to_edges_index(const EdgePredecessorMap& em, const VertexPredecessorMap& vm, const EdgeIndexMap& im, VertexDescriptor dest)
{
std::vector<typename property_traits<EdgeIndexMap>::value_type> res;
while(vm[dest] != dest)
{
res.push_back(im[em[dest]]);
dest = vm[dest];
}
return res;
}
template<typename EdgePredecessorMap, typename VertexPredecessorMap, typename VertexDescriptor, typename EdgeDescriptor>
std::vector<EdgeDescriptor> translate_to_edges_descriptor(const EdgePredecessorMap& em, const VertexPredecessorMap& vm, VertexDescriptor dest, EdgeDescriptor para_deduce_helper)
{
std::vector<EdgeDescriptor> res;
while(vm[dest] != dest)
{
res.push_back(em[dest]);
dest = vm[dest];
}
return res;
}
make_edge_predecessor_recorder是一个helper函数。另外2个函数转换函数,因为通过visitor记录的是顶点和边的关系。如何通过visitor直接得到边序列我没有写出来。translate_to_edges_descriptor最后一个参数是一个帮助推导descriptor类型的。
下面是一个使用例子:
typedef boost::adjacency_list<vecS, vecS, bidirectionalS, no_property, property<edge_weight_t, int, property<edge_index_t, int> > > G;
G g(4);
graph_traits<G>::edge_iterator vi, vi_end;
add_edge(1,2,g); // 1
add_edge(1,3,g); // 2
add_edge(1,4,g); // 3
add_edge(2,4,g); // 4
add_edge(3,4,g); // 5
add_edge(2,1,g); // 6
property_map<G, edge_weight_t>::type weightmap = get(edge_weight, g);
property_map<G, edge_index_t>::type indexmap = get(edge_index, g);
tie(vi, vi_end) = edges(g);
weightmap[*vi] = 1;
indexmap[*vi] = 1;
++vi;
weightmap[*vi] = 1;
indexmap[*vi] = 2;
++vi;
weightmap[*vi] = 3;
indexmap[*vi] = 3;
++vi;
weightmap[*vi] = 5;
indexmap[*vi] = 4;
++vi;
weightmap[*vi] = 1;
indexmap[*vi] = 5;
++vi;
weightmap[*vi] = 1;
indexmap[*vi] = 6;
std::vector<G::vertex_descriptor> p(num_vertices(g));
std::vector<G::edge_descriptor> q(num_edges(g));
graph_traits<G>::vertex_descriptor s = vertex(2, g);
dijkstra_shortest_paths(g, s, predecessor_map(&p[0]).visitor(make_edge_predecessor_recorder(&q[0])));
std::vector<G::edge_descriptor> eis = translate_to_edges_descriptor(q, p, 4, G::edge_descriptor());
for(int i = 0; i < eis.size(); ++i)
{
std::cout<<eis[i];
}
光写着几个函数就花了我不少时间,开源代码的通病,文档不详细造成的。
3. BGL是否通用
很遗憾,BGL只适合纯粹的图论问题。如果你需要算法在找路时根据属性和规则去定义dijkstra中的特定步骤,BGL是无能为力的。虽然BGL提供了完善的visitor但是注意这些visitor并不能改变算法的行为,这和STL的functor是不一样的。所有visitor的返回值都是void,它们不能给算法提供任何信息。
所以BGL不适合一些较为复杂的场合,但是通过入一种能够修改算法特定步骤行为的visitor也是有可能的做到的。只是BGL很久都没有更新了,估计不会有大的变化了。还有不得不提的是BGL这种框架过于复杂,比STL要复杂多了,对C++模板不熟悉的人可能根本就摸不到头脑。很多的可定制属性必然会使的自定义扩展在类型推导上过于复杂(在我写的那几个小函数里就可以说明这一点)。总之,BGL还是小众的东西,不会像STL那样。第一,图这种结构需求量少。第二,小场合的应用还用不着使用BGL,否则学习它的时间比自己写一个还要多。当然如果你会用那是很好的。第三,上手难度过大。
相关文章推荐
- 图论学习(一)使用Boost Graph Library表示图
- Boost Graph Library (BGL)学习:使用Bundled Properties
- 学习笔记--macos下boost安装与Xcode配置
- boost uuid 学习笔记
- STM32 USB Host Library 学习笔记 (2) USBH_InterruptSendData() USBH_ClrFeature()
- STM32 USB Host Library 学习笔记 (3) USBH_HandleControl()
- 学习笔记 Real COM with the MFC Library (译一)
- boost asio学习笔记1--Reactor与Proactor模式
- 使用C++ Boost Graph Library 进行社交网络分析入门篇
- boost.asio 学习笔记01——概述
- boost.asio 学习笔记07——总结
- Caffe学习笔记5-BLAS与boost::thread加速
- 媒体层图形技术之AssetsLibrary 学习笔记
- boost asio io_service学习笔记
- boost.asio 学习笔记 1-3
- Armadillo C++ linear algebra library 学习笔记(5)——矩阵的分解
- 动态链接库(Dynamic Link Library)学习笔记(附PE文件分析)
- 媒体层图形技术之AssetsLibrary 学习笔记
- V-rep学习笔记:Reflexxes Motion Library 2
- Armadillo C++ linear algebra library 学习笔记(4)——矩阵的运算