简体   繁体   English

如何遍历图形并在提升过程中提取边缘权重?

[英]How to traverse a graph and extract edge weights along the way with boost?

Suppose I have the following undirected graph 假设我有以下无向图

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

Where A and C are empty strings and the real numbers are weights. 其中A和C是空字符串,实数是权重。 Now I want to get from B to A. I know in boost, that can be done with 现在我想从B转到A。我知道可以通过以下方法完成

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

When I get to vertex A, I want to be able to randomize the string from B, based on some math on the weight of the edge from B to A. So I want to be able to do something like g[A].name = newString(weight from B to A); 当我到达顶点A时,我希望能够基于从B到A的边的权重的数学运算,将B的字符串随机化。因此,我希望能够做类似g[A].name = newString(weight from B to A); .

The problem is I don't really know how to do that. 问题是我真的不知道该怎么做。 I have edge weights and they are imported properly from a file that I am reading in. And I know how to get the weights if I iterate through this way: 我有边缘权重,它们是从正在读取的文件中正确导入的。如果通过这种方式进行迭代,我知道如何获得权重:

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

The issue with this is that this will iterate through the nodes of the graph and not necessarily caring about any of the edges. 这样做的问题是,这将遍历图的节点,而不必关心任何边缘。

Is there something in boost that will do what I want? 有什么助推器可以满足我的需求吗? I have tried looking at this implementation from Stack Overflow, and I understand how to start at a specific vertex. 我尝试过从Stack Overflow查看此实现 ,并且我了解如何从特定的顶点开始。 However I am not sure if there is a way to tweak this algorithm to use my edge weights to achieve what I want, and to be honest the documentation for DFS is hard to understand. 但是我不确定是否有一种方法可以调整该算法以使用边缘权重来实现我想要的功能,说实话,DFS的文档很难理解。 I believe DFS is what I want, but I am not sure how to implement this easily without breaking down and writing my own DFS which I don't really want to do. 我相信DFS是我想要的,但是我不确定如何轻松地实现此功能而不分解并编写我自己不想做的DFS。

Now I want to get from B to A. I know in boost, that can be done with [...] 现在,我想从B转到A。我知道,可以通过[...]

No, the code just enumerates vertices, even those without edges and in no particular order. 不,代码只是枚举顶点,即使那些没有边且没有特定顺序的顶点也是如此。

When I get to vertex A 当我到达顶点A时

What does it mean to "get to vertex A"? “到达顶点A”是什么意思? Do you need have a path there, or are you simply going to enumerate all vertices and traverse edges from there? 您是否需要在此处建立路径,还是只是要枚举所有顶点并从那里遍历边?

I want to be able to randomize the string from B, based on some math on the weight of the edge from B to A 我希望能够基于一些关于从B到A的边缘权重的数学运算,将B的字符串随机化

I read this as "I want to calculate a string based on the weight of edges discovered and update the label string for the destination vertex . 我将其读为“我想根据发现的边的权重来计算字符串,并更新目标顶点的标签字符串。

That's at least slightly confusing, because it suggests a direction on edges of an undirected graph. 这至少有点令人困惑,因为它暗示了无向图的边缘上的方向。 I'm assuming you indeed want to use a direction (the direction of traversal during edge discovery). 我假设您确实要使用一个方向(边缘发现期间的遍历方向)。

And I know how to get the weights if I iterate through this way: [...snip...] The issue with this is that this will iterate through the nodes of the graph and not necessarily caring about any of the edges. 而且我知道如果通过这种方式进行迭代,如何获得权重:[... snip ...]的问题是,这将遍历图的节点,而不必关心任何边缘。

Huh. That's completely flipped around. 那完全被翻转了。 that loop does iterate edges, specifically. 该循环确实会迭代边缘。 Did you swap the code samples? 您交换了代码示例吗?

Is there something in boost that will do what I want? 有什么助推器可以满足我的需求吗?

That's unanswerable until you know what you want. 除非您知道自己想要什么,否则无法回答。

I believe DFS is what I want 我相信DFS是我想要的

Okay... Let's get you started a little bit: 好吧...让我们开始吧:

The Sample Graph 样本图

Live On Coliru 生活在Coliru

#include <boost/graph/adjacency_list.hpp>

Let's define a graph that can store labels ( name ) and weights: 让我们定义一个可以存储标签( name )和权重的图形:

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

Now, let's print the sample graph in Graphviz format: 现在,让我们以Graphviz格式打印示例图:

Graph make_sample();
void dump(Graph&);

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

That's a bit like cheating, but it generally helps to start out with the big picture and hide the details, so let's implement make_sample : 这有点像作弊,但是通常可以帮助您从整体上入手并隐藏细节,因此让我们实现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;
}

And dump : 然后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);
}

The graph looks like this: 该图如下所示: 在此处输入图片说明

Adding DFS 添加DFS

Search is simple: 搜索很简单:

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

But you wanted to do the magic: 但是您想做魔术:

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

Now already, we print: 现在,我们打印:

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

Now, let's check the properties of the related vertices: 现在,让我们检查相关顶点的属性:

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

Now you can do some logic: 现在您可以执行一些逻辑:

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

Which already prints: 已打印:

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

Write - Access 写-访问

For safety reasons, Graph is received by const& in the visitor. 出于安全原因, const&在访问者中收到Graph To be able to mutate, we need a writable reference. 为了能够变异,我们需要一个可写的参考。 One way is to store a Graph& inside the visitor: 一种方法是将Graph&存储在访问者内部:

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;
    }
};

Perhaps more idiomatic would be to use Property Maps - which have similar effect: 也许更惯用的方法是使用属性图-效果类似:

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;
    }
};

Caveat: write access during algorithm is dangerous. 注意:算法期间的写访问是危险的。 Be careful not to violate the pre-conditions/invariants of the algorithm 注意不要违反算法的前提条件/不变式

Full Demo 完整演示

Live On Coliru 生活在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());
}

Prints: 印刷品:

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

Which looks like : 看起来像 在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM