Стоимость проезда в питерском метро
Лафа с пешком до работы кончилась и я решил узнать какой всё же проездной на метро выгоднее. Естественно, лёгких путей мы не ищем и на интуицию полагаться не будем.
Итак, модель. Для простоты смоделируем “год” в 360 дней из 12 месяцев по 30 дней. Ну, для простоты. Для примерного результата этого достаточно. И заставим модель кататься по проездным этот год
- Не брались проездные на 90 дней — один из них не может быть выгоднее жетонов (1890/70 = 27 рублей за каждую поездку), другой — подорожника (1560/60 = 26 рублей за каждую поездку)
- Не брался месячный проездной на 70 поездок. Сначала я его неправильно смоделировал, а потом заломало. Про него — отдельно
Результат:
Абсцисса — число поездок в неделю, ордината — средняя стоимость одной поездки
- Не стоит париться с проездными, если вы ездите меньше раза в день. Думаю, это и так очевидно.
- Подорожник рулит необычайно
- Нет особого смысла в “толстых” проездных (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