【问题标题】:Dijkstra graph with a table of weights on each edgeDijkstra 图在每条边上都有一个权重表
【发布时间】:2015-06-03 14:48:20
【问题描述】:

我有一个提升图,每个边都有多个权重(想象一天中每小时一组权重)。这些权重值中的每一个都存储在propretyEdge 类中:

class propretyEdge {
    std::map<std::string,double> weights; // Date indexed 
}

我用这些属性创建了一个图表,然后用正确的值填充它。 现在的问题是我想在图表上的一组特定权重上启动 Dijkstra 算法:例如,一个函数可能是:

void Dijkstra (string date, parameters ... )

这将使用

weights[date]

图表每条边的值。

我一遍又一遍地阅读文档,但我无法清楚地了解我必须做什么。我当然需要写这样的东西,但我不知道要开始:

boost::dijkstra_shortest_paths (
    (*graph_m), 
    vertex_origin_num_l,
    // weight_map (get (edge_weight, (*graph_m)))
    // predecessor_map(boost::make_iterator_property_map(predecessors.begin(), get(boost::vertex_index, (*graph_m)))).
    // distance_map(boost::make_iterator_property_map(distances.begin (), get(vertex_index,(*graph_m) )))
    predecessor_map(predecessorMap).
    distance_map(distanceMap)
);

感谢您的帮助。

编辑

感谢出色的Answer of Sehe,我能够在 MacOS 和 Ubuntu 上做我想做的事。

但是当我们尝试在 Visual Studio 2012 上编译这段代码时,VS 似乎不太擅长理解 boost 的指针功能。所以我们修改了Sehe的部分:

auto dated_weight_f = [&](Graph::edge_descriptor ed) {
    return g[ed].weights.at(date);
};

auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);

作者:

class dated_weight_f {
public:
  dated_weight_f(Graph* graph_p,std::string date_p){
    graph_m=graph_p;
    date_m=date_p;
  }
  typedef double result_type;
    result_type operator()(Edge edge_p) const{
    return (*graph_m)[edge_p].weights.at(date_m);
  }
private:
  Graph* graph_m;
  std::string date_m;
};

const auto dated_weight_map = make_function_property_map<Edge>(dated_weight_f(graph_m,date_l));

不使用指针函数的优点。

【问题讨论】:

  • @Sehe 它根本不是重复的..
  • @Erowlin 我想如果你使用实际参数会有点好。我的论点在链接的答案中。我认为你知道这个问题的更好答案吗?我很高兴邀请您发布它。
  • @Erowlin 无论如何,为了让问题的相关性更加明显,我已经开始在一个应该是全面的答案中解释这一点。请问是什么让你如此自信地说它不是复制品? (如果我不得不猜测是没有“dijkstra”这个词?)。
  • 抱歉没有解释。另一个问题中遗漏的是您发布的明确解释。谢谢你。我认为太多的问题被标记为“重复”而没有太多的解释。
  • @Erowlin 在这种情况下,我真的认为这是有道理的,因为问题是相同的(我们对每个边都有多个权重)并且答案也是 :) 在我看来,人们经常过于轻视如果他们没有把事情说清楚,那就是很好的提示。 (这伤害了提问者和回答者)。我现在不介意。我得到了一个很好的答案,我可以在将来重复使用 :)

标签: c++ boost dijkstra boost-graph boost-property-map


【解决方案1】:

由于这个问题is answered in the other answer 显然不是很清楚,所以我会解释一下。

真正只需要一个自定义的weight_map 参数,它是“有状态的”并且可以为给定日期选择特定值。

您可以根据需要将其复杂化 ¹,因此您甚至可以在给定未知日期的情况下内插/外推权重 ²,但为了演示的目的,让我们保持简单。

让我们像上面那样(大致)定义图形类型:

struct propretyEdge {
    std::map<std::string, double> weights; // Date indexed 
};

using Graph = adjacency_list<vecS, vecS, directedS, no_property, propretyEdge>;

现在,让我们生成一个随机图表,其中包含 3 个不同日期的随机权重:

int main() {
    Graph g;
    std::mt19937 prng { std::random_device{}() };
    generate_random_graph(g, 8, 12, prng);

    uniform_real<double> weight_dist(10,42);
    for (auto e : make_iterator_range(edges(g)))
        for (auto&& date : { "2014-01-01", "2014-02-01", "2014-03-01" })
            g[e].weights[date] = weight_dist(prng);

然后,跳到目标:

    for (std::string const& date : { "2014-01-01", "2014-02-01", "2014-03-01" }) {
        Dijkstra(date, g, 0);
    }
}

现在你如何实现Dijkstra(...)?从文档样本中收集,你会做类似的事情

void Dijkstra(std::string const& date, Graph const& g, int vertex_origin_num_l = 0) {

    // magic postponed ...

    std::vector<Graph::vertex_descriptor> p(num_vertices(g));
    std::vector<double>                   d(num_vertices(g));
    std::vector<default_color_type>       color_map(num_vertices(g));

    boost::typed_identity_property_map<Graph::vertex_descriptor> vid; // T* property maps were deprecated

    dijkstra_shortest_paths(g, vertex_origin_num_l,
            weight_map(dated_weight_map).
            predecessor_map(make_iterator_property_map(p.data(),   vid)).
            distance_map(make_iterator_property_map(d.data(),      vid)).
            color_map(make_iterator_property_map(color_map.data(), vid))
        );

现在唯一不清楚的地方应该是dated_weight_map

输入Boost Property Maps

正如我在链接的Is it possible to have several edge weight property maps for one graph BOOST? 中所展示的,您可以拥有各种属性映射³,包括调用用户定义的函数。这是缺少的部分:

auto dated_weight_f = [&](Graph::edge_descriptor ed) {
    return g[ed].weights.at(date);
};

auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);

瞧:完成

我希望到现在为止,问题中的对应关系以及链接问题的答案都已经清楚了。剩下要做的就是以漂亮的图片发布完整的现场样本和结果:

Live On Coliru

#include <boost/property_map/property_map.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/property_map_iterator.hpp>

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

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <fstream>

using namespace boost;

struct propretyEdge {
    std::map<std::string, double> weights; // Date indexed 
};

using Graph = adjacency_list<vecS, vecS, directedS, no_property, propretyEdge>;

void Dijkstra(std::string const& date, Graph const& g, int vertex_origin_num_l = 0) {

    auto dated_weight_f = [&](Graph::edge_descriptor ed) {
        return g[ed].weights.at(date);
    };

    auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);

    std::vector<Graph::vertex_descriptor> p(num_vertices(g));
    std::vector<double>                   d(num_vertices(g));
    std::vector<default_color_type>       color_map(num_vertices(g));

    boost::typed_identity_property_map<Graph::vertex_descriptor> vid; // T* property maps were deprecated

    dijkstra_shortest_paths(g, vertex_origin_num_l,
            weight_map(dated_weight_map).
            predecessor_map(make_iterator_property_map(p.data(),   vid)).
            distance_map(make_iterator_property_map(d.data(),      vid)).
            color_map(make_iterator_property_map(color_map.data(), vid))
        );

    std::cout << "distances and parents for '" + date + "':" << std::endl;
    for (auto vd : make_iterator_range(vertices(g)))
    {
        std::cout << "distance(" << vd << ") = " << d[vd] << ", ";
        std::cout << "parent(" << vd << ") = " << p[vd] << std::endl;
    }
    std::cout << std::endl;

    std::ofstream dot_file("dijkstra-eg-" + date + ".dot");

    dot_file << "digraph D {\n"
        "  rankdir=LR\n"
        "  size=\"6,4\"\n"
        "  ratio=\"fill\"\n"
        "  graph[label=\"shortest path on " + date + "\"];\n"
        "  edge[style=\"bold\"]\n" 
        "  node[shape=\"circle\"]\n";

    for (auto ed : make_iterator_range(edges(g))) {
        auto u = source(ed, g),
            v = target(ed, g);

        dot_file 
            << u << " -> " << v << "[label=\"" << get(dated_weight_map, ed) << "\""
            << (p[v] == u?", color=\"black\"" : ", color=\"grey\"")
            << "]";
    }
    dot_file << "}";
}

int main() {
    Graph g;
    std::mt19937 prng { std::random_device{}() };
    generate_random_graph(g, 8, 12, prng);

    uniform_real<double> weight_dist(10,42);
    for (auto e : make_iterator_range(edges(g)))
        for (auto&& date : { "2014-01-01", "2014-02-01", "2014-03-01" })
            g[e].weights[date] = weight_dist(prng);

    for (std::string const& date : { "2014-01-01", "2014-02-01", "2014-03-01" }) {
        Dijkstra(date, g, 0);
    }

}

输出,例如


¹只要您保留您正在调用的算法所需的不变量。特别是,在给定相同边缘的情况下,您必须在执行期间始终返回相同的权重。另外,有些算法不支持负权重等。

² 在这种情况下,我强烈建议使用 Boost ICL interval_map,但我离题了

³ 另见map set/get requests into C++ class/structure changes

【讨论】:

  • 我不知道该怎么感谢你!事实上,另一个问题的回答非常接近我想要的,但老实说,我错过了你在回答中非常清楚地解释的几个步骤。非常感谢 !我确实实现了我想要的,你的代码很清晰。
  • @sehe 有没有想过写一本 BGL 书?我会买的。
  • @pbible 嗯。我会先做Boost Spirit book :) (这无济于事,我并没有真正了解图论。不过我确实了解了 C++)
  • 少数几个熟悉 c++ 的人之一。我已经编程一年了,几乎看不懂哈哈。
  • TBH 我认为令人惊讶的是,许多人都对 C++ 深谙此道。或者 Haskell,就此而言。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-25
  • 2013-06-02
  • 2016-03-06
  • 2022-11-10
相关资源
最近更新 更多