您的位置:首页 > 其它

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,否则学习它的时间比自己写一个还要多。当然如果你会用那是很好的。第三,上手难度过大。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: