Вычисление дня недели в уме

  • Tutorial
imageСуществует множество способов прокачать мозг. Задачи «n-back» или мобильные приложения для тренировки навыка быстрого счета в уме. Но эти задачи оторваны от текущей реальности, а хотелось бы прокачать мозг практичным навыком.

Зачем? Ведь можно быстро посчитать на гаджете. Увы, совсем не быстро, т.к. потребуется время на поиски и активацию гаджета, поиск приложения, ввод даты, осознание полученного результата. А еще можно друзей/подруг порадовать своими внезапно появившимися экстраординарными способностями. Кстати, друзья быстро осознают удобство использования вечного календаря с голосовым интерфейсом.

Разве это возможно? Как-то раньше обходились без компьютеров. В одной из тв-передач «ищем таланты» показывали натренированного трехлетнего ребенка, который может вычислять произведение трехзначных чисел (пощадите своих детей). Впрочем, взрослые уже не дети и их мозг частично кристаллизован, в смысле слабо обучаем. Значит нужно запоминать как можно меньше и максимально задействовать имеющиеся навыки.

В алгоритмике часто объемы вычислений могут быть скомпенсированы объемами памяти. Т.е. чем больше оперативки доступно, тем меньше потребуется вычислений. Аналогично работает мозг – чем больше мы запомнили, тем быстрее ищем решение. Запомнили несколько формул для сборки кубика Рубика – соберете за пару минут (после длительной тренировки). Запомнили полторы сотни формул – соберете за пару десятков секунд. Мировой рекорд 2013 года – 8.18 сек. Еще раз: чем больше помним – тем быстрее решение.

Алгоритм

Нужно взять смещение (день недели) первого дня года (y) и смещение месяца (m). Затем вычислить сумму y+m+d, где d – день месяца, и найти остаток от деления на 7. Получим номер дня недели.

Что нужно запомнить

Размышления
В целом, достаточно запомнить все дни недели всех 28 лет (периодичность пропорциональна произведению периодов високосных лет и дней недели). Последовательность в 10k. Это довольно много.

Если добавить одну операцию сложения, то будет достаточно запомнить лишь пару рядов чисел:

m(month) = { 6 2 2 5 0 3 5 1 4 6 2 4 }, с января по декабрь

y(year) = { 6 0 1 2 4 5 6 0 2 3 4 5 0 1 2 3 5 6 0 1 3 4 5 6 1 2 3 4 }, с 1988 по 2015

Например: 13 сентября 2013 = (13 + 4 + 2) % 7 = 5 (пятница)

Смещения для месяца берутся из календаря некоторого года. Смещение месяца равно количеству серых квадратов в начале месяца. Например, не високосный 2006 год. Смещение для этого года будет 0.

image

Все же запомнить смещения для всех лет и затем выполнять быстрый поиск по индексу довольно сложная когнитивная задача. Есть альтернативный путь – вычислить. Нужно взять две последние цифры года (+100 для XXI века) — Y. Далее найти ближайший прошлый високосный Yв. Взять dY = Y – Yв. Тогда смещение года можно вычислить

y(Y) = (50 – Yв/2 + dY)

Недостаток формулы в том, что для 2004 и далее смещение будет отрицательным, а для начала и середины XX века двузначными, что слегка затрудняет вычисления в уме. Можно использовать разные формулы для каждого века, в которых учитываются только две младшие цифры года. Например, 12 для 2012г и 1912г.

XX: (50 – Yв/2 + dY) % 7 или (8 – Yв/2 % 7 + dY)
XXI: (7 – Yв/2 % 7 + dY)

В итоге может оказаться проще запомнить таблицу смещений в таком виде:

image

Смещение для года можно вычислить через сумму смещения ближайшего меньшего високосного года и его разницы с искомым годом. Семь цифр запомнить проще чем 28. К тому же, цифры расположены в убывающем порядке с шагом 2. (Да, да, (0 – 2) будет 5, помним про остаток от деления на 7). Можно запомнить цифры (6, 4, 2, 0, -2, -4, -6), что при вычислениях даст аналогичный результат. Года кратные 20 располагаются в косом квадрате 3х3 по схеме «ход конем» c 2000 годом в центре. Значения смещений месяцев и лет согласованы так, чтобы на 2000 год приходилось смещение 0. А шаг между соседними рядами 28 лет.

Например, для 2014 смещение будет y(2014) = y(2012) + 2 = 1 + 2 = 3. А день программиста 13 сентября 2014 года будет (y(2014) + m(сен) + 13) = (3 + 4 + 13) = 20 => 20 % 7 = 6, т.е. суббота.

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

image

Обратите внимание, что вдруг (?), в порядке сверху вниз и слева направо, цифры выстроились в возрастающий ряд (первая цветная таблица). Можно запоминать только остатки от деления на 7 (вторая цветная таблица) или для восстановления всей таблицы запомнить только разности (последняя таблица). Прибавляя 1 к 1, получим для марта 2, для июня 2+1=3, для сентября 3+1=4 и т.д. Одинаковые значения раскрашены в одинаковые цвета. Для быстрого поиска нам поможет вторая цветная таблица. Помним, что строки — это сезоны, начиная с весны. Это крайне непривычно. Но в древнем Риме год начинался именно с марта. Это отражено в названиях месяцев латинскими цифрами: September/October/November/December – 7/8/9/10, т.е. февраль был последним 12м месяцем года, к которому добавляли високосный день.

image

12 апреля 1961 года: (6 + 1 + 5 + 12) = (0 + 5 + 12) => 17 % 7 = 3 – среда.

Важно!!! У программистов вечная проблема с потерянной единицей. В нашей задачке без этого не обошлось. Для января и февраля високосного года нужно вычитать единицу.

14 февраля 2012 = (y(2012)+m(фев)+14) — 1 = (1 + 2 +14) — 1 => 16 % 7 = 2, т.е. вторник.

Еще нужно помнить, что не все года что делятся на 4 будут високосными (исключения — 2100, 1900, 1800, ….). Соответственно, необходимо учесть смещение для века. Впрочем, даже если не учитывать последнее исключение можно безошибочно оперировать днями недели за XX и XXI века, что достаточно для большинства житейских случаев.

Немного оптимизации.

Вычисления можно производить в потоковом режиме. Обычно дату рождения (или любую другую дату) сообщают начиная с дня месяца, например, 23 декабря 1913 года. Т.е. в процессе сообщения даты можно частично вычислить искомую сумму 23 + m(дек) = 27 или даже 23 % 7 + m(дек) = 2 + 4 = 6 и затем уже задуматься y(1913) = y(1912) + 1 = 3. В итоге сообщить 30 % 7 = (6 + 3) % 7 = 2, вторник.

Часто приходится оперировать датами текущего года. Т.е. смещение года вы всегда будете помнить, т.к. от частого использования значение «закэшируется». Например, для 2014 смещение равно 3.

Что мы получили. Правила заполнения таблиц простые и вы скорее всего их запомнили и сможете воспроизвести себе шпаргалку в любом месте в любое время. Но для быстрого счета таблицы проще заучить целиком. Ведь мы не восстанавливаем таблицы сложения и умножения для расчета сдачи перед кассой. Эти таблицы «прошиты» еще в начальной школе. Для запоминания таблиц легче всего воспользоваться тренажером Week Brain Calc (Windows Phone).

После непродолжительной тренировки можно порадовать любимых своими уникальными способностями.
Share post

Comments 20

    0
    >но эти задачи оторваны от текущей реальности, а хотелось бы прокачать мозг практичным навыком

    Не могли бы вы пояснить? То есть, этими приложениями не получится натренировать навык быстрого счета в уме?
      +1
      Натренировать можно. Но востребован ли навык? Например, известная всем хаброюзерам «В уме» формирует навыки довольно сложных расчетов, которые в повседневной реальности редко используются.
        0
        Я просто не пользовался таким софтом, но периодически подумываю, т.к. хотелось бы быстрее выполнять арифметические действия над целыми и дробными числами.
        А какие именно навыки там формируются? Умножение и деление трехзначных чисел, быстрое возведение в квадрат — это, допустим, весьма полезные навыки. А также операции с числами с плавающей запятой.
          0
          Думаю Вам стоит попробовать этот тренажер. Достаточно пары часов на знакомство. Подробный анализ задач не делал, но многие задачи мне показались через чур частными. В арифметике полно частных случаев и, как отмечалось выше, чем больше эвристик помнишь, тем быстрее вычисляешь. Например, умножение 11*52 = 5 * 100 + (5+2)*10 + 2 = 572. И таких правил под сотню. Многие из них связаны с теорией чисел.
            0
            11*52 пишем: 5.toString() + (5+2).toString() + 2.toString() = 572 :)
              0
              ""+5+(5+2)+2, — это должно работать в C# & Java
                0
                Я чтоб донести суть метода ) Достаточно раздвинуть и сложить )
      +3
      О, это очень любопытная тема на самом деле!
      Я даже собирался сам написать статью, но, к сожалению, руки так и не дошли.

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

      Возьмем, например, 1988г. (первый в ряду).

      Разобьем его на 2 части: 1900 и номер года — 88.
      Для 19хх всегда берем +1.
      А из 88 можно отбросить числа кратные 28, ближайшее — 84, тогда останется 4.

      Далее из того что осталось отбрасываем числа кратные 7 (просто для удобства) 4%7 = 4.
      И еще шаг: опять же, то что осталось, делим на 4 и округляем в меньшую сторону, 4/4=1.

      т.е. если собрать все в кучу. пусть y — номер года, 88 в примере

      n = ((y%28)%7) + int((y%28)/4)

      в итоге получаем: 1 (за счет 1900) + 4 + 1 = 6.

      еще пример: крайнюю циферку — 2015г.
      для 20хх — вместо +1 будет +0, т.е. просто отбрасываем

      остается:
      15%7 = 1
      int(15/4) = 3

      0 + 1 + 3 = 4.

      В общем, если есть интерес к этой теме я могу написать свой вариант расчета.
        0
        Рассматривал похожий вариант. Не понравилась операция %28. Существенно ограничивает кол-во потенциальных пользователей метода. Поэтому в моей реализации используется сложение с одноразрядными числами, операции %4 и %7 и дополнение до 7(или 8). Для XX века еще удобно пользоваться дополнением до 50. Но после небольшой тренировки значения y() запоминаются и дополнением уже можно не пользоваться.
          0
          операция %28 же в целом не обязательна.
          запомнить достаточно просто: 28 — 56 — 84. если год близок к одному из чисел можно упростить расчет, отбросив лишнее.
            0
            Согласен, но для 21 века придется еще несколько чисел запомнить. Проблема в том, что в уме найти разность двузначных чисел с получением двузначного результата ментально сложнее, чем получить однозначное число. А это усложняет метод.
        +1
        Таки проще в уме посчитать по формуле Зеллера.

        А ещё лучше, вот так:

        month, day, year — номер месяца, день, год

        a = (14 – month) / 12
        y = year – a
        m = month + 12 * a – 2
        day_of_week = (7000 + (day + y + y / 4 – y / 100 + y / 400 + (31 * m) / 12)) MOD 7

        (/ — целочисленное деление, MOD — нахождение остатка)

        В итоге получится число от 0 до 6.
        0 — воскресенье, 1 — понедельник, ..., 6 — суббота

          +1
          Формула Зеллера скорее для компьютера, а не для вычисления в уме. Для вычисления дня недели в 2012/15гг мне сейчас требуется около 3 секунд. Методом Зеллера понадобится десятка два секунд.
            0
            Да, насчёт времени, Вы правы. Мне просто больше по душе формулу запомнить, чем набор чисел.
          +2
          напомнило:

          Была у Александра Ивановича удивительная особенность. Он мгновенно умножал и делил в уме большие трехзначные и четырехзначные числа. Но это не освободило Александра Ивановича от репутации туповатого парня.

          — Слушай, Александр Иванович, — спрашивал сосед, — сколько будет 836 на 423?

          — 353628, — отвечал Корейко, помедлив самую малость. И сосед не проверял результата умножения, ибо знал, что туповатый Корейко никогда не ошибается

          (С) Ильф и Петров. Золотой телёнок.
            0
            Увы, не все так просто. Харизма у тупого может зашкаливать. А многие гении по житейски психи. Например, Джон Форбс Нэш-младший.
            0
              0
              Примерно так и считал с детства, только вместо «судного дня» запоминалось преимущественно 1 января в разные годы периода 1980-2009, а также даты других запомнившихся событий, типа 11.09.2001.
              0
              Нужно разобраться с самой причиной проблемы :)
              Проекты стабильного календаря
                0
                А проблема ли это?

                Вот когда у одного человека ДР будет постоянно приходиться на понедельник, а у другого на пятницу, вот это будет проблема.

              Only users with full accounts can post comments. Log in, please.