首页 文章

Boost :: graph Dijkstra和自定义对象和属性

提问于
浏览
5

我想在程序的其他部分使用boost 's dijkstra algorithm (since I' m . 我遇到的问题是将自定义对象(我相信它们被称为 property )添加到 adjacency_list .

基本上我有一个自定义边缘类,它维护有关边缘和通过它连接的顶点的各种信息 . 我想使用 adjaceny_list 所需的边缘属性存储我的自定义数据对象

我已经成功实现了boost provides的玩具示例 . 我试图使用自定义属性无济于事(boost exampleboost properties) . 我已经能够弄清楚如何将我的自定义数据结构包含到boost adjaceny_list 结构中 .

在我的情况下,我有以下程序:

Main.cpp的:

#include <iostream>
#include <fstream>
#include "dijkstra.h"
#include <vector>

int
main(int, char *[])
{
  // Generate the vector of edges from elsewhere in the program
  std::vector<VEdge*> edges; //someclass.get_edges();

  td* test = new td(edges);
  test->run_d();

  test->print_path();

  return EXIT_SUCCESS;
}

Dijkstra.cpp:

#include <iostream>
#include <fstream>
#include "dijkstra.h"

using namespace boost;

td::td() {
    kNumArcs = sizeof(kEdgeArray) / sizeof(Edge);
    kNumNodes = 5;
}

td::td(std::vector<VEdge*> edges) {
    // add edges to the edge property here
    for(VEdge* e : edges) {
        // for each edge, add to the kEdgeArray variable in some way
        // The boost example forces the input to be an array of edge_property type.  
        // So here is where I will convert my raw VEdge data structure to 
        // the custom edge_property that I am struggling to understand how to create.
    }
    kNumArcs = sizeof(kEdgeArray) / sizeof(Edge);
    kNumNodes = 5;
}

void td::run_d() {
    kGraph = graph_t(kEdgeArray, kEdgeArray + kNumArcs, kWeights, kNumNodes);

    kWeightMap = get(edge_weight, kGraph);
    kP = std::vector<vertex_descriptor >(num_vertices(kGraph));
    kD = std::vector<int>(num_vertices(kGraph));
    kS = vertex(A, kGraph);

    dijkstra_shortest_paths(kGraph, kS,
            predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph))).
                    distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph))));
}

void td::print_path() {
    std::cout << "distances and parents:" << std::endl;
    graph_traits < graph_t >::vertex_iterator vi, vend;
    for (boost::tie(vi, vend) = vertices(kGraph); vi != vend; ++vi) {
        std::cout << "distance(" << kName[*vi] << ") = " << kD[*vi] << ", ";
        std::cout << "parent(" << kName[*vi] << ") = " << kName[kP[*vi]] << std::
        endl;
    }
}

void td::generate_dot_file() {
    std::cout << std::endl;

    std::ofstream dot_file("figs/dijkstra-eg.dot");

    dot_file << "digraph D {\n"
            << "  rankdir=LR\n"
            << "  size=\"4,3\"\n"
            << "  ratio=\"fill\"\n"
            << "  edge[style=\"bold\"]\n" << "  node[shape=\"circle\"]\n";

    graph_traits < graph_t >::edge_iterator ei, ei_end;
    for (boost::tie(ei, ei_end) = edges(kGraph); ei != ei_end; ++ei) {
        graph_traits < graph_t >::edge_descriptor e = *ei;
        graph_traits < graph_t >::vertex_descriptor
                u = source(e, kGraph), v = target(e, kGraph);
        dot_file << kName[u] << " -> " << kName[v]
                << "[label=\"" << get(kWeightMap, e) << "\"";
        if (kP[v] == u)
            dot_file << ", color=\"black\"";
        else
            dot_file << ", color=\"grey\"";
        dot_file << "]";
    }
    dot_file << "}";
}

Dijkstra.h:

#ifndef _TEMPD_H_
#define _TEMPD_H_

#pragma once

#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>

using namespace boost;

typedef adjacency_list < listS, vecS, directedS,
        no_property, property < edge_weight_t, int > > graph_t;
typedef graph_traits < graph_t >::vertex_descriptor vertex_descriptor;
typedef std::pair<int, int> Edge;

struct VEdge{
    // custom variables here
    VNode start;
    VNode end;
    int weight;
    int id;
    // other irrelevant data pertinent to my program that must be preserved
};

struct VNode {
    // custom variables here
    int x;
    int y;
    int id;
    // other irrelevant data pertinent to my program that must be preserved
}

enum nodes { A, B, C, D, E };

class td {
public:
    td();
    td(std::vector<VEdge*>);
    ~td();

    void run_d();

    void print_path();
    void generate_dot_file();
private:
    Edge kEdgeArray[9] = { Edge(A, C), Edge(B, B), Edge(B, D), Edge(B, E),
            Edge(C, B), Edge(C, D), Edge(D, E), Edge(E, A), Edge(E, B)
    };
    char kName[5] = {'A','B','C','D','E'};
    int kWeights[9] = { 1, 2, 1, 2, 7, 3, 1, 1, 1 };
    int kNumArcs;
    int kNumNodes;
    vertex_descriptor kS;
    graph_t kGraph;
    std::vector<int> kD;
    std::vector<vertex_descriptor> kP;
    property_map<graph_t, edge_weight_t>::type kWeightMap;
};
#endif

我知道我的例子有点做作,但它传达了我想要完成的事情 . 我知道我的 edge_descriptor 需要一个自定义数据结构,它被发送到 graph_t typedef .

所以我想改变我的Dijkstra.h文件看起来像这样:

struct vertex_label_t {vertex_property_tag kind;};
struct edge_label_t {edge_property_tag kind;};

typedef property <vertex_custom_t, VNode*>,
    property <vertex_label_t, string>,
        property <vertex_root_t, ing> > > vertex_p;

typedef property <edge_custom_t, VEdge*>,
    property <edge_label_t, string > > edge_p;

typedef adjacency_list < listS, vecS, directedS,
        vertex_p, edge_p > graph_t;
typedef graph_traits < graph_t >::vertex_descriptor vertex_descriptor;

1 回答

  • 7

    好的 . 自https://stackoverflow.com/questions/28889423/boost-adjacency-list-swap-errors-when-using-boost-dijkstra以来,你已经走了很长的路;样本是独立的,可以编译¹

    我想我可以连接一些点,希望这会有所帮助 .

    1.使用VEdge

    对于最简单的选项,我使用 Bundled Properties ,并按如下方式定义 VEdge

    struct VEdge {
        int id;
        int source, target;
        double weight;
        // custom variables here
    };
    

    现在,我们将图形定义为

    using graph_t = boost::adjacency_list<boost::listS, boost::vecS, 
                        boost::directedS, boost::no_property, VEdge>;
    using weight_map_t = boost::property_map<graph_t, double VEdge::*>::type;
    

    如您所见,权重图具有更复杂的类型,如Properties maps from bundled properties中所述 . 你可以得到实际的 Map :

    weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
    

    现在,让我们在 VEdge (A = 0 ... E = 4)的向量中重新创建问题中的测试数据:

    std::vector<VEdge> edges {
        { 2100, 0, 2, 1 },
        { 2101, 1, 1, 2 },
        { 2102, 1, 3, 1 },
        { 2103, 1, 4, 2 },
        { 2104, 2, 1, 7 },
        { 2105, 2, 3, 3 },
        { 2106, 3, 4, 1 },
        { 2107, 4, 0, 1 },
        { 2108, 4, 1, 1 },
    };
    
    test_dijkstra test(edges);
    

    构造函数有一点点复杂性,只能从边缘找到顶点的数量 . 我使用Boost Range算法来查找最大顶点节点id并传递:

    test_dijkstra::test_dijkstra(std::vector<VEdge> edges) {
        using namespace boost::adaptors;
    
        size_t max_node;
    
        boost::partial_sort_copy(
                edges | transformed([](VEdge const &e) -> size_t { return std::max(e.source, e.target); }),
                boost::make_iterator_range(&max_node, &max_node + 1),
                std::greater<size_t>());
    
        auto e = edges | transformed([](VEdge const &ve) { return std::make_pair(ve.source, ve.target); });
        kGraph = graph_t(e.begin(), e.end(), edges.begin(), max_node + 1);
    }
    

    注意如何传递 edges.begin() :它不是"forced to be a an array of edge_property type" . 迭代器会很好 .

    现在dijkstra需要得到 weight_map 参数,因为它不再是默认的内部属性:

    void test_dijkstra::run_dijkstra() {
    
        weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
    
        vertex_descriptor kS    = vertex(0, kGraph);
        kP                      = std::vector<vertex_descriptor>(num_vertices(kGraph));
        kD                      = std::vector<int>(num_vertices(kGraph));
    
        dijkstra_shortest_paths(
            kGraph, kS,
                predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph)))
            .distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph)))
            .weight_map(kWeightMap));
    }
    

    对于此示例,我将 A 转换为 0 作为起始顶点 . 结果路径与原始路径完全相同

    enter image description here

    完整计划

    Live On Coliru

    #include <boost/config.hpp>
    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/dijkstra_shortest_paths.hpp>
    #include <boost/graph/graph_traits.hpp>
    #include <boost/property_map/property_map.hpp>
    #include <boost/range/algorithm.hpp>
    #include <boost/range/adaptors.hpp>
    #include <fstream>
    #include <iostream>
    
    struct VEdge {
        int id;
        int source, target;
        double weight;
        // custom variables here
    };
    
    class test_dijkstra {
        using graph_t           = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property, VEdge>;
        using vertex_descriptor = boost::graph_traits<graph_t>::vertex_descriptor;
        using edge_descriptor   = boost::graph_traits<graph_t>::edge_descriptor;
        using weight_map_t      = boost::property_map<graph_t, double VEdge::*>::type;
    
      public:
        test_dijkstra(std::vector<VEdge>);
        ~test_dijkstra() {}
    
        void run_dijkstra();
    
        void print_path();
        void generate_dot_file();
    
      private:
        graph_t kGraph;
    
        std::vector<int> kD;
        std::vector<vertex_descriptor> kP;
    };
    
    test_dijkstra::test_dijkstra(std::vector<VEdge> edges) {
        using namespace boost::adaptors;
    
        size_t max_node;
    
        boost::partial_sort_copy(
                edges | transformed([](VEdge const &e) -> size_t { return std::max(e.source, e.target); }),
                boost::make_iterator_range(&max_node, &max_node + 1),
                std::greater<size_t>());
    
        auto e = edges | transformed([](VEdge const &ve) { return std::make_pair(ve.source, ve.target); });
        kGraph = graph_t(e.begin(), e.end(), edges.begin(), max_node + 1);
    }
    
    void test_dijkstra::run_dijkstra() {
    
        weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
    
        vertex_descriptor kS    = vertex(0, kGraph);
        kP                      = std::vector<vertex_descriptor>(num_vertices(kGraph));
        kD                      = std::vector<int>(num_vertices(kGraph));
    
        dijkstra_shortest_paths(
            kGraph, kS,
                predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph)))
               .distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph)))
               .weight_map(kWeightMap));
    }
    
    void test_dijkstra::print_path() {
        std::cout << "distances and parents:" << std::endl;
        boost::graph_traits<graph_t>::vertex_iterator vi, vend;
    
        for (boost::tie(vi, vend) = vertices(kGraph); vi != vend; ++vi) {
            std::cout << "distance(" << *vi << ") = " << kD[*vi] << ", ";
            std::cout << "parent(" << *vi << ") = " << kP[*vi] << "\n";
        }
    }
    
    void test_dijkstra::generate_dot_file() {
        weight_map_t kWeightMap = boost::get(&VEdge::weight, kGraph);
    
        std::ofstream dot_file("figs/dijkstra-eg.dot");
    
        dot_file << "digraph D {\n"
                 << "  rankdir=LR\n"
                 << "  size=\"4,3\"\n"
                 << "  ratio=\"fill\"\n"
                 << "  edge[style=\"bold\"]\n"
                 << "  node[shape=\"circle\"]\n";
    
        boost::graph_traits<graph_t>::edge_iterator ei, ei_end;
        for (boost::tie(ei, ei_end) = edges(kGraph); ei != ei_end; ++ei) {
            boost::graph_traits<graph_t>::edge_descriptor e = *ei;
            boost::graph_traits<graph_t>::vertex_descriptor u = source(e, kGraph), v = target(e, kGraph);
            dot_file << u << " -> " << v << "[label=\"" << get(kWeightMap, e) << "\"";
    
            if (kP[v] == u)
                dot_file << ", color=\"black\"";
            else
                dot_file << ", color=\"grey\"";
            dot_file << "]";
        }
        dot_file << "}";
    }
    
    int main() {
        std::vector<VEdge> edges {
            { 2100, 0, 2, 1 },
            { 2101, 1, 1, 2 },
            { 2102, 1, 3, 1 },
            { 2103, 1, 4, 2 },
            { 2104, 2, 1, 7 },
            { 2105, 2, 3, 3 },
            { 2106, 3, 4, 1 },
            { 2107, 4, 0, 1 },
            { 2108, 4, 1, 1 },
        };
    
        test_dijkstra test(edges);
        test.run_dijkstra();
    
        test.print_path();
        test.generate_dot_file();
    }
    

    2.使用VEdge *

    如果你坚持使用属性中的指针,一些事情会变得更复杂:

    • 您需要管理元素的生命周期

    • 你不能使用 double VEdge::* weight_map_t . 相反,您需要为此调整自定义属性映射:

    auto kWeightMap = boost::make_transform_value_property_map(
                [](VEdge* ve) { return ve->weight; },
                boost::get(boost::edge_bundle, kGraph)
            );
    
    • 从好的方面来说,您可以使用简写索引器表示法来评估 edge_descriptor 的边缘属性,如 generate_dot_file() 所示:
    dot_file << u << " -> " << v << "[label=\"" << kGraph[e]->weight << "\"";
    
    • 当然这种方法避免了将 VEdge 对象复制到包中,因此它可能更有效

    没有进一步的麻烦(并没有打扰内存泄漏):

    Live On Coliru

    #include <boost/config.hpp>
    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/dijkstra_shortest_paths.hpp>
    #include <boost/graph/graph_traits.hpp>
    #include <boost/property_map/property_map.hpp>
    #include <boost/range/algorithm.hpp>
    #include <boost/range/adaptors.hpp>
    
    #include <boost/property_map/transform_value_property_map.hpp>
    
    #include <fstream>
    #include <iostream>
    
    struct VEdge {
        int id;
        int source, target;
        double weight;
        // custom variables here
    };
    
    class test_dijkstra {
        using graph_t           = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property, VEdge*>;
        using vertex_descriptor = boost::graph_traits<graph_t>::vertex_descriptor;
        using edge_descriptor   = boost::graph_traits<graph_t>::edge_descriptor;
    
      public:
        test_dijkstra(std::vector<VEdge*>);
        ~test_dijkstra() {}
    
        void run_dijkstra();
    
        void print_path();
        void generate_dot_file();
    
      private:
        graph_t kGraph;
    
        std::vector<int> kD;
        std::vector<vertex_descriptor> kP;
    };
    
    test_dijkstra::test_dijkstra(std::vector<VEdge*> edges) {
        using namespace boost::adaptors;
    
        size_t max_node;
    
        boost::partial_sort_copy(
                edges | transformed([](VEdge const* e) -> size_t { return std::max(e->source, e->target); }),
                boost::make_iterator_range(&max_node, &max_node + 1),
                std::greater<size_t>());
    
        auto e = edges | transformed([](VEdge const *ve) { return std::make_pair(ve->source, ve->target); });
        kGraph = graph_t(e.begin(), e.end(), edges.begin(), max_node + 1);
    }
    
    void test_dijkstra::run_dijkstra() {
    
        auto kWeightMap = boost::make_transform_value_property_map(
                    [](VEdge* ve) { return ve->weight; },
                    boost::get(boost::edge_bundle, kGraph)
                );
    
        vertex_descriptor kS    = vertex(0, kGraph);
        kP                      = std::vector<vertex_descriptor>(num_vertices(kGraph));
        kD                      = std::vector<int>(num_vertices(kGraph));
    
        dijkstra_shortest_paths(
            kGraph, kS,
                predecessor_map(boost::make_iterator_property_map(kP.begin(), get(boost::vertex_index, kGraph)))
               .distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph)))
               .weight_map(kWeightMap));
    }
    
    void test_dijkstra::print_path() {
        std::cout << "distances and parents:" << std::endl;
        boost::graph_traits<graph_t>::vertex_iterator vi, vend;
    
        for (boost::tie(vi, vend) = vertices(kGraph); vi != vend; ++vi) {
            std::cout << "distance(" << *vi << ") = " << kD[*vi] << ", ";
            std::cout << "parent(" << *vi << ") = " << kP[*vi] << "\n";
        }
    }
    
    void test_dijkstra::generate_dot_file() {
    
        std::ofstream dot_file("figs/dijkstra-eg.dot");
    
        dot_file << "digraph D {\n"
                 << "  rankdir=LR\n"
                 << "  size=\"4,3\"\n"
                 << "  ratio=\"fill\"\n"
                 << "  edge[style=\"bold\"]\n"
                 << "  node[shape=\"circle\"]\n";
    
        boost::graph_traits<graph_t>::edge_iterator ei, ei_end;
        for (boost::tie(ei, ei_end) = edges(kGraph); ei != ei_end; ++ei) {
            boost::graph_traits<graph_t>::edge_descriptor e = *ei;
            boost::graph_traits<graph_t>::vertex_descriptor u = source(e, kGraph), v = target(e, kGraph);
            dot_file << u << " -> " << v << "[label=\"" << kGraph[e]->weight << "\"";
    
            if (kP[v] == u)
                dot_file << ", color=\"black\"";
            else
                dot_file << ", color=\"grey\"";
            dot_file << "]";
        }
        dot_file << "}";
    }
    
    int main() {
        std::vector<VEdge*> edges {
            new VEdge { 2100, 0, 2, 1 },
            new VEdge { 2101, 1, 1, 2 },
            new VEdge { 2102, 1, 3, 1 },
            new VEdge { 2103, 1, 4, 2 },
            new VEdge { 2104, 2, 1, 7 },
            new VEdge { 2105, 2, 3, 3 },
            new VEdge { 2106, 3, 4, 1 },
            new VEdge { 2107, 4, 0, 1 },
            new VEdge { 2108, 4, 1, 1 },
        };
    
        test_dijkstra test(edges);
        test.run_dijkstra();
    
        test.print_path();
        test.generate_dot_file();
    }
    

    ¹拍打后silly typos

    ²自包含 Live On Coliru

相关问题