Как одной математической формулой по номеру месяца посчитать количество дней в нем?

Автор: | 01.06.2022

Недавно, после долгого времени без сна, я задумался, как можно узнать количество дней в месяце, зная их количество. На эту тему есть детские песенки, есть способ считать косточками, но меня эти способы не устраивают — я погрузился в поиски математической формулы, которую через некоторое время вывел.

Итак, задача

Формально, другими словами, нам нужно получить функцию f(x), которая давала бы следующий список значений (который, кстати, я нашел где-то в Интернете и не вывел по мнемоническим правилам):

Следует отметить, что в качестве аргумента мы получаем только номер месяца, то есть не учитываем високосные годы и f(2) = 28.

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

Чем мы будем пользоваться

Помимо сложения, вычитания и умножения я буду использовать две операции: целочисленное деление и операцию взятия остатка. Напомню, что это такое:

  • Целочисленное деление или «округление в меньшую сторону». Для меня это будет представлять собой обычное деление: а/б — то есть а/б. Например, 5/3 = 1.
  • Взяв остальное по модулю. Традиционно я буду обозначать деление с остатком: а%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).

Перевод статьи «Формула количества дней в каждом месяце»

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *