2016/02/16
プログラミングの基礎 16.4 最初の完動プログラム
プログラミングの基礎で作ったメトロネットワーク最短路問題の解答.
ダイクストラ法を使い求める.
ここまでのメトロネットワーク最短路問題に関係する問題の解答すべてここに書いてある.
(* サポートページからダウンロードしたglobal_ekimei_listとglobal_ekikan_list *)
#use "metro.ml"
(* 目的:ekimei_t型のデータを受け取り,「路線名,駅名(かな)」を返す *)
(* hyoji : ekimei_t -> string *)
let hyoji ekimei = match ekimei with
{kanji = kanji;
kana = kana;
romaji = romaji;
shozoku = shozoku;}
-> shozoku ^ "," ^ kanji ^ "(" ^ kana ^ ")";;
(* hyoji test *)
print_string "hyouji test"
let test1 = hyoji {kanji = "茗荷谷"; kana = "みょうがだに";
romaji = "myogadani"; shozoku = "丸ノ内線"}
= "丸ノ内線,茗荷谷(みょうがだに)"
(* 目的:ローマ字の駅名(文字列)と駅名リスト(ekimei_t list 型)を受け取り
園駅の漢字表記を返す *)
(* romaji_to_kanji -> string -> ekimei_t list -> string *)
let rec romaji_to_kanji name lst = match lst with
[] -> ""
| {kanji = kanji; kana = kana; romaji = romaji; shozoku = shozoku} :: rest ->
if name = romaji
then kanji
else romaji_to_kanji name rest
(* romaji_to_kanji test *)
let test1 = romaji_to_kanji "" [] = ""
let test2 = romaji_to_kanji "" global_ekimei_list = ""
let test3 = romaji_to_kanji "myogadani" [] = ""
let test4 = romaji_to_kanji "myogadani" global_ekimei_list = "茗荷谷"
let test5 = romaji_to_kanji "aoyamaicchome" global_ekimei_list = "青山一丁目"
let test6 = romaji_to_kanji "heiwadai" global_ekimei_list = "平和台"
(* 目的:漢字の駅名を2つと駅間リストを受け取ったら,駅間リストの中からその2駅間の距離を返す *)
(* get_ekikan_kyori : string -> string -> ekikan_t list -> float *)
let rec get_ekikan_kyori eki1 eki2 lst = match lst with
[] -> infinity
| {kiten = kiten; shuten = shuten; keiyu = keiyu; kyori = kyori; jikan = jikan} :: rest ->
if eki1 = kiten && eki2 = shuten
then kyori
else if eki2 = kiten && eki1 = shuten
then kyori
else get_ekikan_kyori eki1 eki2 rest
(* get_ekikan_kyori test *)
let test1 = get_ekikan_kyori "錦糸町" "住吉" [] = infinity
let test2 = get_ekikan_kyori "錦糸町" "" global_ekikan_list = infinity
let test3 = get_ekikan_kyori "" "錦糸町" global_ekikan_list = infinity
let test4 = get_ekikan_kyori "横浜駅" "錦糸町" global_ekikan_list = infinity
let test5 = get_ekikan_kyori "大手町" "三越前" global_ekikan_list = 0.7
let test6 = get_ekikan_kyori "三越前" "大手町" global_ekikan_list = 0.7
let test7 = get_ekikan_kyori "霞ヶ関" "日比谷" global_ekikan_list = 1.2
(* 目的:ローマ字の駅名を2つ受け取り,その間の距離を調べ,つながっている場合は
「A駅からB駅までのはxkmです」と返し,繋がっていない場合は
「A駅からB駅はつながっていません」と返す*)
let kyori_wo_hyoji r1 r2 =
let k1 = romaji_to_kanji r1 global_ekimei_list in
let k2 = romaji_to_kanji r2 global_ekimei_list in
let not_exist = "という駅は存在しません" in
if k1 = ""
then r1 ^ not_exist
else if k2 = ""
then r2 ^ not_exist
else let kyori = get_ekikan_kyori k1 k2 global_ekikan_list in
if kyori = infinity
then k1 ^ "駅と" ^ k2 ^ "駅はつながっていません"
else k1 ^ "駅から" ^ k2 ^ "駅までは" ^ string_of_float kyori ^ "kmです"
(* kyori_wo_hyoji test *)
let test1 = kyori_wo_hyoji "otemachi" "hibiya" = "大手町駅と日比谷駅はつながっていません"
let test2 = kyori_wo_hyoji "" "kinsityo" = "という駅は存在しません"
let test3 = kyori_wo_hyoji "yokohama" "kinsityo" = "yokohamaという駅は存在しません"
let test4 = kyori_wo_hyoji "otemachi" "mitsukoshimae" = "大手町駅から三越前駅までは0.7kmです"
let test5 = kyori_wo_hyoji "mitsukoshimae" "otemachi" = "三越前駅から大手町駅までは0.7kmです"
let test6 = kyori_wo_hyoji "kasumigaseki" "hibiya" = "霞ヶ関駅から日比谷駅までは1.2kmです"
type eki_t = {
namae : string; (* 駅名(漢字の文字列) *)
saitan_kyori : float; (* 最短距離(実数) *)
temae_list : string list; (* 駅名(漢字の文字列)のリスト *)
}
(* 目的:string型の駅名(漢字)とekimei_t list型を受け取り,
ekimei_t listをeki_t listに変え,その際駅名と一致する駅についてはshokikaする *)
(* make_initial_eki_list : string -> ekimei_t list -> eki_t list *)
let make_initial_eki_list name lst =
List.map (fun eki -> match eki with
{kanji = k; kana = a; romaji = r; shozoku = s}
-> if k = name
then {namae = k; saitan_kyori = 0.; temae_list = [k]}
else {namae = k; saitan_kyori = infinity; temae_list = []})
lst;;
(* make_initial_eki_list test *)
let test1 = make_initial_eki_list "代々木上原" [{kanji="代々木上原"; kana="よよぎうえはら";
romaji="yoyogiuehara"; shozoku="千代田線"}]
= [{namae = "代々木上原"; saitan_kyori = 0.; temae_list = ["代々木上原"]}];;
let test2 = make_initial_eki_list "明治神宮前" [{kanji="代々木公園"; kana="よよぎこうえん";
romaji="yoyogikouen"; shozoku="千代田線"};
{kanji="明治神宮前"; kana="めいじじんぐうまえ";
romaji="meijijinguumae"; shozoku="千代田線"}]
= [{namae = "代々木公園"; saitan_kyori = infinity; temae_list = []};
{namae = "明治神宮前"; saitan_kyori = 0.; temae_list = ["明治神宮前"]}];;
let test3 = make_initial_eki_list "赤坂" [{kanji="表参道"; kana="おもてさんどう";
romaji="omotesandou"; shozoku="千代田線"};
{kanji="乃木坂"; kana="のぎざか";
romaji="nogizaka"; shozoku="千代田線"};
{kanji="赤坂"; kana="あかさか";
romaji="akasaka"; shozoku="千代田線"}]
= [{namae = "表参道"; saitan_kyori = infinity; temae_list = []};
{namae = "乃木坂"; saitan_kyori = infinity; temae_list = []};
{namae = "赤坂"; saitan_kyori = 0.; temae_list = ["赤坂"]}];;
(* 目的:ekimei_t型のレコードとekimei_t型のリストを受け取ったら,平仮名の昇順となる位置に
ekimei_t型のレコードを挿入する.同じ駅がリストにあれば挿入しない.
seiretsuのための補助関数*)
(* ekimei_isnert : -> ekimei_t -> ekimei_t list -> ekimei_t list *)
let rec ekimei_insert eki lst = match lst with
[] -> [eki]
| ({kanji = kanji; kana = kana; romaji = romaji; shozoku = shozoku;} as first) :: rest ->
if kana = eki.kana
then lst (* リストにあったほうが残る *)
else if eki.kana < kana (* 駅のkana < first のkana *)
then eki :: lst
else first :: ekimei_insert eki rest
(* test data *)
let yoyogiuehara_tiyodasen = {kanji="代々木上原"; kana="よよぎうえはら";
romaji="yoyogiuehara"; shozoku="千代田線"}
let otemachi_tiyodasen = {kanji="大手町"; kana="おおてまち";
romaji="otemachi"; shozoku="千代田線"}
let otemachi_hanzoumonsen = {kanji="大手町"; kana="おおてまち";
romaji="otemachi"; shozoku="半蔵門線"}
(* test *)
let test1 = ekimei_insert otemachi_tiyodasen [] = [otemachi_tiyodasen]
let test2 = ekimei_insert otemachi_tiyodasen [otemachi_hanzoumonsen] = [otemachi_hanzoumonsen]
let test3 = ekimei_insert otemachi_tiyodasen [yoyogiuehara_tiyodasen]
= [otemachi_tiyodasen; yoyogiuehara_tiyodasen]
let test4 = ekimei_insert yoyogiuehara_tiyodasen [otemachi_tiyodasen]
= [otemachi_tiyodasen; yoyogiuehara_tiyodasen]
(* 目的:ekimei_t型のリストを受け取ったら,それを平仮名の順に整列し,
さらに駅の重複を取り除いたekimei_t型のリストを返す *)
(* seiretsu : ekimei_t -> ekimei_t *)
let rec seiretsu lst = match lst with
[] -> []
| first :: rest -> ekimei_insert first (seiretsu rest)
let test1 = seiretsu [] = []
let test2 = [yoyogiuehara_tiyodasen]
= [yoyogiuehara_tiyodasen]
let test3 = seiretsu [otemachi_tiyodasen; otemachi_hanzoumonsen]
= [otemachi_hanzoumonsen]
let test4 = seiretsu [otemachi_tiyodasen; otemachi_hanzoumonsen; yoyogiuehara_tiyodasen]
= [otemachi_hanzoumonsen; yoyogiuehara_tiyodasen]
let otemachi = {namae = "大手町"; saitan_kyori = 0.; temae_list = ["大手町"]}
let mitsukoshimae = {namae = "三越前"; saitan_kyori = infinity; temae_list = []}
let shibuya = {namae = "渋谷"; saitan_kyori = infinity; temae_list = []}
let aoyamaichome = {namae = "青山一丁目"; saitan_kyori = infinity; temae_list = []}
(* 目的:直前に確定した駅 p (eki_t型)と味覚手の役のリスト v (eki_t list型)を受け取り
必要な更新処理を行った後の未確定の駅のリストを返す*)
(* koushin -> eki_t -> eki_t list -> ekikan_t list -> eki_t list *)
let koushin p v ekikan =
List.map
(fun q ->
let kyori = get_ekikan_kyori p.namae q.namae ekikan in
if kyori = infinity
then q
else let p_keiyu_q_kyori = p.saitan_kyori +. kyori in
if p_keiyu_q_kyori < q.saitan_kyori
then { namae = q.namae; saitan_kyori = p_keiyu_q_kyori;
temae_list =(q.namae) :: p.temae_list}
else q)
v;;
(* test data *)
let otemachi = {namae = "大手町"; saitan_kyori = 0.; temae_list = ["大手町"]}
let mitsukoshimae = {namae = "三越前"; saitan_kyori = infinity; temae_list = []}
let shibuya = {namae = "渋谷"; saitan_kyori = infinity; temae_list = []}
let aoyamaichome = {namae = "青山一丁目"; saitan_kyori = infinity; temae_list = []}
let nagatacho = {namae = "永田町"; saitan_kyori = infinity; temae_list = []}
let shinochanomizu = {namae = "新御茶ノ水"; saitan_kyori = infinity; temae_list = []}
(* koushin test *)
let koushin_test1 = koushin otemachi
[mitsukoshimae; shibuya; shinochanomizu; aoyamaichome] global_ekikan_list
=[{namae = "三越前"; saitan_kyori = 0.7;
temae_list = ["三越前"; "大手町"]};
{namae = "渋谷"; saitan_kyori = infinity; temae_list = []};
{namae = "新御茶ノ水"; saitan_kyori = 1.3;
temae_list = ["新御茶ノ水"; "大手町"]};
{namae = "青山一丁目"; saitan_kyori = infinity; temae_list = []}];;
(* 目的:eki_t list型のリストを受け取り,「最短距離最小の駅」と
「最短距離最小の駅以外からなるリスト」の組を返す *)
(* saitan_wo_bunri : eki_t list -> eki_t * eki_t list *)
let saitan_wo_bunri eki_list =
List.fold_right
(fun first rest_saitan ->
let (minimum, lst) = rest_saitan in
if minimum.namae = ""
then (first, lst)
else if first.saitan_kyori <= minimum.saitan_kyori
then (first, minimum :: lst)
else (minimum, first :: lst))
eki_list
({namae = ""; saitan_kyori = infinity; temae_list = []}, [])
let test1 = saitan_wo_bunri [{namae = "三越前"; saitan_kyori = 0.7;
temae_list = ["三越前"; "大手町"]};
{namae = "渋谷"; saitan_kyori = 0.3; temae_list = ["渋谷"]};
{namae = "新御茶ノ水"; saitan_kyori = 1.3;
temae_list = ["新御茶ノ水"; "大手町"]};
{namae = "青山一丁目"; saitan_kyori = infinity;
temae_list = []}]
=({namae = "渋谷"; saitan_kyori = 0.3; temae_list = ["渋谷"]},
[{namae = "三越前"; saitan_kyori = 0.7;
temae_list = ["三越前"; "大手町"]};
{namae = "新御茶ノ水"; saitan_kyori = 1.3;
temae_list = ["新御茶ノ水"; "大手町"]};
{namae = "青山一丁目"; saitan_kyori = infinity; temae_list = []}]);;
(* 目的:eki_t list型の未確定の駅のリストとekikan_t list型の駅間のリストを受け取り
ダイクストラのアルゴリズムにしたがって各駅について
最短距離と最短経路が正しく入ったリストを返す *)
(* dijkstra_main : eki_t list -> ekikan_t list -> eki_t list *)
let rec dijkstra_main eki_list ekikan = match eki_list with
[] -> []
| first :: rest -> match (saitan_wo_bunri eki_list) with
(saitan1, []) -> [saitan1]
| (saitan2, rest) -> saitan2 :: (dijkstra_main (koushin saitan2 rest ekikan) ekikan) ;;
(* test_data *)
let otemachi = {namae = "大手町"; saitan_kyori = 1.7; temae_list = ["神保町"]};;
let mitsukoshimae = {namae = "三越前"; saitan_kyori = infinity; temae_list = []};;
let suitenguumae = {namae = "水天宮前"; saitan_kyori = infinity; temae_list = []};;
let kiyosumishirakawa = {namae = "清澄白河"; saitan_kyori = infinity; temae_list = []};;
let sumiyoshi = {namae = "住吉"; saitan_kyori = infinity; temae_list = []};;
let dijkstra_test1 = dijkstra_main [otemachi; mitsukoshimae; suitenguumae;
kiyosumishirakawa; sumiyoshi]
global_ekikan_list
= [{namae = "大手町"; saitan_kyori = 1.7; temae_list = ["神保町"]};
{namae = "三越前"; saitan_kyori = 2.4;
temae_list = ["三越前"; "神保町"]};
{namae = "水天宮前"; saitan_kyori = 3.7;
temae_list = ["水天宮前"; "三越前"; "神保町"]};
{namae = "清澄白河"; saitan_kyori = 5.4;
temae_list = ["清澄白河"; "水天宮前"; "三越前"; "神保町"]};
{namae = "住吉"; saitan_kyori = 7.30000000000000071;
temae_list =
["住吉"; "清澄白河"; "水天宮前"; "三越前"; "神保町"]}];;
(* 目的:始点の駅名(ローマ字の文字列)と終点の駅名(ローマ字の文字列)を受け取り
seiretsuを使ってglobal_ekimei_list の重複を取り除き,
romaji_to_kanji を使って始点と終点の漢字表記を求め
make_initial_eki_listを使って駅のリストを作り,
dijkstra_mainを使って各駅までの最短路を確定し,
その中空終点の駅のレコード(eki_t型)を返す*)
(* dijkstra : string -> string -> eki_t *)
let dijkstra siten shuten =
let sorted_ekimei_list = seiretsu global_ekimei_list in
let siten_kanji = romaji_to_kanji siten sorted_ekimei_list in
let shuten_kanji = romaji_to_kanji shuten sorted_ekimei_list in
let initialized_list = make_initial_eki_list siten_kanji sorted_ekimei_list in
let saitan_list = dijkstra_main initialized_list global_ekikan_list in
let rec serch item lst = match lst with
[] -> {namae = ""; saitan_kyori = infinity; temae_list = []}
| first :: rest ->
if first.namae = item
then first
else serch item rest
in serch shuten_kanji saitan_list;;
(* test *)
(* サポートページからのコピペ *)
let test1 = dijkstra "shibuya" "gokokuji" =
{namae = "護国寺"; saitan_kyori = 9.8;
temae_list =
["護国寺"; "江戸川橋"; "飯田橋"; "市ヶ谷"; "麹町"; "永田町";
"青山一丁目"; "表参道"; "渋谷"]}
let test2 = dijkstra "myogadani" "meguro" =
{namae = "目黒"; saitan_kyori = 12.7000000000000028;
temae_list =
["目黒"; "白金台"; "白金高輪"; "麻布十番"; "六本木一丁目"; "溜池山王";
"永田町"; "麹町"; "市ヶ谷"; "飯田橋"; "後楽園"; "茗荷谷"]}