Итак, задача

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

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

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

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

  • Целочисленное деление, или "деление с округлением вниз". У меня оно будет представлено, как обычное деление: a / b - имея в виду ⌊a / b⌋. Например, 5 / 3 = 1.
  • Взятие остатка по модулю. Обозначу традиционно деление с остатком: a % b = a - (a / b) * b. Например, 5 % 3 = 2.

Основы, или Правило со множеством исключений

Давайте попробуем найти такую закономерность, которая удовлетворила бы как можно большему количеству значений аргумента. Обычно количество дней в месяце колеблется между 30 и 31. При этом, можно заметить зависимость этого числа от четности месяца - значит, воспользуемся операцией взятия остатка по модулю 2. Кажется, это должно быть нечто, вроде:

Неплохой старт! Не обращая внимания на февраль, для которого явно придется пойти на какие-то уловки, порадуемся тому, что мы смогли подогнать функцию под первую половину года. А далее, начиная с августа, четность надо сменить на противоположную. Сделать это можно, заменив x%2 в первом варианте формулы на (x+1)%2:

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

Маска

Нам нужно, чтобы +1 в делимом "активировалось" только при достижении аргументом значений, больших 8, т.е. нам необходимо применить некоторую маску. При этом значения аргумента не могут превосходить 12. Значит, нам идеально подойдет целочисленное деление аргумента на 8:

Ровно как нам и нужно. Воспользуемся этим выводом:

Уху! Все правильно, кроме февраля. Как неожиданно.

Февраль

Во всех месяцах 30 или 31 день, в феврале же - 28 (напомню, мы не рассматриваем високосные года).

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

В самой последней версии нашей формулы февралю достались целых 30 дней. А потому нам нужно отсечь у него пару дней. Естественно, от этого пострадают и еще какие-то месяцы: или слева от февраля, или справа от него в нашем списке - однако, справа месяцев гораздо меньше, поэтому нам придется пожертвовать именно январем, затем подправив формулу и для него. Отсечь дни для первого и второго месяцев можно с помощью выражения 2%x:

Тогда наша формула принимает уже следующий вид:

Остался последний шаг - подлатать январь. Это сделать не так сложно: просто добавим 2 дня только к нему, т.е. к такому месяцу, чей номер меньше либо равен единице. Как вам идея использовать для этой цели 1/x? Проверяем:

Бинго! 12 из 12!

Заключение

Итак, мы вывели искомую формулу, вот она, записанная на языке JavaScript:

function f(x) {
	return 28 + (x + Math.floor(x/8)) % 2 + 2 % x + 2 * Math.floor(1/x);
}

Спросите меня, сколько дней в сентябре? Я скажу вам: f(9).