簡體   English   中英

Boost :: graph Dijkstra和自定義對象和屬性

[英]Boost::graph Dijkstra and custom objects and properties

我想使用boost的dijkstra算法(因為我在程序的其他部分使用了boost)。 我遇到的問題是將自定義對象(我相信它們被稱為property )添加到adjacency_list

基本上我有一個自定義邊緣類,它維護有關邊緣和通過它連接的頂點的各種信息。 我想使用adjaceny_list所需的邊緣屬性存儲我的自定義數據對象

我已經成功實現了提升提供的玩具示例。 我試圖使用自定義屬性無濟於事( 提升示例提升屬性 )。 我只是將我的VEdge數據結構封裝在一個結構或其他東西中,我只需要能夠檢索它。 但我無法弄清楚如何將我的自定義數據結構包含到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;

好的。 https://stackoverflow.com/questions/28889423/boost-adjacency-list-swap-errors-when-using-boost-dijkstra以來,你已經走了很長的路。 樣本是獨立的,可以編譯¹

我想我可以連接一些點,希望這會有所幫助。

1.使用VEdge

對於最簡單的選項,我使用Bundled PropertiesVEdge如下方式定義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;

正如您所看到的那樣,權重貼圖具有更復雜的類型,如捆綁屬性的屬性貼圖中所述 你可以得到實際的地圖:

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() :它不是“被強制為edge_property類型的數組”。 迭代器會很好。

現在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作為起始頂點。 結果路徑與原始路徑完全相同

在此輸入圖像描述

完整計划

住在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對象復制到捆綁包中,因此可能更有效

沒有進一步的麻煩(並沒有打擾內存泄漏):

住在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();
}

¹拍打愚蠢的錯別字后

²自成一體的Live On Coliru

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM