Как одной математической формулой по номеру месяца посчитать количество дней в нем?
44К открытий45К показов
Рассказывает Куртис МакЭнроэ
Недавно после длительного времени, проведенного без сна, я задумался над тем, как можно узнать количество дней в месяце, зная его номер. Существуют детские стишки на эту тему, есть способ подсчета по костяшкам, но эти методы не устраивают меня — я погрузился в поиск математической формулы, которую и вывел спустя некоторое время.
Итак, задача
Формально Другими словами, нам нужно получить функцию 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
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
Заключение
Итак, мы вывели искомую формулу, вот она, записанная на языке JavaScript:
Спросите меня, сколько дней в сентябре? Я скажу вам: f(9).
44К открытий45К показов