【问题标题】:How does distance_recorder work in boost graph library?distance_recorder 在 boost 图形库中是如何工作的?
【发布时间】:2018-05-29 11:22:17
【问题描述】:

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

点文件在这里。

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

我在这里做错了什么?

【问题讨论】:

    标签: boost boost-graph


    【解决方案1】:

    它是如何工作的?

    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 与“更合乎逻辑的”d[0,7] = 3 不匹配。然而,你没有做任何事情/错误/并且距离计算没有错。

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

    啊哈!出了点问题,或与您预期的不同。事实上,如果你删除了关于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&lt;&gt; 的纯粹和简单,所以让我们创建一个外部 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);
    

    我们恢复了原始图片。唷。

    注意为了记录距离,我们仍然使用“技术”vertex_descriptor 作为隐式顶点 ID。这是因为这样可以更轻松地使用vector&lt;&gt; 作为距离图。

    或者,我们可以使用“用户友好”的 id_map 并存储在关联 LvaluePropertMap

    在报告中使用节点 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);
    

    现在,我们有了这个漂亮、令人放心的输出:

    完整的演示清单

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

    【讨论】:

    • 这是一个很好的答案。我同意这个问题(标题)本身措辞不佳。我会在我的问题中添加一个注释来反映这一点。
    猜你喜欢
    • 2020-04-15
    • 2021-07-02
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-02
    • 1970-01-01
    相关资源
    最近更新 更多