Два программиста и календарь
hard

Два программиста и календарь

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

Java — это не будущее, это уже настоящее
Изучите программирование на Java.
Начать бесплатно
Java — это не будущее, это уже настоящее

Два программиста снова спорят о работе и JavaScript:

— Я сейчас в одном проекте делаю календарь, и для него я завёл отдельный массив с днями недели. Логично же?

— Ты что, не можешь по номеру месяца посчитать количество дней в нём?

— А ты что, можешь?!

🤔 Можно ли по номеру месяца посчитать количество дней в нём, используя одну формулу?

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

Что делать с февралём

В високосный год в феврале 29 дней, а не 28, как обычно. Но так как мы не знаем, про какой год идёт речь, и февраль не влияет на количество дней в других месяцах, то для простоты будем считать, что в феврале у нас всегда 28 дней. 

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

Для начала запишем в виде таблицы наши месяцы и дни:

Задача: два программиста и календарь

Получается, что если на вход мы подадим 8 (август), то на выходе функция нам должна вернуть число 31 (количество дней в августе). Такую функцию мы и будем делать.

Магия математики: целочисленное деление и остаток

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

Целочисленное деление — это целая часть от деления одного числа на другое, в математике обозначается обратной косой чертой: \. Грубо говоря, это сколько раз одно число полностью помещается в другом. 

Например, 7 \ 2 = 3. Это значит, что если 7 разделить на 2, то получится 3,5, но так как нам нужна целая часть от деления, то остаётся только 3.

Ещё примеры:

8 \ 3 = 2

5 \ 1 = 5

12 \ 4 = 3

Остаток от деления (его ещё называют остаток по модулю) — это как раз то, что остаётся от предыдущего деления, то, что не поместилось в целую часть. Иногда обозначается символом — %.

Для примера возьмём те же самые пары чисел, что были выше: 7 % 2 = 1. Это значит, при делении 7 на 2 у нас останется в остатке единица:

7 = (2 × 3) + 1

Эта единица, которая не влезла в деление, и называется остатком от деления.

Ещё примеры с теми же числами:

8 % 3 = 2, потому что [8 = (3 × 2) + 2]

5 % 1 = 0, потому что [5 = (1 × 5) + 0]

12 % 4 = 0, потому что [12 = (4 × 3) + 0]

Используя только простую математику и эти две операции, попробуем найти нужную формулу.

Первый подход: проверяем чётные и нечётные месяцы

Попробуем сразу подобрать такое решение, которое подходило бы к большинству месяцев. Обычное количество дней — 30 или 31. Давайте проверим, вдруг дни зависят от того, чётный номер у месяца или нечётный. 

Чтобы это выяснить, используем операцию взятия остатка от деления на 2. Если месяц чётный, то число разделится нацело и в остатке будет 0. Если месяц нечётный, то в остатке будет 1. 

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

f = 30 + x % 2

Задача: два программиста и календарь

У нас совпала почти половина месяцев, неплохо для начала. Как мы видим, март, месяц номер 3, как раз имеет 31 день, а июнь, месяц номер 6, — 30 дней. 

С февралём будем разбираться потом, а сейчас нужно что-то придумать, чтобы начиная с августа наша формула работала наоборот. Для этого надо заменить в нашей формуле чётные месяцы на нечётные, чтобы при делении они давали в остатке 1, а не 0, как раньше. Для этого достаточно прибавить единицу к иксу: (x + 1) % 2

f = 30 + (x + 1) % 2

Задача: два программиста и календарь

Теперь у нас идеально вписывается вторая половина календаря, но первая предсказуемо сломалась. Надо найти способ как-то объединить эти два способа.

Второй подход: объединяем обе половины формулы

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

Август — месяц номер 8, поэтому будем проверять, дошли мы уже до восьмого месяца или нет. Мы знаем, что всего месяцев 12, и если взять целую часть от деления на 8, то до августа она будет равна нулю, а начиная с августа — единице:

1 \ 8 = 0

8 \ 8 = 1

12 \ 8 = 1

Чтобы в этом убедиться, давайте нарисуем таблицу, куда запишем все значения:

Задача: два программиста и календарь

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

Когда мы использовали в самом начале в формуле выражение х % 2 — работала первая половина месяцев, а когда добавили единицу — (х + 1) % 2, — то работала вторая половина. Нам нужно, чтобы единица в этой формуле появлялась начиная с августа, а для этого как раз мы использовали целочисленное деление на 8. Поэтому если мы икс целочисленно разделим на 8, то начиная с августа он будет давать единицу, а до этого — ноль. 

Целая часть от деления на 8 выглядит так: х \ 8. Используем это в нашей формуле:

f =  30 + (x + x \ 8) % 2

Задача: два программиста и календарь

Всё работает, кроме февраля. Давайте это исправим.

Добавляем февраль в формулу

В последней формуле у февраля получилось 30 дней, а надо 28. Значит, нужно убрать из него два дня. Так как формула у нас общая для всех, получается, что дни уберутся для всех месяцев либо до, либо после февраля. После февраля месяцев много, а до него — только январь. Поэтому уберём два дня у первых двух месяцев, а январь подправим потом.

Чтобы убрать 2 дня, сделаем в формуле не 30 дней, а 28:

f = 28 + …

Но тогда это повлияет на все месяцы! Добавим по 2 дня к каждому месяцу начиная с марта. Для этого используем выражение 2 % х, оно берёт остаток от деления на икс. 

Для января и февраля этот остаток будет равен нулю:

2 % 1 = 0 [2 = (1 × 2) + 0]

2 % 2 = 0 [2 = (2 × 1) + 0]

А для всех остальных остаток будет равен двум, например, для июля:

2 % 7 = 2 [2 = (7 × 0) + 2]

Запишем все значения 2 % х в таблицу:

Задача: два программиста и календарь

Да, действительно, к первым двум месяцам ничего не прибавляется, а остальные получают нужные два дня. Запишем это в формулу и проверим:

f = 28 + (x + x / 8) % 2 + (2 % x)

Задача: два программиста и календарь

Финал: добавляем январь

Осталось решить последнюю проблему: прибавить два дня только к январю. Скажем это на языке формул: нам нужно прибавить два дня к месяцу, номер которого равен единице. Если использовать то, что мы уже знаем, то единицу получить очень просто: 1 \ х.

Если икс равен единице, то наша целая часть от деления тоже равна единице:

1 \ 1 = 1 

А для всех остальных иксов целая часть будет равна нулю:

1 \ 2 = 0

1 \ 9 = 0

Чтобы прибавить не один, а два месяца, умножим это на 2: (1 \ х) × 2. Теперь подставим в формулу:

f = 28 + (x + x \ 8) % 2 + 2 % x + (1 \ x) × 2

Задача: два программиста и календарь

Сработало!

Чтобы перевести эту формулу на язык JavaScript, нужно знать следующее:

  • целочисленного деления в JS нет, поэтому вместо него используют простое деление и отбрасывание дробной части. Поэтому 7 \ 2 в JS превращается в Math.floor(7/2);
  • остаток от деления в JavaScript есть, и он обозначается теми же процентами: 7 % 2.

Зная это, легко написать функцию на JavaScript, которая по номеру месяца выдаст количество дней в нём. Скопируйте этот код и запустите его в консоли браузера:

function f(x) { 
    return 28 + (x + Math.floor(x/8)) % 2 + 2 % x + Math.floor(1/x) * 2; 
    }
var x = Number(prompt('Введите номер месяца'));
alert('Дней в этом месяце: ' + f(x) );

Почему лучше составить справочник с числом дней?

С точки зрения быстродействий программы гораздо разумнее составить справочник числа дней, например так:

function getDaysInMonth(monthNumber){
      var DaysInMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
      return DaysInMonth[monthNumber];
}

Ноль в начале — потому что нулевого месяца в году не бывает и их принято считать всё-таки с единицы :-)

Справочник в нашем случае — намного более элегантное и экономное решение, потому что поиск числа дней по номеру месяца в таком справочнике требует очень небольшого числа действий. А наша замороченная формула требует безумного числа вычислений ради какой-то очень простой задачи. Оно того не стоит.

Зато мы смогли.

Редактура:

Максим Ильяхов

Художник:

Даня Берковский

Корректор:

Ирина Михеева

Вёрстка:

Мария Дронова

Соцсети:

Олег Вешкурцев

Java — это не будущее, это уже настоящее
Изучите программирование на Java и станьте тем, кто поддерживает современную ИТ-инфраструктуру во всём мире
Начать бесплатно
Java — это не будущее, это уже настоящее Java — это не будущее, это уже настоящее Java — это не будущее, это уже настоящее Java — это не будущее, это уже настоящее
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
Инженерная задачка для программистов

Как запрограммировать датчик для вращающегося диска?

medium
Задача на логику
Задача про сторожа и фонарик

Ваша любимая задача на перебор и логику.

easy
Задача про бейсбольную биту

Эта задача решается не так просто, как кажется.

easy
Два программиста и календарь
Два программиста и календарь

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

hard
Космическая задача из NASA
Космическая задача из NASA

На размышление даётся десять… девять… восемь…

easy
Блогеры и колёса

Эту задачу могут решить только люди с абстрактным мышлением

easy
Простая задача про круги, которая выглядит сложной
Простая задача про круги, которая выглядит сложной

Но на деле она точно простая.

easy
Задача про пончики, аналитиков и маркетологов
Задача про пончики, аналитиков и маркетологов

Её можно решить несколькими способами

easy
Задача про треугольник и неполные размеры
Задача про треугольник и неполные размеры

Как найти площадь треугольника, если нет точных данных?

easy
hard
[anycomment]
Exit mobile version