美文网首页
BGL(boost graph Library)(0-7)

BGL(boost graph Library)(0-7)

作者: vaisy | 来源:发表于2017-05-16 09:08 被阅读0次

    BGL官网入口
    Boost库说明


    BGL的用途是给某些图的结构提供接口,而隐藏其内部细节
    不需要built,但是编译程序一定要--optimized
    三种方式得到STL:

    • 算法/数据结构
      每种算法都以数据结构无关的方式写,允许在不同类的容器中使用。代码量为O(m+n),m是算法数,n是容器数。
    • 函数对象扩展
      用户可以通过函数对象自行修改STL。
    • 元素类型参数化
      像std::list<T>,
      BGL采用了类似STL的方式
    • 算法/数据结构互通
      首先,将BGL的图形算法写入一个可以抽出特定图形数据结构细节的接口。 像STL一样,BGL使用迭代器来定义数据结构遍历的接口。 有三种不同的图形遍历模式:遍历图形中的所有顶点,遍历所有边缘,并沿着图形的相邻结构(从顶点到其每个邻居)。 每个遍历模式都有独立的迭代器
      该通用接口允许诸如breadth_first_search()之类的模板函数可以处理各种图形数据结构,从使用指针链接节点的图形到以数组编码的图形。 这种灵活性在图形领域尤为重要。 图形数据结构通常是针对特定应用程序定制的。 传统上,如果程序员想要重用算法实现,他们必须将它们的图形数据转换/复制到图形库的规定的图形结构中。 图书馆如LEDA,GTL,Stanford GraphBase; 在Fortran中编写的图形算法尤其如此。 这严重限制了图形算法的重用。
      相比之下,使用外部自适应的BGL通用图形算法可以按照原样使用定制(或甚至遗留的)图形结构(参见“如何将现有图形转换为BGL”)。 外部自适应围绕数据结构包装一个新界面,而不需要复制,而不将数据放在适配器对象内。 BGL界面经过精心设计,使之适应性变得容易。 为了证明这一点,我们在BGL图算法中建立了使用各种图形结构(LEDA图,Stanford GraphBase图,甚至Fortran样式阵列)的接口代码。
    • 通过访客实现扩展
      BGL的图算法是可扩展的。 BGL引入了一个访问者的概念,它只是一个具有多种方法的函数对象。 在图形算法中,通常有几个关键的“事件点”可用于插入用户定义的操作。 访问对象具有在每个事件点调用的不同方法。 特定的事件点和相应的访问者方法取决于具体的算法。 它们通常包括start_vertex(),discover_vertex(),inspect_edge(),tree_edge()和finish_vertex()等方法。
    • 顶点和边缘属性多参数化
      BGL通用的第三种方法类似于STL容器中的元素类型的参数化,但是对于图形来说,故事再复杂一些。 我们需要将值(称为“属性”)与图形的顶点和边缘相关联。 另外,通常需要将多个属性与每个顶点和边缘相关联; 这是我们通过多参数化的意思。 STL std :: list <T>类的元素类型有一个参数T。 类似地,BGL图类具有顶点和边缘“属性”的模板参数。 属性指定属性的参数化类型,并为属性分配标识标签。 该标签用于区分边缘或顶点可能具有的多个属性。 附加到特定顶点或边缘的属性值可以通过属性映射获取。 每个属性都有一个单独的属性映射。
      传统图形库和图形结构在图形属性的参数化方面下降。 这是图表数据结构必须为应用程序定制的主要原因之一。 BGL图类中属性的参数化使它们非常适合重复使用。

    算法

    核心算法模式和一系列图算法:
    核心算法模式:

    • Breadth First Search
    • Depth First Search
    • Uniform Cost Search
      BGL不会用核心算法模式在图上计算数值,只是为图算法构建块。而图算法包括以下:
    • Dijkstra's Shortest Paths
    • Bellman-Ford Shortest Paths
    • Johnson's All-Pairs Shortest Paths
    • Kruskal's Minimum Spanning Tree
    • Prim's Minimum Spanning Tree
    • Connected Components
    • Strongly Connected Components
    • Dynamic Connected Components (using Disjoint Sets)
    • Topological Sort
    • Transpose
    • Reverse Cuthill Mckee Ordering
    • Smallest Last Vertex Ordering
    • Sequential Vertex Coloring

    数据结构

    提供两种图类型一种边列表适配器
    邻接表;邻接矩阵;边列表;
    其中最通用的是邻接表;高度参数化,可以针对不同情况优化

    int main(int,char*[])
      {
        // create a typedef for the Graph type
        typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;//使用vector来存出边和顶点集的数据结构,双向
    
        // Make convenient labels for the vertices
        enum { A, B, C, D, E, N };
        const int num_vertices = N;
        const char* name = "ABCDE";
    
        // writing out the edges in the graph
        typedef std::pair<int, int> Edge;
        Edge edge_array[] = 
        { Edge(A,B), Edge(A,D), Edge(C,A), Edge(D,C),
          Edge(C,E), Edge(B,D), Edge(D,E) };
        const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);
    
        // declare a graph object
        Graph g(num_vertices);
    
        // add the edges to the graph object
        for (int i = 0; i < num_edges; ++i)
          add_edge(edge_array[i].first, edge_array[i].second, g);
        ...
        return 0;
      }
    

    除了使用add_edge()接口,也可以使用迭代器:

    Graph g(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge), num_vertices);`
    

    此外,点数也是不固定的,可以使用 add_vertex() & remove_vertex()
    边集访问:edges()返回的迭代器,source()和target()为被该边连接的点

      int main(int,char*[])
      {
        // ...
        std::cout << "edges(g) = ";
        graph_traits<Graph>::edge_iterator ei, ei_end;
        for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
    //tie接口将返回的pair分离为两个变量。在本例中为ei和ei_end;
            std::cout << "(" << index[source(*ei, g)] 
                      << "," << index[target(*ei, g)] << ") ";
        std::cout << std::endl;
        // ...
        return 0;
      }
    

    邻接结构:
    std::for_each(vertices(g).first, vertices(g).second, exercise_vertex<Graph>(g));
    使用 exercise_vertex是为了在运行遍历时保留对图像的引用。将其模板化以便在不同的图类型中重用

    顶点描述符是由每个图形类型提供的,可用于通过以下部分中描述的out_edges(),in_edges(),adjacent_vertices()和属性映射函数来访问有关图形的信息。 vertex_descriptor类型通过graph_traits类获得。下面使用的typename关键字是必需的,因为scope :: operator(graph_traits <Graph>类型)左侧的类型取决于模板参数(Graph类型)。这是我们如何定义函子的应用方法:

      template <class Graph> struct exercise_vertex {
        //...
        typedef typename graph_traits<Graph>
          ::vertex_descriptor Vertex;
        void operator()(const Vertex& v) const
        {
          //...
        }
        //...
      };
    

    边描述:out_edges()函数,两个参数:定点和图对象。返回pair迭代器,提供该顶点所有出边的接口,出边迭代器dereference其中之一给出边描述符对象。(类似顶点描述符)

      template <class Graph> struct exercise_vertex {
        //...
        void operator()(const Vertex& v) const
        {
          typedef graph_traits<Graph> GraphTraits;
          typename property_map<Graph, vertex_index_t>::type index = get(vertex_index, g);
    
          std::cout << "out-edges: ";
          typename GraphTraits::out_edge_iterator out_i, out_end;
          typename GraphTraits::edge_descriptor e;
          for (boost::tie(out_i, out_end) = out_edges(v, g); 
               out_i != out_end; ++out_i) {
            e = *out_i;
            Vertex src = source(e, g), targ = target(e, g);
            std::cout << "(" << index[src] << "," 
                      << index[targ] << ") ";
          }
          std::cout << std::endl;
          //...
        }
        //...
      };
    

    in_edges()与之类似,(前提是双向图,且仅用于邻接表结构)
    有时,算法不需要查看图形的边,只关心顶点。 因此,图形界面还包括AdjacencyGraph接口的adjacent_vertices()函数,它提供对相邻顶点的直接访问。 此函数返回一对邻接迭代器。 dereference邻接迭代器给出相邻顶点的顶点描述符。
    涂色:加权。由于一般一个算法只用到一个属性,因此属性和图对象应当分别存储.属性有两类:内部存储属性/外部存储属性.内部属性通常采用模板插件,外部属性方法较多.直接存储的方法:点或者边映射的映射.对于vecS(即vector,参见某章),这种映射型容器会自己分配索引,用vertex_index_t来查找,而边不可以(废话太多了不就加个索引么)
    用访问者扩展算法
    一般来讲lib中的算法足够使用,但很有可能不完全相同.比如Dij算法通常记录最短距离,但是有可能我们需要用到最短路径树.一种方法是在最短路径树中记录每个节点的前导.
    在STL中,这种计算通过functor实现.这是每个算法的可选参数.在BGL中这个由游客来实现.一个游客就是一个functor,它有多种方法被调用
    (会在vistor章节详细介绍.)每一个方法都可以在算法的某一个点被调用.BGL提供了一些常用任务的访问者.可以自己创建BGL到visitor,本处先介绍前缀记录的实现和使用.以Dijkstra为例,创建一个Dijkstra visitor
    前缀记录访问者的功能分为两部分.为了存储可访问前缀属性,我们使用属性映射.而后,前缀访问者只对要记录的父负责.创建了一个record_predecessor属性类及模板在前缀属性映射上,由于这个访问者只有一种方法,我们继承dijkstra_visitor,为其余提供空白方法.构造函数将使用属性映射对象并将其保存在数据成员中.
    回顾:m_开头说明是类成员变量,构造函数的意图是把p存进m_这个protected里去.
    然后又是一个模板函数

      template <class PredecessorMap>
      class record_predecessors : public dijkstra_visitor<>
      {
      public:
        record_predecessors(PredecessorMap p)
          : m_predecessor(p) { }
    
        template <class Edge, class Graph>
        void edge_relaxed(Edge e, Graph& g) {
          // set the parent of the target(e) to source(e)
          put(m_predecessor, target(e, g), source(e, g));
        }
      protected:
        PredecessorMap m_predecessor;
      };
    

    将source记录为目标顶点的前身,每次relax时覆盖之前的值,使用put来记录前缀.访客的edge_filter通知算法什么时候启用explore(),该情况下我们只在最短路径树中通知边所以制定tree_edge_tag.
    为了方便的创建前缀访问,启用helper如下

    //类模板外函数?总之定义了make_,返回了record_的结果
      template <class PredecessorMap>
      record_predecessors<PredecessorMap>
      make_predecessor_recorder(PredecessorMap p) {
        return record_predecessors<PredecessorMap>(p);
      }
    

    现在可以像下文一样使用:

    vector<Vertex> p(num_vertices(G), graph_traits<G>::null_vertex());
     //容器(大小,迭代器)
      dijkstra_shortest_paths(G, s, distance_map(&d[0]). 
                              visitor(make_predecessor_recorder(&p[0])));
    //计算最短路径,添加visitor并存储 参数是数组的位置
      cout << "parents in the tree of shortest paths:" << endl;
      for(vi = vertices(G).first; vi != vertices(G).second; ++vi) {
        cout << "parent(" << *vi;
        if (p[*vi] == graph_traits<G>::null_vertex())
          cout << ") = no parent" << endl; 
        else 
          cout << ") = " << p[*vi] << endl;
      }
    

    邻接矩阵:一般用于密集图表
    边列表:可以使用任意边迭代和实现

    相关文章

      网友评论

          本文标题:BGL(boost graph Library)(0-7)

          本文链接:https://www.haomeiwen.com/subject/jnacxxtx.html