
Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.
Розповідає Куртис МакЭнрое
Нещодавно після тривалого часу, проведеного без сну, я замислився над тим, як можна упізнати кількість днів в місяці, знаючи його номер. Існують дитячі вірші на цю тему, є спосіб підрахунку по кісточках, але ці методи не влаштовують мене – я занурився в пошук математичної формули, яку і вивів через деякий час.
Отже, завдання
Формально Іншими словами, нам треба отримати функцію f (x), яка б давала наступний список значень (який я, до речі кажучи, знайшов десь в інтернеті, а не вивів за допомогою мнемонічних правил) :
Варто врахувати, що в якості аргументу ми отримуємо тільки номер місяця, тобто ми не враховуємо високосні роки, і f (2) = 28.
Якщо ви хочете упізнати отриманий мною результат, пролистните до кінця цієї сторінки. Те, що буде описано далі – це виведення шуканої формули.
Чим ми користуватимемося
Окрім складання, віднімання і множення, я скористаюся двома операціями: цілочисельним діленням і операцією узяття залишку. Нагадаю, що це таке :
- Цілочисельне ділення, чи “ділення з округленням вниз”. У мене воно буде представлено, як звичайне ділення:
a / b
– маючи на увазі⌊a / b⌋
. Наприклад,5 / 3 = 1
. - Узяття залишку по модулю. Позначу традиційно ділення із залишком:
a % b = a - (a / b) * b
. Наприклад,5 % 3 = 2
.
Вони мають однаковий пріоритет і являються левоассоциативными.
Основи, або Правило з множиною виключень
Давайте спробуємо знайти таку закономірність, яка задовольнила б як можна більшій кількості значень аргументу. Звичайна кількість днів в місяці коливається між 30 і 31. При цьому, можна помітити залежність цього числа від парності місяця – значить, скористаємося операцією узяття залишку по модулю 2. Здається, це повинно бути щось, ніби:
f₁ (x) = 30 + x%2
Непоганий старт! Не звертаючи уваги на лютий, для якого явно доведеться піти на якісь хитрощі, порадіємо тому, що ми змогли підігнати функцію під першу половину року. А далі, починаючи з серпня, парність потрібно змінити на протилежну. Зробити це можна, замінивши x%2 у першому варіанті формули на (x+1)%2:
f₂ (x) = 30 + (x + 1) % 2
Як і очікувалося, тепер перша половина року вже поза областю правильних значень, зате місяці з серпня по грудень дали те, що потрібно. Знайдемо спосіб об’єднати дві ці частини.
Маска
Нам треба, щоб +1 в ділимому ” активувалося” тільки при досягненні аргументом значень, великих 8, тобто нам необхідно застосувати деяку маску. При цьому значення аргументу не можуть перевершувати 12. Значить, нам ідеально підійде цілочисельне ділення аргументу на 8:
Рівно як нам і треба. Скористаємося цим виведенням:
f₃ (x) = 30 + (x + x / 8) % 2
Юшку! Все правильно, окрім лютого. Як несподівано.
Лютий
У усіх місяцях 30 або 31 день, в лютому ж – 28 (нагадаю, ми не розглядаємо високосні роки).
Історична довідка: у романському календарі лютий був останнім місяцем року – погодитеся, додавання одного дня у високосні роки в кінець календаря інтуїтивно зрозуміліше. Проте ми користуємося григоріанським календарем, в якому найкоротший місяць перенесений ближче до початку по волі одного з мудрих правителів.
У самій останній версії нашої формули лютому дісталися цілих 30 днів. А тому нам треба відсікти у нього пару днів. Природно, від цього постраждають і ще якісь місяці: або зліва від лютого, або праворуч від нього в нашому списку – проте, справа місяців значно менше, тому нам доведеться пожертвувати саме січнем, потім підправивши формулу і для нього. Відсікти дні для першого і другого місяців можна за допомогою вираження 2%x:
Тоді наша формула набирає вже наступного вигляду:
f₄ (x) = 28 + (x + x / 8) % 2 + 2 % x
Залишився останній крок – підлатати січень. Це зробити не так складно: просто додамо 2 дні тільки до нього, тобто до такого місяця, чий номер менше або дорівнює одиниці. Як вам ідея використати для цієї мети 1/x? Перевіряємо:
f₅ (x) = 28 + (x + x / 8) % 2 + 2 % x + 1 / x * 2
Бінго! 12 з 12!
Висновок
Отже, ми вивели шукану формулу, ось вона, записана на мові,JavaScript:
function f (x) { return 28 + (x + Math.floor (x/8)) % 2 + 2 % x + 2 * Math.floor (1/x); }
Запитайте мене, скільки днів у вересні? Я скажу вам: f (9).
Переклад статті “A Formula for the Number of Days in Each Month”
Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?