※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

2010/8/5
架空鉄道の話。
ネット上に柏鉄鉄道という路線の設定を作っているテツオタの方がいまして、リンク先のような路線図を作っていました。
http://www.pixiv.net/member_illust.php?mode=medium&illust_id=8264767
http://www.pixiv.net/member_illust.php?mode=medium&illust_id=11922112

このデータから下記リンク先のような運賃表を完成させたいそうなのです。
http://www.pixiv.net/member_illust.php?mode=medium&illust_id=12044821


架空の路線や運賃表を作って楽しむ人、完成した表をみて楽しむ人。
僕には何が面白いのか、理解できない世界ですが、話を聞いてみると運賃表の作成どうやら手作業で行っていて大変なようです。


コメントをやり取りしてみるとデータは静的、信頼できないデータは来ないし外部センサーが不調になる可能性も考えなくていい、ユーザーがでたらめな操作もしない。
プログラムとしてはとても楽な話のようです。
C#の勉強もかねて運賃表を作成することにしてみました。





コードは、指定した2駅間の最短路線距離を経路探索で求める処理です。
理屈では、環状線だらけの路線図でも対応できる処理になっています。



  • 運賃表算出過程

鉄道データ.csv
まずは上記データが路線のつながり方と距離を表現しておりましたのでこれを加工しまして、全ての駅の隣駅とそこまでの路線距離をデータにしました。
柏鉄路線データ.txt
柏鉄路線データ駅名一覧.txt

基本的に運賃は駅間の距離数で決まります。



次に、学研都市線と空港路線だけ加算運賃でしたので、この区間でのった場合、もしくは降りた場合の処理のためのデータを追加しておきます。
計算方法は初乗り運賃加算としました。
加算運賃駅の一覧。
柏線学研都市線駅名一覧.txt
柏線空港路線駅名一覧.txt

加算運賃のルール
1 学研で乗り空港線で降りる
2 学研で乗り学研で降りる
3 学研で乗り、学研と空港以外で降りる
4 学研と空港以外で乗り、学研で降りる

1→学研都市、空港両線の加算運賃を適用
2→加算運賃の適用は無し
3→学研都市線の加算運賃を適用
4→学研都市線の加算運賃のみを適用します。

でした。



これらデータを下に、下記プログラムを使いまして、
↓こちらの表を作成しました。
カンマ区切りのCSV形式ですので、コピーしてExcelで見てください。
運賃一覧表.txt
文字化けした場合はこちら↓
http://www.pixiv.net/novel/show.php?id=19533



今回は簡単なプログラムでしたがもし、駅スパートのような処理が必要になるとしたら相当大変だったと思います。
運賃が最低になるルートの検索や、指定時間から出発して最短時間で現地につけるルートの検索など。

こうなるとルート検索中に、乗り継ぎをしたとき次に乗る電車の候補を求めたり、路線が変わったときの初乗り運賃を足したりといった各種処理が必要になったり、検索過程のショートカット、組み合わせ量が膨大になるので検索先の優先順位の設定、各鉄道ごとの特例ルールへの対処など記述すべき処理がおおくなるだろうので、大変だったろうなというのが実感。
簡単な処理でよかった。





  • 運賃表作成プログラム
製作者 堀江伸一
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.IO;

namespace 架空鉄道柏鉄運賃表計算プログラム
{
   public partial class Form1 : Form
   {

       Hashtable ht = new Hashtable();
       List<decimal> lenSums = new List<decimal>();
       List<string> gakkennTosisen = new List<string>();
       List<string> kuukouSen = new List<string>(); 


       public Form1()
       {
           InitializeComponent();
       }

       private void Form1_Load(object sender, EventArgs e)
       {

       }

       private void button1_Click(object sender, EventArgs e)
       {
           
           StreamReader sr = new StreamReader(
                           "D:/色々/架空鉄道運賃表/柏鉄路線データ.txt", Encoding.GetEncoding("Shift_JIS"));
           char[] delimiterChars = {'\t'};

           while (!sr.EndOfStream)
           {
               string s=sr.ReadLine();
               string[] row = s.Split(delimiterChars);
               setStationDate(row[0], row[1], decimal.Parse(row[2])); 
           }
           sr.Close();
           sr = new StreamReader(
                          "D:/色々/架空鉄道運賃表/柏鉄路線データ駅名一覧.txt", Encoding.GetEncoding("Shift_JIS"));
           List<string> stationNames = new List<string>();
           while (!sr.EndOfStream)
           {
               stationNames.Add ( sr.ReadLine());
           }
           sr.Close();
           

           sr = new StreamReader(
              "D:/色々/架空鉄道運賃表/柏線学研都市線駅名一覧.txt", Encoding.GetEncoding("Shift_JIS"));
           while (!sr.EndOfStream)
           {
               gakkennTosisen.Add(sr.ReadLine());
           }
           sr.Close();



           sr = new StreamReader(
               "D:/色々/架空鉄道運賃表/柏線空港路線駅名一覧.txt", Encoding.GetEncoding("Shift_JIS"));
           while (!sr.EndOfStream)
           {
               kuukouSen.Add(sr.ReadLine());
           }
           sr.Close();



           int count =stationNames.Count;  
           string [,] sUntinDate=new string [count+1,count+1];
           sUntinDate[0, 0] = "";
           for (int i = 1; i < count+1; i++)
           {
               sUntinDate[0, i] = stationNames[i-1];
               sUntinDate[i, 0] = stationNames[i-1];  
           }

               for (int i = 0; i < count; i++)
               {
                   for (int j = 0; j < count; j++)
                   {
                       if (i == j)
                       {
                           sUntinDate[i + 1, j + 1] = "0";
                       }
                       else
                       {
                           search_root(stationNames[i], stationNames[j], decimal.Parse ("0.00"));
                           lenSums.Sort();
                           sUntinDate[i + 1, j + 1] = CalcUntin(stationNames[i], stationNames[j], lenSums[0]).ToString();
                           lenSums.Clear();
                       }
                       }
               }


               string outDate = "";
               StreamWriter sw = new StreamWriter("D:/色々/架空鉄道運賃表/運賃一覧表.txt", true, System.Text.Encoding.GetEncoding("shift_jis"));

               for (int i = 0; i < count+1; i++)
               {
                   for (int j = 0; j < count+1; j++)
                   {
                       outDate += sUntinDate[i, j] + ",";
                           
                   }
                   sw.WriteLine(outDate);     
                   outDate="";
               }


               sw.Close();
       }



       internal int CalcUntin(string sStartStation, string sGoalStation, decimal km)
       {
           /*
           路線
           空港線          20
           学研都市線      30

            * 
           1キロ以下の端数は切り上げ
           小人は半額(10円以下切り捨て)
           1~3	130円
           4~6	160円
           7~10	190円
           11~15	220円
           16~20	250円
           21~25	280円
           26~30	310円
           31~35	350円
           36~40	390円
           41~45	430円
           46~50	470円
           51~60	510円
           61~70	550円
           71~80	600円
           81~90	650円
           91~100	700円    
           101~110	750円
           111~120	800円

           */
           int Untin=0;
           int km2=(int)Math.Ceiling(km);
           if (km2 > 0 && km2 < 4)
           {
               Untin = 130;
           }
           if (km2 > 3 && km2 < 7)
           {
               Untin = 160;
           }
           if (km2 > 6 && km2 < 11)
           {
               Untin = 190;
           }
           if (km2 > 10 && km2 < 16)
           {
               Untin = 220;
           }


           if (km2 > 15 && km2 < 21)
           {
               Untin =250;
           }
           if (km2 > 20 && km2 < 26)
           {
               Untin = 280;
           }
           if (km2 > 25 && km2 < 31)
           {
               Untin = 310;
           }
           if (km2 > 30 && km2 < 36)
           {
               Untin = 350;
           }



           if (km2 > 35 && km2 < 41)
           {
               Untin = 390;
           }
           if (km2 > 40 && km2 < 46)
           {
               Untin = 430;
           }
           if (km2 > 45 && km2 < 51)
           {
               Untin = 470;
           }
           if (km2 > 50 && km2 < 61)
           {
               Untin = 510;
           }


           if (km2 > 60 && km2 < 71)
           {
               Untin = 550;
           }
           if (km2 > 70 && km2 < 81)
           {
               Untin = 600;
           }
           if (km2 > 80 && km2 < 91)
           {
               Untin = 650;
           }
           if (km2 > 90 && km2 < 101)
           {
               Untin = 700;
           }
           if (km2 > 100 && km2 < 111)
           {
               Untin = 750;
           }
           if (km2 > 110 && km2 < 121)
           {
               Untin = 800;
           }

           bool b1 = kuukouSen.Contains(sStartStation);
           bool b2 = kuukouSen.Contains(sGoalStation);
           bool b3 = gakkennTosisen.Contains(sStartStation);
           bool b4 = gakkennTosisen.Contains(sGoalStation);  
           


           //もし加算運賃の路線がもう一本多ければ、加算運賃の計算を2次元の組み合わせ表を用意して計算する必要があったわけです。
           //このサイズならif文で書けるので、対応表は用意しませんでした。

           /*
            * 1 学研で乗り空港線で降りる              学研都市、空港両線の加算運賃を適用
           2 学研で乗り学研で降りる                   加算運賃の適用は無し
           3 学研で乗り、学研と空港以外で降りる       学研都市線の加算運賃を適用-
           4 学研と空港以外で乗り、学研で降りる場合   学研都市線の加算運賃
            */



           //学研線で乗車した場合
           if(b3==true && b2==false && b4==false){
               //学研線から乗り、学研線と空港線意外で降りた場合
               Untin += 30;
           }
           
           if(b3==true && b2==true){
               //学研線から乗り、空港線で降りた場合
               Untin+=50; 
           }

           
           
           //空港線で乗車 
           
           if(b1==true && b2==false && b4==false){
               //空港線から乗り、学研線と空港線意外で降りた場合
               Untin += 20;
           }
           if(b1==true && b4==true){
               //空港線から乗り、学研線で降りた場合
               Untin += 50;
           }
           
           
           

           //それ以外で乗車
           if(b1==false && b3==false && b2==true){
               //学研線と空港線意外で乗り、空港線で降りた場合
               Untin += 20;
           }
           if(b1== false && b3==false && b4==true){
               //学研線と空港線以外で乗り、学研線で降りた場合。
               Untin += 30;
           }



           return Untin; 
       }



       internal void search_root(string sNowStation, string sGoalStation, decimal sumKm)
       {
           
           

           if(sNowStation==sGoalStation){
               lenSums.Add(sumKm);  
           }else{
               stationDate sd = (stationDate)ht[sNowStation];

               if (sd != null && sd.stampGoOk==true )
               {
                   sd.stampGoOk = false;
                   ht[sNowStation] = sd;
                   for (int i = 0; i < sd.nextStation.Count; i++)
                   {
                       search_root(sd.nextStation[i], sGoalStation, sumKm + sd.nextLen[i]);
                   }
                   sd.stampGoOk = true;
                   ht[sNowStation] = sd;
               }
               
           }
       }


       public void setStationDate(string sStation1, string sStation2, decimal len)
       {

           stationDate SD; 

           if (ht[sStation1] == null)
           {
               ht[sStation1] = new stationDate();
           }
           SD=(stationDate)ht[sStation1];
           SD.set_StationDate(sStation2 ,len);
           ht[sStation1] = SD;


           if (ht[sStation2] == null)
           {
               ht[sStation2] = new stationDate();
           }
           SD = (stationDate)ht[sStation2];
           SD.set_StationDate(sStation1, len);
           ht[sStation2] = SD;
       }

       private void button2_Click(object sender, EventArgs e)
       {
           MessageBox.Show(Math.Ceiling(decimal.Parse("0.0")).ToString());
           MessageBox.Show(Math.Ceiling(decimal.Parse("0.0000001")).ToString());
           MessageBox.Show(Math.Ceiling(decimal.Parse("1.0")).ToString());
           MessageBox.Show(Math.Ceiling(decimal.Parse("1.9999999")).ToString()); 
       }



   }
   public class stationDate
   {
       internal List<string> nextStation = new List<string>();
       internal List<decimal> nextLen = new List<decimal>();
       internal bool stampGoOk = true;//駅間の経路探索のとき一度通った駅か記録するため、 通ってないならtrue
       public void set_StationDate(string sNextStation, decimal dLen)
       {
           nextStation.Add(sNextStation);
           nextLen.Add(dLen);
       }
   }
}









2010/8/8