distance_recorder如何在boost图库中工作?

时间:2018-05-29 11:22:17

标签: boost boost-graph

我正在学习第一次呼吸算法。该图是一个完整的二叉树。我想打印每个节点到根的距离。例如,d[0, 0] = 0, d[0, 1] = 1, d[0, 2] = 1, d[0, 3] = 2, d[0, 7] = 3

enter image description here

点文件在这里。

digraph G {
0;
1;
2;
3;
4;
5;
6;
7;
8;
9;
10;
11;
12;
13;
14;
0->1 ;
0->2 ;
1->3 ;
1->4 ;
2->5 ;
2->6 ;
3->7 ;
3->8 ;
4->9 ;
4->10 ;
5->11 ;
5->12 ;
6->13 ;
6->14 ;
}

该程序非常简单,make_bfs_visitor采用distance_recorder来记录树边缘之间的距离。

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/graphviz.hpp>

int main()
{
    using DiGraph = boost::adjacency_list<>;
    DiGraph g;
    std::ifstream dot_file("graph.dot");
    boost::dynamic_properties dp{ boost::ignore_other_properties };
    boost::read_graphviz(dot_file, g, dp);

    auto vd0 = *boost::vertices(g).first;

    using vertices_size_type = boost::graph_traits<DiGraph>::vertices_size_type;

    std::vector<vertices_size_type> distances(boost::num_vertices(g));
    auto dist_pmap = boost::make_iterator_property_map(
        distances.begin(), get(boost::vertex_index, g));

    auto vis = boost::make_bfs_visitor(
        boost::record_distances(dist_pmap, boost::on_tree_edge()));

    boost::breadth_first_search(g, vd0, visitor(vis));

    for (auto d : distances)
        std::cout << d << ' ';
    std::cout << '\n';

    for (auto d : boost::make_iterator_range(boost::vertices(g)))
      std::cout << d << ' ';
    std::cout << '\n';
    return 0;
}

输出不是我的预期。

0 1 3 3 3 3 3 1 2 2 2 2 3 3 3 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 

我在这里做错了什么?

1 个答案:

答案 0 :(得分:2)

它是如何运作的?

record_distances是活动访问者的工厂。 make_bfs_visitor将其绑定到(默认情况下)BFS访问者模型中。

我猜这不是你的问题。它不清楚你做什么期望什么,因为输出并没有看到我的所有信息。也许您想以更有用的格式显示它:

for (auto vd : boost::make_iterator_range(boost::vertices(g)))
    std::cout << "d[" << vd0 << ", " << vd << "] = " << distances.at(vd) << "\n";

现在输出是:

d[0, 0] = 0
d[0, 1] = 1
d[0, 2] = 3
d[0, 3] = 3
d[0, 4] = 3
d[0, 5] = 3
d[0, 6] = 3
d[0, 7] = 1
d[0, 8] = 2
d[0, 9] = 2
d[0, 10] = 2
d[0, 11] = 2
d[0, 12] = 3
d[0, 13] = 3
d[0, 14] = 3

现在,给出了什么?因为在您的问题的标题中,您建议您期望:

  

例如,d [0,0] = 0,d [0,1] = 1,d [0,2] = 1,d [0,3] = 2,d [0,7] = 3

明显不匹配! d[0,7] = 1并不匹配&#34;更合乎逻辑的&#34; d[0,7] = 3。然而,你没有做任何事情/错误/并且距离计算没有错。

有一个微妙的观察者错误!您认为顶点描述符7指的是您在输入中使用该数字显示的顶点。但是,如果您打印read_graphviz读取的图表:

enter image description here

AHA!出了点问题,或者与预期的不同。事实上,如果你删除了#34;魔术位&#34;关于ignore_other_properties您实际上并不知道其含义:

boost::dynamic_properties dp; // {boost::ignore_other_properties};

你被告知了这件事:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::property_not_found> >'
  what():  Property not found: node_id.

实际上,你告诉Boost 读取顶点,但要忽略Dot文件中的顶点id。因此,结果是输入中图形的保证同构,但ID是未指定/实现定义的顺序。

修复它:读取节点ID

让我们保持adjacency_list<>的纯粹和简单,让我们创建一个外部ID映射:

DiGraph g;
std::map<DiGraph::vertex_descriptor, int> vertex_ids;

auto id_map = boost::make_assoc_property_map(vertex_ids);
boost::dynamic_properties dp;
dp.property("node_id", id_map);

std::ifstream dot_file("graph.dot");
boost::read_graphviz(dot_file, g, dp);

现在,当我们使用原始节点ID写回图形时:

boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));

std::ofstream dot_file("output.dot");
boost::write_graphviz_dp(dot_file, g, dp);

我们得到了原始图片。呼。

  

注意为了记录距离,我们仍然使用&#34;技术&#34; vertex_descriptor作为隐式顶点id。这是因为这样可以更容易地使用vector<>作为距离图。

     

或者,我们可以使用&#34;用户友好的&#34; id_map并存储在关联LvaluePropertMap

在报告中使用node-id

从这里起,修复报告相对容易:

for (auto vd : boost::make_iterator_range(boost::vertices(g)))
    std::cout << "d[" << id_map[vd0] << ", " << id_map[vd] << "] = " << distances.at(vd) << "\n";

打印:

d[0, 0] = 0
d[0, 1] = 1
...
d[0, 6] = 2
d[0, 7] = 3
...

耶!恢复了理智。

奖励:可视化距离

让我们为显示从根到目标顶点的距离的每条边添加一个标签:

std::map<DiGraph::edge_descriptor, int> edge_labels;
auto label_map = boost::make_assoc_property_map(edge_labels);

for (auto ed : boost::make_iterator_range(boost::edges(g)))
    label_map[ed] = distances.at(target(ed, g));

boost::dynamic_properties dp;
dp.property("node_id", id_map);
dp.property("label", label_map);

std::ofstream dot_file("output.dot");
boost::write_graphviz_dp(dot_file, g, dp);

现在,我们有了这个美丽,令人放心的输出:

enter image description here

完整演示列表

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/graphviz.hpp>
using DiGraph = boost::adjacency_list<>;

int main()
{
    DiGraph g;
    std::map<DiGraph::vertex_descriptor, int> vertex_ids;

    auto id_map = boost::make_assoc_property_map(vertex_ids);
    {
        boost::dynamic_properties dp;
        dp.property("node_id", id_map);

        std::ifstream dot_file("graph.dot");
        boost::read_graphviz(dot_file, g, dp);
    }

    auto vd0 = *boost::vertices(g).first;

    std::vector<int> distances(boost::num_vertices(g));
    auto dist_pmap = boost::make_iterator_property_map(distances.begin(), get(boost::vertex_index, g));

    auto vis = boost::make_bfs_visitor(
        boost::record_distances(dist_pmap, boost::on_tree_edge()));

    boost::breadth_first_search(g, vd0, visitor(vis));

    for (auto vd : boost::make_iterator_range(boost::vertices(g)))
        std::cout << "d[" << id_map[vd0] << ", " << id_map[vd] << "] = " << distances.at(vd) << "\n";

    {
        std::map<DiGraph::edge_descriptor, int> edge_labels;
        auto label_map = boost::make_assoc_property_map(edge_labels);

        for (auto ed : boost::make_iterator_range(boost::edges(g)))
            label_map[ed] = distances.at(target(ed, g));

        boost::dynamic_properties dp;
        dp.property("node_id", id_map);
        dp.property("label", label_map);

        std::ofstream dot_file("output.dot");
        boost::write_graphviz_dp(dot_file, g, dp);
    }
}