Всем привет! Меня зовут Аргентум, или же Бронислав. На момент написания этой статьи мне 15 лет. Недавно я победил в школьном туре олимпиады по информатике, а скоро иду на муниципальный этап.

По нынешней системе всероссийских олимпиад, программирование на олимпиаде по информатике начинается с 8 класса. У нас в школе олимпиада проводилась в формате онлайн, на платформе "Сириус". Для решения задач по программированию можно было использовать Python, C++, C#, Java, Pascal и некоторые другие. Интересно, что я потратил ~20 минут олимпиадного времени на подключение стабильного интернета.

Я бы не сказал, что я гений программирования, второй Торвальдс, Страуструп, Столлман, Гвидо Ван Россум, Билл Гейтс, Цукерберг вместе взятые. Но я старался решать. За это время, пока велись расчеты победителя школьного тура, я решил изучить олимпиадное программирование и решение задач по программированию. И эти задачи могут касаться также других предметов - химии, физики, математики и так далее, т.к. я решил, что решая такие задачи, я также закрепляю пройденный материал других.

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

Текста и кода будет много, постарался сделать как можно качественнее. Весь код и примеры я буду показывать преимущественно на Python.

Перечень моих репозиториев исходного кода

  1. Наука и инжинерные вычисления

    Это целая библиотека, которую я начал разрабатывать в первой моей статье на хабре

  2. Общие задачи (в разработке, пока пустой)

    В репозитории есть директория tasks, в ней несколько директорий - base, medium, pro и модуль тестирования задач. В них я складываю задачи по сложности - легкие в base, средние в medium, сложные в pro.

    Также в репозитории есть файл olimp.py, через которого вы можете запустить решение той или иной задачи.

Предметные задачи (Химия) - относительная молекулярная масса

Начнем с простого и легкого - определение относительной молекулярной массы вещества. Эту тему обычно изучают первой в курсе по химии в школе.

Ниже я привел формулу для вычисления относительной молекулярной массы на примере NaCl (хлорид натрия, или же поваренная соль).

Mr(NaCl) = Ar(Na) + Ar(Cl) = 22,9898 + 35,453 = 58,4428

Для того, чтобы реализовать такой калькулятор, нам потребуется парсер химических функций и сам класс элемента. Герои не ищут простых путей, мы создадим все с нуля

import re                              # Импорт библиотеки для регулярных выражений
from collections import Counter        # Импорт Counter из коллекций

Сверху мы импортировали нужные нам модули. Они нужны для парсинга формулы.

class Element:
    """Класс химического элемента"""
    def __init__(self, short_name: str, electronic_conf_of_outer_layer: str,
                name: str, atomic_number: int, relative_atomic_mass: float,
                group: str, period: int, row: int, group_num: int,
                side_group: bool, is_metal: bool):
        self.short_name = short_name    # химический символ элемента
        self.electronic_conf_of_outer_layer = electronic_conf_of_outer_layer    # внешний электронный слой
        self.name = name    # название химического элемента
        self.atomic_number = atomic_number    # атомное число, заряд ядра
        self.relative_atomic_mass = relative_atomic_mass    # относитльная атомная масса
        self.group = group    # группа (A или B)
        self.side_group = side_group    # побочная ли группа (True, False), зависит от группы
        self.is_metal = is_metal    # металл ли элемент
        self.period = period    # период
        self.row = row     # ряд
        self.group_num = group_num    # номер группы (от 1 до 8)


# Словарь элементов с ключом в виде химического символа элемента
ELEMENTS = {
    # Символ ЭлКонф Название АтомноеЧисло ОтносАтомМасса группа период ряд
    # номерГруппы ЭтопобочнаяГруппа ЭтоМетал
    'H': Element('H', '1s^1', 'Водород', 1, 1.00794, 'A', 1, 1, 1, False, False),
    'He': Element('He', '1s^2', 'Гелий', 2, 4.002602, 'A', 8, 1, 1, False, False),
    'Li': Element('Li', '2s^1', 'Литий', 3, 6.941, 'A', 1, 2, 2, False, True),
    'Be': Element('Be', '2s^2', 'Бериллий', 4, 9.01218, 'A', 2, 2, 2, False, True),
    'B': Element('B', '2s^2 2p^1', 'Бор', 5, 10.811, 'A', 3, 2, 2, False, False),
    'C': Element('C', '2s^2 2p^2', 'Углерод', 6, 12.011, 'A', 4, 2, 2, False, False),
    'N': Element('N', '2s^2 2p^3', 'Азот', 7, 14.0067, 'A', 5, 2, 2, False, False),
    'O': Element('O', '2s^2 2p^4', 'Кислород', 8, 15.9994, 'A', 6, 2, 2, False, False),
    'F': Element('F', '2s^2 2p^5', 'Фтор', 9, 18.998403, 'A', 7, 2, 2, False, False),
    'Ne': Element('Ne', '2s^2 2p^6', 'Неон', 10, 20.179, 'A', 8, 2, 2, False, False),
    'Na': Element('Na', '3s^1', 'Натрий', 11, 22.98977, 'A', 1, 3, 3, False, True),
    # добавляйте другие элементы по надобности
}

Сверху мы создали класс элемента и словарь элементов (я добавил не все элементы, а только с водорода до натрия включительно). Если вам нужно больше, создавайте по шаблону

def repl(m):
    return m[1] * int(m[2] if m[2] else 1)


def parse_molecule(formula: str) -> dict:
    while '(' in formula:
        formula = re.sub(r'\((\w*)\)(\d*)', repl, formula)
    while '[' in formula:
        formula = re.sub(r'\[(\w*)\](\d*)', repl, formula)
    formula = re.sub(r'([A-Z][a-z]?)(\d*)', repl, formula)
    formula_dict = Counter(re.findall('[A-Z][a-z]*', formula))

    return formula_dict


def get_element_mass(element):
    return ELEMENTS[element].relative_atomic_mass


def calculate_relative_molecular_mass(formula):
    result = parse_molecule(formula)
    mass = 0

    for i in result.items():
        print(f'{i[1]} {ELEMENTS[i[0]].name} = {ELEMENTS[i[0]].relative_atomic_mass * i[1]}')
        mass += get_element_mass(i[0]) * i[1]

    return mass


def main():
    formula = input('Введите формулу: ')
    mass = calculate_relative_molecular_mass(formula)
    if mass is not None:
        print(f'Относительная молекулярная масса формулы {formula} = ~{mass}')
    else:
        print(f'Ошибка парсинга формулы {formula}')


if __name__ == "__main__":
    main()

Сверху конец кода, там мы вычисляем молекулярную массу формулы.

При запуске мы вводим свою формулу... И вуаля, все работает как часы. Весь код вы можете просмотреть в моем репозитории.

Предметные задачи (Химия) - массовая доля элемента в веществе

Пошли серьезные расчеты. Ниже вы видите, как рассчитать массовую долю водорода в формуле H2O (вода).

\frac{Ar(H) * 2}{Mr(H2O)} * 100\% = \frac{2}{18} * 100\% = 11.1\%

Мы делим атомную массу элемента на молекулярную массу формулы и умножаем на 100%, и получаем проценты. В итоге - массовая доля водорода равна 11.1%

Сама формула проста - делим атомную массу элемента и умножаем ее на количество элементов на относительную молекулярную массу всей формулы, после умножаем на 100% и получаем сколько процентов занимает элемент в формуле.

Реализуем это на Python, в добавок к предыдущему коду

def calculate_mass_fraction_of_element(formula, element):
    formula = parse_molecule(formula) # парсим формулу
    mass_fraction = 0 # массовая доля
    mass = 0 # молекулярная масса

    for i in formula.items(): # высчитываем молекулярную массу
        mass += get_element_mass(i[0]) * i[1]

    for i in formula.items(): # высчитываем массовую долю элемента
        if ELEMENTS[i[0]].short_name == element:
            mass_fraction = (get_element_mass(i[0]) * i[1] / mass) * 100
            break

    return mass_fraction

Вот и все. При запуске этой функции мы получим массовую долю в процентах

Решение простых задач

  1. Из массива [2, 4, 19, 20, 40, 23, 45, 1000, 34, 23, 1, -3] найдите те, которые делятся на 2 без остатка и больше 5.

    На выходе должно получиться [20, 40, 1000, 34]

Решение

Существует много разных решений, но наиболее рациональным будет следующее:

print([i for i in [2, 4, 19, 20, 40, 23, 45, 1000, 34, 23, 1, -3] if i % 2 == 0 and i > 5])

  1. Найдите три ключа с самыми высокими значениями в словаре my_dict = {'a':500, 'b':5874, 'c': 560,'d':400, 'e':5874, 'f': 20}.

    На выходе должно получиться ['b', 'e', 'c']

Решениие
mydict = {'a':500, 'b':5874, 'c': 560,'d':400, 'e':5874, 'f': 20}
print(sorted(mydict, key=mydict.get, reverse=True)[:3])

  1. Постройте числовой треугольник из 12 рядов, по типу:


1
22
333
4444
55555
Решение
rows = 12
for i in range(rows):
    for i in range(i):
        print(i, end="")
    print("")

Выше представлено решение

Олимпиадное программирование

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

Любой спортивный программист выбрал бы нечитабельное решение за 1 строчку кода, чем такое же, но за две.

Принцип олимпиадного программирования - это участие в соревнованиях по решению нетривиальных алгоритмических задач. Программисту важно быстро решить задачу, не сделав ошибок (но их нет только в том коде, которого не существует). Жюри готовит описание задачи, формат входных и выходных данных, сколько максимум ресурсов может использовать программа.

Для того чтобы стать профессионалом в олимпиадном программировании, вам надо решать не только задачи на программирование, но и простые, логические задачи.

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

Заключение

Олимпиадное программирование, решение задач - штука сложная, но полезная. Они заставляют думать и отметать неэффективные решения.

Можно начинать с Python, но постепенно переходить на C++, если хотите участвовать в более сложных олимпиадах.

Минимум для изучения в программировании - переменные, функции, операторы и логика, массивы, циклы, процедуры, классы, стандартные библиотеки.

Сайты для решения задач по программированию:

  • CodeWars (решение задач)

  • Kaggle (исследовательские задачи, связанные с Data Science и МО)

  • LeetCode (решение задач)

  • Codeforces (решение задач)

  • Exercism (решение задач)

  • All Cups (решение задач)


Задавайте вопросы и комментируйте, я рад услышать любое мнение. И конечно, ставьте плюсы!

Комментарии (23)


  1. AndreyDmitriev
    21.11.2023 17:45
    +2

    Ещё до кучи можно Euler Project добавить. Я когда-то очень давно на нём LabVIEW скиллы прокачивал, когда к сертификационному экзамену готовился, типа


  1. lair
    21.11.2023 17:45
    +4

    Но почему я решил написать статью на тему олимпиадного программирования и решения проблем? Как по мне, любому человеку важно постоянно тренировать свои навыки, будь то спортсмен или программист.

    Я надеюсь, что вы отдаете себе отчет, что олимпиадное программирование тренирует навык олимпиадного программирования, а совсем не того, что нужно людям в работе.


    1. GospodinKolhoznik
      21.11.2023 17:45
      +6

      В какой работе? Парню 15 лет, успеет ещё наработаться.

      А вообще я сейчас подумал, в любой работе во все времена самый главный навык это выстраивать связи. Почему не проводятся олимпиады и хакатоны по налаживанию связей? Формат такой: за короткое время надо успеть установить контакт и втереться в доверие к определенному списку лиц. Кто к большему числу втёрся в доверие, тот и победил. Тем более, что теоретический материал по налаживанию связей есть и его в школе давно уже проходят. Методичка называется "Мертвые Души".


      1. lair
        21.11.2023 17:45
        +1

        В какой работе?

        В будущей. Если человек не планирует работать в индустрии разработки ПО - то и прекрасно. Я не против тренировок, я просто предпочитаю, когда люди понимаю, какой мускул они тренируют.


        1. DrArgentum Автор
          21.11.2023 17:45
          +1

          Да, я понимаю что вариант реализации той же сортировки пузырьком для олимпиады и для продакшена - разные.

          В олимпиадах ты обычно пишешь, код с нуля, не соблюдая принципы (тот же DRY). И читать код, скорее всего, будешь только ты. А на работе, как я знаю, с твоим кодом придется работать разным людям, которые могут не все понять.

          Спасибо за ваше мнение, всегда рад стараться :)


          1. Vitimbo
            21.11.2023 17:45

            Сортировка пузырьком в проде обычно вообще не существует :)

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


            1. DrArgentum Автор
              21.11.2023 17:45

              Привел для примера)

              Полностью с вами согласен


          1. domix32
            21.11.2023 17:45
            +1

            Тогда не очень понятно, зачем собственно смотреть на тот код в репе. Там уже есть кучка сомнительных вещей, типа кривого, но рабочего, применения того же argparse или та же копипаста с элементами, вместо массивов или вообще какой-нибудь csv.


            1. DrArgentum Автор
              21.11.2023 17:45

              Спасибо! Я совершенно забыл про CSV. Вы - официально герой. Насчет argparse, пожалуйста, расскажите, в чем его кривота?


              1. domix32
                21.11.2023 17:45
                +2

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

                tar -x -z -f ==> tar -xzf

                длинные соотвественно пишутся из многабукв через два тире. Сначала думал, что явно расставлять дефисы тоже не надо, но дока разубедила. Но в среднем рекомендую почитать варианты приготовления.

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

                'Au': Element('He', "2s^2p2^3d0", blabla...)
                  ^____________^________

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


        1. Didntread
          21.11.2023 17:45

          Человек, умеющий решать олимпиадные задачи, легко разберется и решит любые не олимпиадные задачи, каких, допустим, 99% беклога. А вот человек, не умеющий решать олимпиадные задачи, 1% олимпиадных в беклоге может и ниасилить.


          1. sovaz1997
            21.11.2023 17:45

            Решит-то решит, вопрос в том, насколько это решение будет жизнеспособно от синтетического программиста, который 100% времени посвящал олимпиадным задачам


          1. ViacheslavNk
            21.11.2023 17:45

            Я хоть и сам олимпиадник, но в целом не согласен, по опыту самые большие проблемы возникают не на каких сложных алгоритмических задачах (ну скажем из тех что встречались на практике сжатие данных на основе DAG, реализация succinct структур, оптимизация крипто алгоритмов), а из-за архитектуры, нарушение принципов solid (в частности single responsibility), просто организации большой кодовой базы, кривой обработки ошибок и пр, что потом порождает кучу саппортных ишуев и сложности в “раскуривании” и пр. Ну и как я заметил по опыту, сложную алгоритмическую задачу в принципе может решить любой программист, почитает нужную литературу, статьи, посмотрит реализации и в целом решит ну хороший олимпиадник может сделает это быстрее, но в целом для релизного цикла это не принципиально. А вот умение программировать что бы код не “умирал” от легаси и что бы с ним могли работать на протяжении 5-10 и более лет, совсем другой скил.


          1. lair
            21.11.2023 17:45
            +1

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

            Может быть, что-то сейчас поменялось, но когда я в эту сторону смотрел, в олимпиадных задачах не смотрели на читаемость и поддерживаемость кода; напротив, в силу ограничения по времени ими жертвовали. А еще мне казалось, что в олимпиадах не дают задач на разработку распределенных систем, проектирование АПИ и поддержку легаси с минимальными побочными эффектами. Повторюсь, могу ошибаться. может все и поменялось.

            А вот человек, не умеющий решать олимпиадные задачи, 1% олимпиадных в беклоге может и ниасилить.

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


      1. DrArgentum Автор
        21.11.2023 17:45

        Да, как я слышал, сейчас важны soft skills чуть больше, чем hard skills. Надо работать с людьми


  1. myswordishatred
    21.11.2023 17:45

    Так а это, как задачи-то решать?

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

    Опять всё самому делать.


    1. DrArgentum Автор
      21.11.2023 17:45

      Не опять, сил не хватило на одну большую статью, решил разбить ее на две части


  1. fixthgame
    21.11.2023 17:45
    +1

    Не совсем понимаю зачем нужна данная статья.

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

    Во-вторых про само спортивное написано мало (как будто скопировали с ChatGPT), и я не вижу смысла, чтобы человек, который почти никак не плавает в этой теме писал бы статью про спорт прогу.

    Особенно порадовало то, что вы только одну тему упомянули связанную с задачами (дп), хотя их раз в 30 больше.

    "Любой спортивный программист выбрал бы нечитабельное решение за 1 строчку кода, чем такое же, но за две." это неправда, скорее просто стереотип. Он будет писать то, что удобнее. Иногда написаьь 10 строк значительно легче, чем 5.


  1. cdscds
    21.11.2023 17:45
    +1

    "Постройте числовой треугольник из 12 рядов, по типу:"
    Не уверен что простое задание Вы решили верно.


  1. MegaMANGO
    21.11.2023 17:45

    Недавно я победил в школьном туре олимпиады по информатике, а скоро иду на муниципальный этап.

    Чувак, это не результат. К чему ты это написал?

    Наука и инжинерные вычисления

    Занавес.

    Насчёт самой статьи... Советую выкладывать такое на ютуб или на пикабу. Я понимаю, что в солидарность к твоему возрасту эту статью не будут удалять или минусовать, но прошу, не надо засорять обучающий раздел хабра такого уровня контентом. "Мухи отдельно, котлеты отдельно". Надеюсь на понимание.


    1. DrArgentum Автор
      21.11.2023 17:45
      +1

      Я понимаю, но постоянно стремлюсь к улучшении качества статей. В любом случае, спасибо за отзыв.


  1. theonlymusya
    21.11.2023 17:45
    +1

    Всегда была пассивным пользователем Хабра, но сейчас зарегистрировалась, чтобы оставить здесь комментарий.

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


    1. DrArgentum Автор
      21.11.2023 17:45

      Спасибо огромное за теплые слова!