プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌
(2014.7.12)
Boost.Graphで
JR全線乗り尽くしプラン
を立てる
H.Hiro @ Sapporo.cpp
Twitter: @h_hiro_
http...
自己紹介
●
某研究員
(もう学生じゃありません)
●
アルゴリズム作るのは本業です
●
趣味でもプログラム書いてます
●
C++とかRubyとか他にも何でも
●
最近はアマチュアサッカーの
観戦とかも
ソースコードは
こちらです
https://github.com/maraigue
/cpp-chinese-postman
今回の内容
JR線全線を乗り尽くす
地図:国土数値情報 鉄道データ N02-08
(2008年現在;JR以外の鉄道も入ってます)
鉄道ファンにとって
一つのステータス
これをなるべく
短い乗車距離で
実現したい
今回のルール
●
単に乗り尽くしたら終わりでは
なく、「出発地点まで帰って
くる」までの乗車距離で考える
●
出発地点まで戻るときも含め
JR以外の交通機関は
利用しないとする
予備知識
グラフ理論
●
グラフ:状態(頂点 vertex, node)と、
二つの状態を結ぶもの(辺 edge)からなる
データ構造(今回は駅が頂点、路線が辺)
●
各辺には、通ることで加算される利益や
損失の数値(重み)を与えることができる
(今回は距離を重みと...
補足
●
今回は、JR線が3方向以上に分岐している
駅のみ頂点とする。
●
今回は、辺は両方向に行き来できるもの
のみ扱う(無向グラフ undirected graph)。
一方通行は考えない
桑園 札幌 苗穂 白石 平和
厚別八軒
琴似
22...
Boost.Graph
Boost.Graph:
グラフ構造を扱うライブラリ
// 無向グラフを扱うために必要なものをインクルード
#include <boost/graph/undirected_graph.hpp>
// グラフの型の定義。
// 頂点から出る辺の...
Boost.Graph:
グラフ構造を扱うライブラリ
// 続き
int main(void){
Graph g;
// 頂点を追加
vertex_descriptor v1 = boost::add_vertex("桑園", g);
vert...
実際のコードを
見てみましょう
// 駅名をキー、頂点を値とする連想配列
std::map<std::string, RouteNetwork::vertex_descriptor> names2vertices;
while(!(ifs.eof())){
// (中略:ここ...
今回の
問題設定
今回の問題は、グラフ理論の
用語で言えば
●
無向グラフが与えられたとき
●
すべての辺を少なくとも1回通る
始点と終点が同一である経路で
●
経路の辺の重みの総和が最小に
なるものを求めよ。
ちなみにこの問題は
「中国人郵便配達問題」
って名前が付いてます
なお、Wikipediaの
「中国人郵便配達問題」
を書いた"Sinryow"は
私です
もう少し
グラフ理論について
必要なことを解説します
今回用いる
グラフ理論の概念(1)
橋
225
34
236
628
橋:
その辺1本が消えることで
路線網が分断される
(非連結になる)ような辺
函館
大沼
森
長万部
1256
五稜郭
中小国
↓青森
↑三厩
↑桑園
↓東室蘭
353
━━━━ :橋
━━━━ :橋でない
橋:
その辺1本が消えることで
路線網が分断される
(非連結になる)ような辺
➔
橋は明らかに2回通らないと
ならない
(全路線を通って出発地点に
戻る必要があるため)
今回用いる
グラフ理論の概念(2)
オイラーグラフ
オイラーグラフ:
スタート地点から一筆書き
(全ての辺をちょうど1回ずつ通る経路)で
スタート地点に戻ることのできるグラフ
オイラーグラフでない
(一筆書きはできるけど) オイラーグラフである
なぜオイラーグラフを考える?
➔
もしJRの路線網がオイラーグラフなら、
単に一筆書きになる(各区間1回ずつ通る)
ように辿るのが最短経路なのは明らか。
➔
ただ現実には、2回通らないとならない
区間が存在する。
【補足】いかなる区間も1回か2...
なぜオイラーグラフを考える?
➔
2回通ったところに辺が2本存在すると
仮定してオイラーグラフになれば
全線乗り尽して出発地点に戻れる。
➔
2回通る区間を極力少なくすればよい。
オイラーグラフでない オイラーグラフである
ここを
2本に増やす
オイラーグラフの特性
●
グラフのすべての頂点について、
繋がっている辺の数(次数)が偶数ならば
オイラーグラフである。逆も成り立つ。
●
一部の辺を2本に増やし、上記の条件を
満たせるようにすればよい。
オイラーグラフでない オイラーグラフで...
問題の解き方
1.
橋を見つけて個別のグラフに
切り離す。
橋は「2回通る」と結論付ける。
函館
五稜郭
中小国
大沼
森
長万部
室蘭
東室蘭
苫小牧
沼ノ端
追分
新得
夕張
新夕張
東釧路
根室
南千歳
新千歳空港
桑園 白石
新十津川
増毛
滝川
深...
1.
橋を見つけて個別のグラフに
切り離す。
橋は「2回通る」と結論付ける。
函館
五稜郭
中小国
大沼
森
長万部
室蘭
東室蘭
苫小牧
沼ノ端
追分
新得
夕張
新夕張
東釧路
根室
南千歳
新千歳空港
桑園 白石
新十津川
増毛
滝川
深...
橋を検出するアルゴリズム
http://nupioca.hatenadiary.jp/
entry/2013/11/03/200006
●
アルゴリズム自体はシンプルです
●
逆に、何でこのアルゴリズムでうまく
いくのかが不思議です(私も理解し...
2.
次数2以下の頂点を削除し辺を
統合する。(やらなくても
いいけど効率化のために)
894 新得254 新夕張追分
↓沼ノ端
↑岩見沢
↑富良野
↓東釧路
←南千歳
夕張
橋のため削除
2.
次数2以下の頂点を削除し辺を
統合する。(やらなくても
いいけど効率化のために)
新得254+894=1148追分
↓沼ノ端
↑岩見沢
↑富良野
↓東釧路
←南千歳
夕張
橋のため削除
2.
次数2以下の頂点を削除し辺を
統合する。(やらなくても
いいけど効率化のために)
函館
五稜郭
中小国
大沼
森
長万部
室蘭
東室蘭
苫小牧
沼ノ端
追分
新得
夕張
新夕張
東釧路
根室
南千歳
新千歳空港
桑園 白石
新十津川
増毛...
2.
次数2以下の頂点を削除し辺を
統合する。(やらなくても
いいけど効率化のために)
沼ノ端
追分
新得南千歳
白石
滝川
旭川
富良野
岩見沢
Boost.Graphだとこんな具合
// ---------- 変数宣言類は大幅に省略してます ----------
std::pair<vertex_iterator, vertex_iterator> vertex_range = boo...
3. (どこの辺を2本に増やすか決めたい)
分割された各グラフについて、
「すべての頂点の組に対する
最短距離」を求める
白 岩 滝 旭 富 新 南 沼 追
白
岩
滝
旭
富
新
南
沼
追
沼ノ端
追分
新得南千歳
白石
滝川
旭川
富良野...
白 岩 滝 旭 富 新 南 沼 追
白
岩
滝
旭
富
新
南
沼
追
この問題には「フロイド=ワーシャル法」と
いう有名なアルゴリズムがあって
Boost.Graphにも入っています
boost::floyd_warshall_all_pai...
4. (どこの辺を2本に増やすか決めたい)
次数が奇数の頂点を2つずつ
組み合わせ、さっき求めた距離の
和が最小になるものを見つける
沼ノ端
追分
新得南千歳
白石
滝川
旭川
富良野
岩見沢
●
(白石, 旭川)・(南千歳, 新得)・
(沼ノ...
4. (どこの辺を2本に増やすか決めたい)
次数が奇数の頂点を2つずつ
組み合わせ、さっき求めた距離の
和が最小になるものを見つける
沼ノ端
追分
新得南千歳
白石
滝川
旭川
富良野
岩見沢
最適な組み合わせ:
(白石, 岩見沢)・(南千歳,...
最適な「2つずつの組み合わせ方」
を求めるのは面倒
●
全通り組み合わせてみるのは、
組み合わせ爆発を起こしてしまう
●
「整数計画問題」という形に落とせば、
ライブラリに食わせて解ける
→今回は"GLPK"を使います
整数計画問題
●
変数に整数しか入らず、かつ
指定された制約式を満たすものの中で
特定の式を最大化/最小化する問題
制約式・最大化/最小化する式には
和・定数倍・等式/不等式のみ利用可能
●
変数が多いと一般には低速(組み合わせ爆発
が起きる)...
どう整数計画問題にするのか
●
変数:2つの駅の組み合わせ
(使ったもの:1、使わなかったもの:0)
●
制約式:どの駅も1回しか登場しない
●
最小化すべきもの:組み合わせにより
生じる移動距離の総和
最小化すべき式:777×白石滝川+348...
GLPKに解かせた結果
(JR北海道分)
沼ノ端
追分
新得
南千歳
白石
滝川
旭川
富良野
岩見沢
GLPKに解かせた結果
(JR全国分)
GLPKに解かせた結果
(JR全国分)
メモリ不足で
解けませんでした…。
頂点数140くらい、
考えるべき辺が10,000組くらい
最終結果:JR北海道全線
(橋と、最小距離組み合わせの結果出た辺のみ2回通ればよい)
函館
五稜郭
中小国
大沼
森
長万部
室蘭
東室蘭
苫小牧
沼ノ端
追分
新得
夕張
新夕張
東釧路
根室
南千歳
新千歳空港
桑園 白石
新十津川
増毛
...
最終結果:JR北海道全線
(橋と、最小距離組み合わせの結果出た辺のみ2回通ればよい)
函館
五稜郭
中小国
大沼
森
長万部
室蘭
東室蘭
苫小牧
沼ノ端
追分
新得
夕張
新夕張
東釧路
根室
南千歳
新千歳空港
桑園 白石
新十津川
増毛
...
補足:
●
「2回通る必要のある辺」が求められれば、
実際にどの経路で辿ればよいかは勝手に
決まる。
●
オイラーグラフの特徴として、「辿れなく
なる頂点が存在しなくならない限りは、
適当に辿っていても一筆書きになる」
というものがあるため。
実際に
プログラムで
解いてみます
参考文献・資料:
●
アルゴリズムの大枠の解説:
「経営科学OR用語大事典」
Saul I. Gass、Carl M. Harris
森村英典[監訳]、刀根薫[監訳]、伊理正夫[監訳]
朝倉書店 ISBN 4254121318
●
グラフ理論の...
参考文献・資料:
●
もっと難しい(理論的に考えたときに
計算時間がかかる)問題:
「JRの切符として買える最長のものを求める」
(最長片道切符)
http://www.swa.gr.jp/lop/
ありがとう
ございました
Upcoming SlideShare
Loading in...5
×

Boost.GraphでJR全線乗り尽くしプランを立てる - プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌 (2014.7.12)

2,455

Published on

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,455
On Slideshare
0
From Embeds
0
Number of Embeds
11
Actions
Shares
0
Downloads
9
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Transcript of "Boost.GraphでJR全線乗り尽くしプランを立てる - プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌 (2014.7.12)"

  1. 1. プログラミング生放送+CLR/H+Sapporo.cpp 勉強会@札幌 (2014.7.12) Boost.Graphで JR全線乗り尽くしプラン を立てる H.Hiro @ Sapporo.cpp Twitter: @h_hiro_ http://hhiro.net/about/
  2. 2. 自己紹介
  3. 3. ● 某研究員 (もう学生じゃありません) ● アルゴリズム作るのは本業です ● 趣味でもプログラム書いてます ● C++とかRubyとか他にも何でも ● 最近はアマチュアサッカーの 観戦とかも
  4. 4. ソースコードは こちらです https://github.com/maraigue /cpp-chinese-postman
  5. 5. 今回の内容
  6. 6. JR線全線を乗り尽くす 地図:国土数値情報 鉄道データ N02-08 (2008年現在;JR以外の鉄道も入ってます)
  7. 7. 鉄道ファンにとって 一つのステータス
  8. 8. これをなるべく 短い乗車距離で 実現したい
  9. 9. 今回のルール
  10. 10. ● 単に乗り尽くしたら終わりでは なく、「出発地点まで帰って くる」までの乗車距離で考える ● 出発地点まで戻るときも含め JR以外の交通機関は 利用しないとする
  11. 11. 予備知識
  12. 12. グラフ理論
  13. 13. ● グラフ:状態(頂点 vertex, node)と、 二つの状態を結ぶもの(辺 edge)からなる データ構造(今回は駅が頂点、路線が辺) ● 各辺には、通ることで加算される利益や 損失の数値(重み)を与えることができる (今回は距離を重みとし損失とみなす) 桑園 札幌 苗穂 白石 平和 厚別八軒 琴似 22 16 22 36 4422 22 (辺の重みに書かれた  距離の単位は「0.1km」)
  14. 14. 補足 ● 今回は、JR線が3方向以上に分岐している 駅のみ頂点とする。 ● 今回は、辺は両方向に行き来できるもの のみ扱う(無向グラフ undirected graph)。 一方通行は考えない 桑園 札幌 苗穂 白石 平和 厚別八軒 琴似 22 16 22 36 4422 22 (辺の重みに書かれた  距離の単位は「0.1km」)
  15. 15. Boost.Graph
  16. 16. Boost.Graph: グラフ構造を扱うライブラリ // 無向グラフを扱うために必要なものをインクルード #include <boost/graph/undirected_graph.hpp> // グラフの型の定義。 // 頂点から出る辺の一覧はstd::vector、頂点の一覧はstd::set、 // 無向グラフ、頂点には駅名、辺にはint型で重みを付与 typedef boost::adjacency_list<boost::vecS, boost::setS, boost::undirectedS, boost::property<boost::vertex_name_t, std::string>, boost::property<boost::edge_weight_t, int> > Graph; // 続く
  17. 17. Boost.Graph: グラフ構造を扱うライブラリ // 続き int main(void){ Graph g; // 頂点を追加 vertex_descriptor v1 = boost::add_vertex("桑園", g); vertex_descriptor v2 = boost::add_vertex("白石", g); // 辺を追加 // 第3パラメータは辺の属性(ここでは重み=距離) edge_descriptor e1 = boost::add_edge(v1, v2, 74, g); }
  18. 18. 実際のコードを 見てみましょう
  19. 19. // 駅名をキー、頂点を値とする連想配列 std::map<std::string, RouteNetwork::vertex_descriptor> names2vertices; while(!(ifs.eof())){ // (中略:ここで行を読み込む) // s[0] と s[1] に地点名が、 distance に距離が入る // 頂点を追加(まだ存在していないなら) for(size_t i = 0; i <= 1; ++i){ it = names2vertices.find(s[i]); if(it == names2vertices.end()){ vd[i] = boost::add_vertex(s[i], *this); names2vertices.insert(std::make_pair(s[i], vd[i])); }else{ vd[i] = it->second; } } // 辺を追加 boost::add_edge(vd[0], vd[1], distance, *this); total_distance += distance; }
  20. 20. 今回の 問題設定
  21. 21. 今回の問題は、グラフ理論の 用語で言えば ● 無向グラフが与えられたとき ● すべての辺を少なくとも1回通る 始点と終点が同一である経路で ● 経路の辺の重みの総和が最小に なるものを求めよ。
  22. 22. ちなみにこの問題は 「中国人郵便配達問題」 って名前が付いてます
  23. 23. なお、Wikipediaの 「中国人郵便配達問題」 を書いた"Sinryow"は 私です
  24. 24. もう少し グラフ理論について 必要なことを解説します
  25. 25. 今回用いる グラフ理論の概念(1) 橋
  26. 26. 225 34 236 628 橋: その辺1本が消えることで 路線網が分断される (非連結になる)ような辺 函館 大沼 森 長万部 1256 五稜郭 中小国 ↓青森 ↑三厩 ↑桑園 ↓東室蘭 353 ━━━━ :橋 ━━━━ :橋でない
  27. 27. 橋: その辺1本が消えることで 路線網が分断される (非連結になる)ような辺 ➔ 橋は明らかに2回通らないと ならない (全路線を通って出発地点に 戻る必要があるため)
  28. 28. 今回用いる グラフ理論の概念(2) オイラーグラフ
  29. 29. オイラーグラフ: スタート地点から一筆書き (全ての辺をちょうど1回ずつ通る経路)で スタート地点に戻ることのできるグラフ オイラーグラフでない (一筆書きはできるけど) オイラーグラフである
  30. 30. なぜオイラーグラフを考える? ➔ もしJRの路線網がオイラーグラフなら、 単に一筆書きになる(各区間1回ずつ通る) ように辿るのが最短経路なのは明らか。 ➔ ただ現実には、2回通らないとならない 区間が存在する。 【補足】いかなる区間も1回か2回通ればよく、3回通る 必要はない。(証明は省略)
  31. 31. なぜオイラーグラフを考える? ➔ 2回通ったところに辺が2本存在すると 仮定してオイラーグラフになれば 全線乗り尽して出発地点に戻れる。 ➔ 2回通る区間を極力少なくすればよい。 オイラーグラフでない オイラーグラフである ここを 2本に増やす
  32. 32. オイラーグラフの特性 ● グラフのすべての頂点について、 繋がっている辺の数(次数)が偶数ならば オイラーグラフである。逆も成り立つ。 ● 一部の辺を2本に増やし、上記の条件を 満たせるようにすればよい。 オイラーグラフでない オイラーグラフである 次数3 次数3 次数2 次数2 次数2 次数2 次数2 次数2 次数4 次数4
  33. 33. 問題の解き方
  34. 34. 1. 橋を見つけて個別のグラフに 切り離す。 橋は「2回通る」と結論付ける。 函館 五稜郭 中小国 大沼 森 長万部 室蘭 東室蘭 苫小牧 沼ノ端 追分 新得 夕張 新夕張 東釧路 根室 南千歳 新千歳空港 桑園 白石 新十津川 増毛 滝川 深川 旭川 新旭川 稚内 富良野 様似 岩見沢
  35. 35. 1. 橋を見つけて個別のグラフに 切り離す。 橋は「2回通る」と結論付ける。 函館 五稜郭 中小国 大沼 森 長万部 室蘭 東室蘭 苫小牧 沼ノ端 追分 新得 夕張 新夕張 東釧路 根室 南千歳 新千歳空港 桑園 白石 新十津川 増毛 滝川 深川 旭川 新旭川 稚内 富良野 様似 岩見沢
  36. 36. 橋を検出するアルゴリズム http://nupioca.hatenadiary.jp/ entry/2013/11/03/200006 ● アルゴリズム自体はシンプルです ● 逆に、何でこのアルゴリズムでうまく いくのかが不思議です(私も理解してません)
  37. 37. 2. 次数2以下の頂点を削除し辺を 統合する。(やらなくても いいけど効率化のために) 894 新得254 新夕張追分 ↓沼ノ端 ↑岩見沢 ↑富良野 ↓東釧路 ←南千歳 夕張 橋のため削除
  38. 38. 2. 次数2以下の頂点を削除し辺を 統合する。(やらなくても いいけど効率化のために) 新得254+894=1148追分 ↓沼ノ端 ↑岩見沢 ↑富良野 ↓東釧路 ←南千歳 夕張 橋のため削除
  39. 39. 2. 次数2以下の頂点を削除し辺を 統合する。(やらなくても いいけど効率化のために) 函館 五稜郭 中小国 大沼 森 長万部 室蘭 東室蘭 苫小牧 沼ノ端 追分 新得 夕張 新夕張 東釧路 根室 南千歳 新千歳空港 桑園 白石 新十津川 増毛 滝川 深川 旭川 新旭川 稚内 富良野 様似 岩見沢
  40. 40. 2. 次数2以下の頂点を削除し辺を 統合する。(やらなくても いいけど効率化のために) 沼ノ端 追分 新得南千歳 白石 滝川 旭川 富良野 岩見沢
  41. 41. Boost.Graphだとこんな具合 // ---------- 変数宣言類は大幅に省略してます ---------- std::pair<vertex_iterator, vertex_iterator> vertex_range = boost::vertices(*this); for(vertex_iterator itv = vertex_range.first; itv != vertex_range.second; ++itv){ if(out_degree(*itv, *this) == 2){ // 次数が2の頂点があったら // 隣接する頂点と辺を覚えておき std::pair<out_edge_iterator, out_edge_iterator> edge_range = boost::out_edges(*itv, *this); for(out_edge_iterator ite = edge_range.first; ite != edge_range.second; ++ite, ++i){ sides[i] = vertex_target(*itv, *ite); distance += boost::get(boost::edge_weight, *this, *ite); links[i] = *ite; } // 元の辺を取り除くとともに新しく辺を加える boost::add_edge(sides[0], sides[1], distance, *this); boost::remove_edge(links[0], *this); boost::remove_edge(links[1], *this); } }
  42. 42. 3. (どこの辺を2本に増やすか決めたい) 分割された各グラフについて、 「すべての頂点の組に対する 最短距離」を求める 白 岩 滝 旭 富 新 南 沼 追 白 岩 滝 旭 富 新 南 沼 追 沼ノ端 追分 新得南千歳 白石 滝川 旭川 富良野 岩見沢
  43. 43. 白 岩 滝 旭 富 新 南 沼 追 白 岩 滝 旭 富 新 南 沼 追 この問題には「フロイド=ワーシャル法」と いう有名なアルゴリズムがあって Boost.Graphにも入っています boost::floyd_warshall_all_pairs_shortest_paths(graph, result); resultは「result[vertex1][vertex2] としたときに辺の重みが返る」 ものなら何でもよい。例えばstd::map< vertex_descriptor, std::map<vertex_descriptor, int> > (辺の重みがintの場合) 沼ノ端 追分 新得南千歳 白石 滝川 旭川 富良野 岩見沢
  44. 44. 4. (どこの辺を2本に増やすか決めたい) 次数が奇数の頂点を2つずつ 組み合わせ、さっき求めた距離の 和が最小になるものを見つける 沼ノ端 追分 新得南千歳 白石 滝川 旭川 富良野 岩見沢 ● (白石, 旭川)・(南千歳, 新得)・ (沼ノ端, 岩見沢)・(滝川, 富良野) ● (白石, 南千歳)・(沼ノ端, 岩見沢)・ (滝川, 新得)・(旭川, 富良野) …
  45. 45. 4. (どこの辺を2本に増やすか決めたい) 次数が奇数の頂点を2つずつ 組み合わせ、さっき求めた距離の 和が最小になるものを見つける 沼ノ端 追分 新得南千歳 白石 滝川 旭川 富良野 岩見沢 最適な組み合わせ: (白石, 岩見沢)・(南千歳, 沼ノ端)・ (滝川, 旭川)・(富良野, 新得)
  46. 46. 最適な「2つずつの組み合わせ方」 を求めるのは面倒 ● 全通り組み合わせてみるのは、 組み合わせ爆発を起こしてしまう ● 「整数計画問題」という形に落とせば、 ライブラリに食わせて解ける →今回は"GLPK"を使います
  47. 47. 整数計画問題 ● 変数に整数しか入らず、かつ 指定された制約式を満たすものの中で 特定の式を最大化/最小化する問題 制約式・最大化/最小化する式には 和・定数倍・等式/不等式のみ利用可能 ● 変数が多いと一般には低速(組み合わせ爆発 が起きる)だが、多くの場合に高速 ● 今回の場合は、頂点数の3乗に比例する 程度の時間で解ける(っぽい) ● GLPK:これを解いてくれるライブラリ
  48. 48. どう整数計画問題にするのか ● 変数:2つの駅の組み合わせ (使ったもの:1、使わなかったもの:0) ● 制約式:どの駅も1回しか登場しない ● 最小化すべきもの:組み合わせにより 生じる移動距離の総和 最小化すべき式:777×白石滝川+348×白石岩見沢+… // 「組の距離×使った辺」を最小にする。各組の距離はさっき求めた 0≦白石滝川≦1、0≦白石岩見沢≦1、0≦白石沼ノ端≦1、… // 各組み合わせは「使うか使わないか」の二つだけ 白石滝川+白石岩見沢+白石沼ノ端+…=1 白石滝川+滝川岩見沢+滝川沼ノ端+…=1 // 各駅とも、1回の組み合わせにしか登場しない
  49. 49. GLPKに解かせた結果 (JR北海道分) 沼ノ端 追分 新得 南千歳 白石 滝川 旭川 富良野 岩見沢
  50. 50. GLPKに解かせた結果 (JR全国分)
  51. 51. GLPKに解かせた結果 (JR全国分) メモリ不足で 解けませんでした…。 頂点数140くらい、 考えるべき辺が10,000組くらい
  52. 52. 最終結果:JR北海道全線 (橋と、最小距離組み合わせの結果出た辺のみ2回通ればよい) 函館 五稜郭 中小国 大沼 森 長万部 室蘭 東室蘭 苫小牧 沼ノ端 追分 新得 夕張 新夕張 東釧路 根室 南千歳 新千歳空港 桑園 白石 新十津川 増毛 滝川 深川 旭川 新旭川 稚内 富良野 様似 岩見沢 ━━━━:2回通る ━━━━:1回通る JR北海道の総距離 2457.7km(営業キロ) 左図の総乗車距離 3565.0km(同) 総距離の145%
  53. 53. 最終結果:JR北海道全線 (橋と、最小距離組み合わせの結果出た辺のみ2回通ればよい) 函館 五稜郭 中小国 大沼 森 長万部 室蘭 東室蘭 苫小牧 沼ノ端 追分 新得 夕張 新夕張 東釧路 根室 南千歳 新千歳空港 桑園 白石 新十津川 増毛 滝川 深川 旭川 新旭川 稚内 富良野 様似 岩見沢 一筆書き経路の例 白石→桑園→新十津川→桑園 →長万部→森→大沼→五稜郭 →函館→五稜郭→中小国→五稜郭 →大沼→森→長万部→東室蘭 →室蘭→東室蘭→苫小牧→様似 →苫小牧→沼ノ端→追分→岩見沢 →白石→南千歳→沼ノ端→南千歳 →新千歳空港→南千歳→追分 →新夕張→夕張→新夕張→新得 →東釧路→根室→東釧路→新旭川 →稚内→新旭川→旭川→深川 →増毛→深川→滝川→富良野 →新得→富良野→旭川→深川 →滝川→岩見沢→白石
  54. 54. 補足: ● 「2回通る必要のある辺」が求められれば、 実際にどの経路で辿ればよいかは勝手に 決まる。 ● オイラーグラフの特徴として、「辿れなく なる頂点が存在しなくならない限りは、 適当に辿っていても一筆書きになる」 というものがあるため。
  55. 55. 実際に プログラムで 解いてみます
  56. 56. 参考文献・資料: ● アルゴリズムの大枠の解説: 「経営科学OR用語大事典」 Saul I. Gass、Carl M. Harris 森村英典[監訳]、刀根薫[監訳]、伊理正夫[監訳] 朝倉書店 ISBN 4254121318 ● グラフ理論の基礎: 「グラフ理論入門(原書第4版)」 Robin J. Wilson、西関隆夫[訳]・西関裕子[訳] 近代科学社 ISBN 4764902966 参考資料: http://ocw.hokudai.ac.jp/Course/Faculty/Engineering /GraphTheory/2007/
  57. 57. 参考文献・資料: ● もっと難しい(理論的に考えたときに 計算時間がかかる)問題: 「JRの切符として買える最長のものを求める」 (最長片道切符) http://www.swa.gr.jp/lop/
  58. 58. ありがとう ございました
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×