Boost.Graphでの最短経路探索

先日、Boost.勉強会 #14 東京に参加させて頂いたが、そこでBoost.Graphの話があった。
復習がてら、今回はTDD Boot Camp 東京 for C++での最短経路探索の課題(TDD Boot Camp 東京 for C++ 課題)をBoost.Graphで解いてみる。なお課題は規模が大きいので、今回は横浜近辺の5駅に絞った。

グラフ構造の構築

まずBoost.Graphでグラフ構造を記述する。
なおBoost.Graphはgraphvizのグラフ図を出力できるので、今回は記述したグラフ構造をgraphvizで画像化して視覚的に確認する。コードは以下。

#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <fstream>
#include <boost/graph/graphviz.hpp>

using namespace boost;

typedef adjacency_list<listS, vecS, undirectedS, no_property, property<edge_weight_t, int>> Graph;
typedef std::pair<int, int> Edge;
typedef graph_traits<Graph>::vertex_descriptor Vertex;

//駅の定義
enum {
    Yokohama, 
    MusashiKosugi, 
    Kawasaki, 
    Shibuya, 
    Tokyo, 
    N
};

const char* NameList[] = {
    "Yokohama",
    "MusashiKosugi",
    "Kawasaki",
    "Shibuya",
    "Tokyo"
};

int main()
{
    //経路と所要時間の定義
    const std::vector<Edge> edges = {
        {Yokohama, MusashiKosugi}, 
        {Yokohama, Kawasaki},
        {MusashiKosugi, Kawasaki},
        {MusashiKosugi, Shibuya},
        {Kawasaki, Tokyo},
        {Shibuya, Tokyo}
    };

    const std::vector<int> weights = { 
        23,
        14,
        19,
        21,
        24,
        25
    };
    
    const Graph g(edges.begin(), edges.end(), weights.begin(), N);

    std::ofstream file("trainpath.dot");
    write_graphviz(file, g, make_label_writer(NameList));
    return 0;
}

上記のコードを実行した後、生成されたgraphvizのdotファイルを「dot -Tpng trainpath.dot -o trainpath.png」などのコマンドでpngに変換する。すると以下の様な画像が得られる。

f:id:goyoki:20140302023304p:plain

最短経路検索

グラフ構造を記述できたら、今回は横浜・東京間の最短経路をダイクストラ法を使って導き出す。
コードは以下。

#include <iostream>
#include <vector>
#include <deque>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

using namespace boost;

typedef adjacency_list<listS, vecS, undirectedS, no_property, property<edge_weight_t, int>> Graph;
typedef std::pair<int, int> Edge;
typedef graph_traits<Graph>::vertex_descriptor Vertex;

//駅の定義
enum {
    Yokohama, 
    MusashiKosugi, 
    Kawasaki, 
    Shibuya, 
    Tokyo, 
    N
};
const char* NameList[] = {
    "Yokohama",
    "MusashiKosugi",
    "Kawasaki",
    "Shibuya",
    "Tokyo"
};

int main()
{
    //経路と所要時間
    const std::vector<Edge> edges = {
        {Yokohama, MusashiKosugi}, 
        {Yokohama, Kawasaki},
        {MusashiKosugi, Kawasaki},
        {MusashiKosugi, Shibuya},
        {Kawasaki, Tokyo},
        {Shibuya, Tokyo}
    };
    const std::vector<int> weights = { 
        23,
        14,
        19,
        21,
        24,
        25
    };
    
    //最短経路の探索
    const Graph g(edges.begin(), edges.end(), weights.begin(), N);
    const Vertex from = Yokohama;
    const Vertex to = Tokyo;

    std::vector<Vertex> parents(num_vertices(g));
    std::vector<std::size_t> distance(num_vertices(g));
    dijkstra_shortest_paths(g, from, predecessor_map(&parents[0]).distance_map(&distance[0]));

    //探索結果をリスト化
    std::deque<Vertex> route;
    for (Vertex v = to; v != from; v = parents[v])
    {
        route.push_front(v);
    }
    route.push_front(from);
    for (const Vertex v : route)
    {
        std::cout << NameList[v] << std::endl;
    }
    return 0; 
}

これを実行すると最短経路が以下のようにリストアップされる。

Yokohama
Kawasaki
Tokyo

また最短経路の時間は、上記のコードの場合distance[to]に格納されている。また今回はグラフ構造が自明だったため今回は省いているけれど、経路が存在しないことは「parents[to] == to」が真となることの確認で判定できる。