Повторное использование календарей — питонический путь

Вчера получил календарь на 2016 год. Мне в голову пришёл интересный вопрос: когда я смогу повторно использовать этот календарь, и для какого года можно повторно использовать какой старый календарь?

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

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

Однако есть одна особенность, которая в настоящее время не реализована в онлайн-сервисах вроде whencanireusethiscalendar.com: Вы можете частично повторно использовать календарь.

Причина, по которой два календаря не совпадают, часто — високосный день в одном из них. Однако довольно часто они совпадают либо до високосного дня, либо начиная с дня после високосного дня. Мы называем эти годы годами частичного повторного использования (A) и (B) соответственно. Как только вы рассматриваете эту опцию (т.е. вы меняете календарь где-то около високосного дня, предполагая, что у нас есть как (A), так и (B) пригодные для повторного использования календари на один год), вы сможете повторно использовать календари — особенно календари високосных лет, такие как 2016.

Основываясь на этом понимании, я написал простой python-скрипт полного перебора, использующий только основные библиотеки. Вы можете просто вызвать его из командной строки с годом календаря, который вы хотите повторно использовать:

reuse-calendars.sh
python3 reuse-calendars.py 2015

Скрипт использует подход проб и ошибок, поскольку пространство поиска довольно мало для современного компьютера:

reuse-calendars.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Вычислить список лет, когда можно повторно использовать календарь.
Этот скрипт вычисляет три типа лет повторного использования:
    - Годы полного повторного использования, где каждый день недели совпадает
    - Годы частичного повторного использования (A), где каждый день до
        високосного дня (29 фев) совпадает
    - Годы частичного повторного использования (B), где каждый день начиная с дня
        после високосного дня (29 фев) совпадает
Годы частичного повторного использования не включают годы полного повторного использования по замыслу.
"""
__copyright__ = "Copyright (c) 2015 Uli Köhler"
__license__ = "Apache License v2.0"
__version__ = "1.1"

from datetime import date
from collections import namedtuple

ReuseYears = namedtuple('ReuseYears',
                        ['year', 'fullReuseYears',
                         'partialReuseYearsA', 'partialReuseYearsB'])

def getReuseYears(year, startYear=2005, stopYear=2050):
    """
    Получить годы, когда можно повторно использовать календарь, как объект ReuseYears.
    """
    # Получить номер дня недели ISO для одной из двух опорных дат
    # Опорная дата 1 — первый день года
    # Опорная дата 2 — любая дата ПОСЛЕ високосного дня
    refdate1 = lambda y: date(y, 1, 1).isoweekday()
    refdate2 = lambda y: date(y, 5, 1).isoweekday()
    fullYears = []
    partialYearsA = []  # До високосного дня
    partialYearsB = []  # С високосного дня
    for otherYear in range(startYear, stopYear + 1):
        if otherYear == year: continue
        aMatch = refdate1(year) == refdate1(otherYear)
        bMatch = refdate2(year) == refdate2(otherYear)
        if aMatch and bMatch:
            fullYears.append(otherYear)
        elif aMatch:
            partialYearsA.append(otherYear)
        elif bMatch:
            partialYearsB.append(otherYear)
    return ReuseYears(year, fullYears, partialYearsA, partialYearsB)

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('year', type=int, help='Год для получения ')
    args = parser.parse_args()

    print(getReuseYears(args.year))

Check out similar posts by category: Algorithms