【问题标题】:How to traverse a graph and extract edge weights along the way with boost?如何通过 boost 遍历图并提取边权重?
【发布时间】:2018-05-11 17:23:56
【问题描述】:

假设我有以下无向图

A("")--2.31--B("Hello")--1.036--C("")

其中 A 和 C 是空字符串,实数是权重。现在我想从 B 到 A。我知道在 boost 中,可以用

auto vp = boost::vertices(g);
for(auto vit = vp.first; vit != vp.second; ++vit)
{
  std::cout << *vit <<  " " << std::endl;
}

当我到达顶点 A 时,我希望能够根据从 B 到 A 的边的权重的一些数学来随机化 B 中的字符串。所以我希望能够做类似g[A].name = newString(weight from B to A); 的事情.

问题是我真的不知道该怎么做。我有边缘权重,它们是从我正在读取的文件中正确导入的。如果我通过这种方式迭代,我知道如何获得权重:

auto es = boost::edges(g);
for(auto eit = es.first; eit != es.second; ++eit)
{
  std::cout << get(weight,*eit) << std::endl;
}

这样做的问题是,这将遍历图形的节点,而不必关心任何边。

在 boost 中有什么东西可以满足我的需求吗?我曾尝试从 Stack Overflow 查看this implementation,并且我了解如何从特定顶点开始。但是我不确定是否有办法调整这个算法来使用我的边缘权重来实现我想要的,老实说the documentation for DFS 很难理解。我相信 DFS 是我想要的,但我不确定如何在不分解并编写我自己并不想做的 DFS 的情况下轻松实现它。

【问题讨论】:

    标签: c++ boost graph depth-first-search


    【解决方案1】:

    现在我想从 B 到 A。我知道在 boost 中,可以使用 [...]

    不,代码只是枚举顶点,即使是那些没有边且没有特定顺序的顶点。

    当我到达顶点 A

    “到达顶点 A”是什么意思?您需要在那里有一条路径,还是只是要枚举所有顶点并从那里遍历边?

    我希望能够根据从 B 到 A 的边权重的一些数学计算来随机化 B 中的字符串

    我将其读作“我想根据发现的边的权重计算一个字符串,并更新目标顶点的标签字符串。

    这至少有点令人困惑,因为它暗示了无向图边缘的方向。我假设您确实想使用一个方向(边缘发现期间的遍历方向)。

    如果我通过这种方式迭代,我知道如何获得权重:[...snip...] 问题在于,这将遍历图形的节点,而不必关心任何边缘。

    嗯。这完全颠倒过来了。该循环确实迭代边缘,特别是。您是否交换了代码示例?

    在 boost 中有什么东西可以满足我的需求吗?

    在您知道自己想要什么之前,这是无法回答的。

    我相信 DFS 是我想要的

    好的...让我们开始吧:

    示例图

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    

    让我们定义一个可以存储标签 (name) 和权重的图:

    struct Vertex { std::string name; };
    struct Edge   { double weight; };
    using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
    

    现在,让我们以 Graphviz 格式打印示例图:

    Graph make_sample();
    void dump(Graph&);
    
    int main() {
        Graph g = make_sample();
        dump(g);
    }
    

    这有点像作弊,但通常有助于从大局开始并隐藏细节,所以让我们实现make_sample

    Graph make_sample() {
        Graph g;
        auto A = add_vertex({""}, g);
        auto B = add_vertex({"Hello"}, g);
        auto C = add_vertex({""}, g);
    
        add_edge(A, B, {2.31}, g);
        add_edge(B, C, {1.036}, g);
        return g;
    }
    

    还有dump:

    #include <boost/graph/graphviz.hpp>
    void dump(Graph& g) {
        boost::dynamic_properties dp;
        dp.property("node_id", get(boost::vertex_index, g));
        dp.property("label", get(&Vertex::name, g));
        dp.property("label", get(&Edge::weight, g));
        write_graphviz_dp(std::cout, g, dp);
    }
    

    looks like这个:

    添加 DFS

    搜索很简单:

    #include <boost/graph/depth_first_search.hpp>
    
    struct Visitor : boost::default_dfs_visitor {
    };
    
    void operate_on(Graph& g) {
        Visitor vis;
        std::vector<boost::default_color_type> color(num_vertices(g));
        boost::depth_first_search(g, vis, color.data());
    }
    

    但你想施展魔法:

    struct Visitor : boost::default_dfs_visitor {
        void examine_edge(Graph::edge_descriptor e, const Graph& g) const {
            std::cout << "Examining edge " << e << "\n";
        }
    };
    

    现在,我们打印:

    Examining edge (0,1)
    Examining edge (1,0)
    Examining edge (1,2)
    Examining edge (2,1)
    

    现在,让我们检查相关顶点的属性:

    Vertex const& s = g[source(e, g)];
    Vertex const& t = g[target(e, g)];
    

    现在你可以做一些逻辑了:

    if (s.name.empty() && !t.name.empty()) { // use your own logic here
        std::cout << "Updating label for '" << t.name << "' in edge " << e << "\n";
    }
    

    已经打印的:

    Updating label for 'Hello' in edge (0,1)
    Updating label for 'Hello' in edge (2,1)
    

    写入 - 访问

    出于安全原因,Graph 由访客中的const&amp; 接收。为了能够改变,我们需要一个可写的引用。一种方法是在访问者中存储Graph&amp;

    struct Visitor : boost::default_dfs_visitor {
        Graph& _g;
    
        Visitor(Graph& g) : _g(g) {}
    
        void examine_edge(Graph::edge_descriptor e, const Graph&) const {
    
            // get vertex bundles
            Vertex& s = _g[source(e, _g)];
            Vertex& t = _g[target(e, _g)];
    
            if (s.name.empty() && !t.name.empty()) { // use your own logic here
                t.name += '(' + std::to_string(_g[e].weight) + ')';
            }
            return;
        }
    };
    

    也许更惯用的方法是使用具有类似效果的属性映射:

    struct Visitor : boost::default_dfs_visitor {
        boost::property_map<Graph, std::string Vertex::*>::type _name;
        boost::property_map<Graph, double Edge::*>::const_type _weight;
    
        Visitor(Graph& g)
            : _name(get(&Vertex::name, g)),
              _weight(get(&Edge::weight, static_cast<Graph const&>(g)))
        { }
    
        void examine_edge(Graph::edge_descriptor e, const Graph& g) const {
            auto& sname = _name[source(e, g)];
            auto& tname = _name[target(e, g)];
    
            if (sname.empty() && !tname.empty()) { // use your own logic here
                tname += '(' + std::to_string(_weight[e]) + ')';
            }
            return;
        }
    };
    

    警告:算法期间的写访问是危险的。注意不要违反算法的前置条件/​​不变量

    完整演示

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    
    struct Vertex { std::string name; };
    struct Edge   { double weight; };
    using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
    
    Graph make_sample();
    void dump(Graph&);
    void operate_on(Graph&);
    
    int main() {
        Graph g = make_sample();
        operate_on(g);
        dump(g);
    }
    
    Graph make_sample() {
        Graph g;
        auto A = add_vertex({""}, g);
        auto B = add_vertex({"Hello"}, g);
        auto C = add_vertex({""}, g);
    
        add_edge(A, B, {2.31}, g);
        add_edge(B, C, {1.036}, g);
        return g;
    }
    
    #include <boost/graph/graphviz.hpp>
    void dump(Graph& g) {
        boost::dynamic_properties dp;
        dp.property("node_id", get(boost::vertex_index, g));
        dp.property("label", get(&Vertex::name, g));
        dp.property("label", get(&Edge::weight, g));
        write_graphviz_dp(std::cout, g, dp);
    }
    
    #include <boost/graph/depth_first_search.hpp>
    
    struct Visitor : boost::default_dfs_visitor {
        boost::property_map<Graph, std::string Vertex::*>::type _name;
        boost::property_map<Graph, double Edge::*>::const_type _weight;
    
        Visitor(Graph& g)
            : _name(get(&Vertex::name, g)),
              _weight(get(&Edge::weight, static_cast<Graph const&>(g)))
        { }
    
        void examine_edge(Graph::edge_descriptor e, const Graph& g) const {
            auto& sname = _name[source(e, g)];
            auto& tname = _name[target(e, g)];
    
            if (sname.empty() && !tname.empty()) { // use your own logic here
                tname += '(' + std::to_string(_weight[e]) + ')';
            }
        }
    };
    
    void operate_on(Graph& g) {
        Visitor vis { g };
        std::vector<boost::default_color_type> color(num_vertices(g));
        boost::depth_first_search(g, vis, color.data());
    }
    

    打印:

    graph G {
    0 [label=""];
    1 [label="Hello(2.310000)(1.036000)"];
    2 [label=""];
    0--1  [label=2.31];
    1--2  [label=1.036];
    }
    

    哪个looks like

    【讨论】:

    • 这绝对是一流的解释,我很抱歉我的英语没有很好地通过,但这给了我一个很好的开始。我的意思。从任意顶点开始,在本例中为顶点 B。我将能够改变 A 中的字符串。类似于 A("lelHo") -- 2.31 B("Hello") -- 1.036 -- C("oHlel ”)。字符串根据边缘权重发生变异的地方。我希望这能消除混乱!
    • 好吧,我认为你已经从这里开始了。只需add a starting vertex to the call to depth_first_search 并实现所需的逻辑。这个样本看起来像this
    • 太棒了!非常感谢您抽出宝贵时间为我输入此内容!我真的很感激!
    猜你喜欢
    • 1970-01-01
    • 2011-07-31
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多