Vulpo One

Стоимость проезда в питерском метро

Лафа с пешком до работы кончилась и я решил узнать какой всё же проездной на метро выгоднее. Естественно, лёгких путей мы не ищем и на интуицию полагаться не будем.

Итак, модель. Для простоты смоделируем “год” в 360 дней из 12 месяцев по 30 дней. Ну, для простоты. Для примерного результата этого достаточно. И заставим модель кататься по проездным этот год

  • Не брались проездные на 90 дней — один из них не может быть выгоднее жетонов (1890/70 = 27 рублей за каждую поездку), другой — подорожника (1560/60 = 26 рублей за каждую поездку)
  • Не брался месячный проездной на 70 поездок. Сначала я его неправильно смоделировал, а потом заломало. Про него — отдельно

Результат:

image0

Абсцисса — число поездок в неделю, ордината — средняя стоимость одной поездки

  • Не стоит париться с проездными, если вы ездите меньше раза в день. Думаю, это и так очевидно.
  • Подорожник рулит необычайно
  • Нет особого смысла в “толстых” проездных (25/15, 50/30) — они незначительно выгоднее вариантов “полегче” на тот же период
  • Проездной на 20 рулит над подорожником с 9 до 14 поездок
  • Проездной на 40 рулит над подорожником с 9 до 17 поездок и безусловно рулит над проездным на 20 :)
  • Если вы ездите больше 1 и меньше 3 раз в день, скорее всего вам будет выгоднее проездной на 40 поездок
  • В остальных случаях берите подорожник. Вообще, берите подорожник, с ним не надо паритсья с проездными
  • Проездной на месяц привязан к календарному месяцу. Может он и выгоден, но мне с такой привязкой париться неудобно
  • Все эти тарифы — или от балды, или разводилово

Код модели (C++):

  1 /**
  2  * Saint Petersburg subway payments calc
  3  */
  4
  5 #include <iostream>
  6 #include <list>
  7
  8 class Calendar
  9 {
 10     int day;
 11
 12 public:
 13     Calendar(): day(0) {}
 14
 15     int getDay()
 16     {
 17         return this->day;
 18     }
 19
 20     int getMonth()
 21     {
 22         return this->day / 30;
 23     }
 24
 25     int nextDay()
 26     {
 27         this->day++;
 28     }
 29 };
 30
 31 class TicketSystem
 32 {
 33     double sum;
 34     double last_payment;
 35     int rides;
 36
 37     virtual double processPayment() = 0;
 38
 39 protected:
 40     Calendar *cal;
 41
 42 public:
 43     TicketSystem(Calendar* c): cal(c), sum(0), last_payment(0), rides(0) {}
 44
 45     double ride()
 46     {
 47         this->rides++;
 48         this->sum += this->last_payment = this->processPayment();
 49         return this->last_payment;
 50     }
 51
 52     double getLastPayment()
 53     {
 54         return this->last_payment;
 55     }
 56
 57     double getCurrentSum()
 58     {
 59         return this->sum;
 60     }
 61
 62     int getCurrentRides()
 63     {
 64         return this->rides;
 65     }
 66 };
 67
 68 class PayByToken: public TicketSystem
 69 {
 70     double token_price;
 71
 72     double processPayment()
 73     {
 74         return this->token_price;
 75     }
 76
 77 public:
 78     PayByToken(Calendar* c, double token_price = 27): token_price(token_price), TicketSystem(c) {}
 79 };
 80
 81 class PayByPodorozhnik: public TicketSystem
 82 {
 83     int current_month;
 84     int rides;
 85
 86     double processPayment()
 87     {
 88         if (this->cal->getMonth() != this->current_month)
 89         {
 90             this->current_month = this->cal->getMonth();
 91             this->rides = 0;
 92         }
 93
 94         this->rides++;
 95
 96         if      (rides > 40)
 97             return 19;
 98         else if (rides > 30)
 99             return 20.5;
100         else if (rides > 20)
101             return 22;
102         else if (rides > 10)
103             return 24.5;
104         else
105             return 26;
106     }
107
108 public:
109     PayByPodorozhnik(Calendar* c): TicketSystem(c), current_month(-1), rides(-1) {}
110 };
111
112 class PayByCard: public TicketSystem
113 {
114     double price;
115     int rides, days, days_left, rides_left, prev_day;
116
117 public:
118     PayByCard(Calendar *cal, double price, int rides, int days):
119         TicketSystem(cal), rides(rides), days(days), price(price), days_left(0), rides_left(0), prev_day(-1) {}
120
121     double processPayment()
122     {
123         if (prev_day != cal->getDay())
124         {
125             prev_day = cal->getDay();
126             days_left--;
127         }
128
129         rides_left--;
130
131         if (days_left <= 0 || rides_left <= 0)
132         {
133             rides_left = rides;
134             days_left  = days;
135             return price;
136         }
137
138         return 0;
139     }
140 };
141
142 typedef std::list<TicketSystem*> MethList;
143
144 void run_sim(double);
145
146 int main()
147 {
148     for (double rides = 1.0/7; rides < 8; rides += 1.0/7)
149     {
150         run_sim(rides);
151     }
152
153     return 0;
154 }
155
156 void run_sim(double rides_per_day)
157 {
158     double rides = 0;
159     Calendar *cal = new Calendar();
160
161     MethList list;
162
163     list.push_back(new PayByToken(cal, 27));
164     list.push_back(new PayByPodorozhnik(cal));
165     //list.push_back(new PayByCard(cal, 1890, 70, 90));
166     //list.push_back(new PayByCard(cal, 1560, 60, 90));
167     list.push_back(new PayByCard(cal, 230,  10, 7));
168     list.push_back(new PayByCard(cal, 430,  20, 15));
169     list.push_back(new PayByCard(cal, 530,  25, 15));
170     list.push_back(new PayByCard(cal, 830,  40, 30));
171     list.push_back(new PayByCard(cal, 1025, 50, 30));
172     //list.push_back(new PayByCard(cal, 1290, 70, 30)); // wrong!!!
173
174
175     while(cal->getDay() < 360)
176     {
177         if (rides > 1)
178         {
179             rides -= 1;
180             for (TicketSystem *token: list)
181             {
182                 token->ride();
183             }
184         }
185         else
186         {
187             rides += rides_per_day;
188             cal->nextDay();
189         }
190     }
191
192     std::cout << rides_per_day * 7 << "\t";
193
194     for (TicketSystem *token: list)
195     {
196         std::cout << token->getCurrentSum() / token->getCurrentRides() << "\t";
197     }
198
199     std::cout << std::endl;
200
201     for (TicketSystem *token: list)
202     {
203         delete token;
204         token = nullptr;
205     }
206
207     delete cal;
208 }

Для сборки нужен компилятор с поддержкой C++11. Актуальную версию (если замечу баги или неточности) можно найти на bitbucket.org