【问题标题】:BGL BFS Getting all nodes within 2 depth of a given nodeBGL BFS 获取给定节点 2 深度内的所有节点
【发布时间】:2017-10-07 06:52:53
【问题描述】:

我想知道是否可以使用 Boost BGL BFS 来查找从给定顶点到给定深度级别(例如 2)的所有连接顶点。如果有,怎么做?

我对访问者的概念不是很熟悉,所以知道可以使用访问者来完成会很有帮助。

谢谢!

【问题讨论】:

  • 你有什么?你不能只使用距离图并过滤 xboost.org/doc/libs/1_65_1/libs/graph/doc/distance_recorder.html

标签: c++ boost graph


【解决方案1】:

想到my answer yesterday,我意识到你的问题存在概念问题。

到底是什么距离?

当您执行 BFS 时,您不会获得“实际”距离,而是获得“有效”距离,具体取决于碰巧访问顶点的顺序。这是有用的信息,但鉴于您正在寻找具有特定最大距离的节点,感觉就像您错过了这部分。

确实,DFS 给出了其他结果。如果您并不特别关心使用哪种搜索策略,那么有个好消息:boost::depth_first_visit 具有可选的终止函数¹

如果你替换

breadth_first_search(g, s_source, 
            visitor(make_bfs_visitor(record_distances(distances.data(), on_tree_edge())))
            .color_map(colormap.data())
        );

Live On Coliru

auto stop_when = [&](G::vertex_descriptor vd, ...) { return distances.at(vd)>=2.0; };

depth_first_visit(g, s_source,
        make_dfs_visitor(record_distances(distances.data(), on_tree_edge())),
        colormap.data(),
        stop_when
    );

你可以让算法做更少的工作,所以你得到第二张图片而不是第一张图片:

更准确的

沿着这条路径走得更远一点,你可能会意识到你一直想要最短路径。实际上,使用dijkstra_shortest_paths 会更准确(如果边具有单位权重,则恰好等同于 BFS)。

Dijkstra Live On Coliru

您会注意到,在某个限制下无法修剪最短路径树。

根据您的实际可扩展性要求和其他限制,r_c_shortest_paths 可能会在这里提供帮助:

具有资源约束的最短路径问题 (SPPRC) 在有向图中寻求一条最短(最便宜、最快)的路径,该路径具有任意弧长(行程时间、成本),从一个或多个资源的源节点到目标节点约束。

例如,人们可能会寻求一条从 s 到 t 的最小长度路径,但要受到以下约束

  • 总行程时间不得超过某个上限和/或
  • 必须在沿路径的顶点拾取的某些货物的总量小于或等于某个容量限制和/或
  • 如果两个顶点 i 和 j 在一条路径上被访问,那么 i 必须在 j 之前被访问

这个问题在强烈的意义上是 NP-hard [...]


¹ 我想 DFS 具有终止功能更自然,而 BFS 不能(因为 BFS 将在下降到搜索树中的子节点之前递归地访问所有对等点;这很容易导致由于一个分支被终止,整个树块被跳过)。不过我想类似的机制可以添加到BF访问算法中。

【讨论】:

  • 感谢您对原始答案的改进!我实际上正在尝试使用您的原始答案并且它正在工作。有趣的是,DFS 实际上为我的问题提供了更优化的解决方案,正如我在 BFS 提前终止中所考虑的那样。感谢您对距离定义的意见。事实上,这是我在考虑这个问题时遗漏的东西。谢谢!
【解决方案2】:

所以。我决定尝试一下:您使用distance_recorder 这样做:

breadth_first_search(g, s_source, visitor(make_bfs_visitor(
        record_distances(distances.data(), on_tree_edge())
    ))
);

示例程序

Live On Coliru

using G = adjacency_list<>;
G::vertex_descriptor s_source = 8;

template<typename G> void save_graph(G const& g, std::string const& name, std::vector<double> const& distances);
void save_graph_filtered(double threshold, G const& g, std::string const& name, std::vector<double> const& distances);
G generate();

int main() {
    G g = generate();

    std::vector<double> distances(num_vertices(g));
    std::vector<default_color_type> colormap(num_vertices(g));

    breadth_first_search(g, s_source, 
                visitor(make_bfs_visitor(record_distances(distances.data(), on_tree_edge())))
                .color_map(colormap.data())
    );

    for (auto vd : make_iterator_range(vertices(g)))
        if (colormap.at(vd) == default_color_type{}) 
            distances.at(vd) = -1;

    distances[s_source] = -2;
    save_graph(g, "dotgraph.txt", distances);

    // show only nodes at distance <= 2:
    save_graph_filtered(2.0, g, "dotgraph-filtered.txt", distances);
}

生成随机图

这真的很简单,因为 BGL 有它:

////////////////////////////////////////////
// generate random graph
#include <boost/graph/random.hpp>
#include <random>

size_t s_seed = 0xf5cab8bd; // std::random_device{}();
std::mt19937 s_rng {s_seed};

G generate() {
    G g;
    std::cout << "Seed used: " << std::hex << std::showbase << s_seed << "\n";
    boost::generate_random_graph(g, 20, 30, s_rng);
    return g;
}

硬编码的种子复制了这篇文章中的图像

过滤阈值距离

这也可以很简单:

////////////////////////////////////////////
// filtering for threshold distance
#include <boost/graph/filtered_graph.hpp>
void save_graph_filtered(double threshold, G const& g, std::string const& name, std::vector<double> const& distances) {
    filtered_graph<G, keep_all, std::function<bool(G::vertex_descriptor)>> 
        fg(g, {}, [&](G::vertex_descriptor vd) { return distances[vd]!=-1 && distances[vd]<=threshold; });

    save_graph(fg, name, distances);
}

注意检查 distance != -1 以排除无法到达的顶点。

保存漂亮的 Graphviz 文件

这是最不必要的部分,但它可以制作漂亮的演示:

////////////////////////////////////////////
// graph-viz demo output
#include <fstream>
#include <algorithm>
#include <sstream>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/graph/graphviz.hpp>

template<typename G>
void save_graph(G const& g, std::string const& name, std::vector<double> const& distances) {
    using Vertex = typename G::vertex_descriptor;
    std::ofstream dotfile;
    dotfile.open(name);

    auto shape = [&distances](Vertex vd) {
        return (vd==s_source)?"circle":"Mrecord";
    };

    auto label = [&distances](Vertex vd) {
        std::ostringstream name;
        if (vd == s_source)                 name << "SOURCE";
        else                                name << vd;
        if (auto d = distances[vd]; d >= 0) name << "|distance: " << d;
        return name.str();
    };

    auto max = 1.25* *std::max_element(distances.begin(), distances.end());
    auto dist = [&distances,max](Vertex vd) {
        int r = 224, g = 160, b = 160;
        if (auto d = distances[vd]; d >= 0) {
            r = 255.0 * (1.0 - std::clamp(d, 0.0, max)/max);
            g = 255.0 * (1.0 - std::clamp(d, 0.0, max)/max);
            b = 255;
        }
        std::ostringstream oss;
        oss << std::setfill('0') << '#' << std::hex
            << std::setw(2) << r
            << std::setw(2) << g
            << std::setw(2) << b;
        return oss.str();
    };

    dynamic_properties dp;
    typed_identity_property_map<Vertex> v_;

    dp.property("node_id", get(vertex_index, g));
    dp.property("label", make_transform_value_property_map(label, v_));
    dp.property("shape", make_transform_value_property_map(shape, v_));
    //dp.property("shape", make_constant_property<Vertex>("Mrecord"s));
    dp.property("style", make_constant_property<Vertex>("filled"s));
    dp.property("fillcolor", make_transform_value_property_map(dist, v_));

    write_graphviz_dp(dotfile, g, dp);
}

输出:

未过滤:

过滤阈值(样本中为 2.0):

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 2019-07-14
    • 1970-01-01
    • 2013-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多