简体   繁体   English

Dijkstra 图,每条边都有一个权重表

[英]Dijkstra graph with a table of weights on each edge

I have a boost graph with multiples weights for each edges (imagine one set of weights per hour of the day).我有一个提升图,每个边都有多个权重(想象一天中每小时有一组权重)。 Every one of those weights values is stored in a propretyEdge class :这些权重值中的每一个都存储在一个 propretyEdge 类中:

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

I created a graph with those properties, and then filled it with the right values.我用这些属性创建了一个图表,然后用正确的值填充它。 The problem is now that I want to launch the Dijkstra algorithm over a particular set of weight on the graph : for example a function that could be :现在的问题是我想在图上的一组特定权重上启动 Dijkstra 算法:例如,一个函数可能是:

void Dijkstra (string date, parameters ... )

That would use the那将使用

weights[date]

value for each Edge of the graph.图的每个边的值。

I read over and over the documentation, and I couldn't have a clear picture of what I have to do.我一遍又一遍地阅读文档,但我无法清楚地了解我必须做什么。 I surely need to write something like this, but I have no idea were to start :我肯定需要写这样的东西,但我不知道要开始:

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

Thank you for your help.感谢您的帮助。

Edit编辑

Thanks to the wonderful Answer of Sehe , I was able to do exactly what I wanted on MacOS and on Ubuntu.感谢Sehe的精彩回答,我能够在 MacOS 和 Ubuntu 上做我想做的事。

But when we tried to compile this piece of code on Visual Studio 2012, it appeared that VS wasn't very good at understanding pointer function of boost.但是当我们尝试在Visual Studio 2012上编译这段代码时,VS似乎不太擅长理解boost的指针函数。 So we modified the part of Sehe :所以我们修改了 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);

by :经过 :

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

Which had the advantage of not using a pointer function.其优点是不使用指针函数。

Since it's apparently not immediately clear that this question is answered in the other answer , I'll explain.由于在另一个答案中回答了这个问题显然不是很清楚,我会解释一下。

All you really need is a custom weight_map parameter that is "stateful" and can select a certain value for a given date.真正需要的是一个自定义的weight_map参数,它是“有状态的”并且可以为给定日期选择某个值。

You can make this as complicated as you wish ¹, so you could even interpolate/extrapolate a weight given an unknown date ², but let's for the purpose of this demonstration keep it simple.您可以随心所欲地使它变得复杂¹,因此您甚至可以在给定未知日期的情况下内插/外推一个权重²,但为了演示目的,让我们保持简单。

Let's define the graph type (roughly) as above:让我们定义图形类型(大致)如上:

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

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

Now, let's generate a random graph, with random weights for 3 different dates:现在,让我们生成一个随机图,其中包含 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);

And, jumping to the goal:并且,跳到目标:

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

Now how do you implement Dijkstra(...) ?现在你如何实现Dijkstra(...) Gleaning from the documentation sample, you'd do something like从文档示例中收集,你会做类似的事情

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

Now the only unclear bit here should be dated_weight_map .现在唯一不清楚的地方应该是dated_weight_map

Enter Boost Property Maps输入Boost 属性映射

As I showed in the linked Is it possible to have several edge weight property maps for one graph BOOST?正如我在链接中显示的那样,一个图 BOOST 是否可以有多个边权重属性图? , you can have all kinds of property maps ³, including invocation of user-defined functions. ,您可以拥有各种属性映射³,包括调用用户定义的函数。 This is the missing piece:这是缺失的部分:

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

Voilà: done瞧:完成

I hope that by now, the correspondence in the question as well as the answer of the linked question is clear.我希望到目前为止,问题中的对应关系以及链接问题的答案是清楚的。 All that's left to do is post the full live sample and the outcome in a pretty picture:剩下要做的就是在一张漂亮的图片中发布完整的实时样本和结果:

Live On Coliru住在 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);
    }

}

Output, eg输出,例如

在此处输入图片说明


¹ As long as you keep the invariants required by the algorithm you're invoking. ¹只要您保留您正在调用的算法所需的不变量。 In particular, you must return the same weight consistently during the execution, given the same edge.特别是,在给定相同的边缘的情况下,您必须在执行期间始终如一地返回相同的权重。 Also, some algorithms don't support negative weight etc.此外,某些算法不支持负权重等。

² I'd highly suggest using a Boost ICL interval_map in such a case but I digress ² 我强烈建议在这种情况下使用 Boost ICL interval_map但我离题了

³ see also map set/get requests into C++ class/structure changes ³ 另请参阅将设置/获取请求映射到 C++ 类/结构更改

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

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