Найти в Дзене

Программные методы решения 9 задания

Оглавление

О задании

С основными формулировками, типами и методами решения 9 заданий мы уже знакомились ранее, когда изучали подход к решению данных заданий через редактор электронных таблиц. Хоть это задание и нацелено на работу с электронными таблицами, мы все же разберем вариант его решения с помощью программы на языке Python. При должной сноровке такой метод позволит быстрее и надежнее решить задание 9 ЕГЭ по информатике.

К тому же, сдавая ЕГЭ по информатике, вы рассчитываете на поступление в технический вуз, где важным аспектом обучения является именно написание собственных программ, а не использование офисных приложений.

Далее давайте вкратце вспомним отличия в типах этого задания и разберем основные подходы к его решению.

Как уже говорилось ранее, разделяем мы 9 задания на три типа по значению, которое нужно дать в ответ:

  1. Количество строк, удовлетворяющих условию
  2. Минимальный или максимальный номер такой строки
  3. Сумму чисел (или результат любой другой операции с числами) в этой строке

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

  1. Только одно условие из двух (здесь используем исключающее ИЛИ)
  2. Хотя бы одно условие из двух (используем обычное ИЛИ)
  3. Сразу оба условия (пользуемся логической функцией И)

Все это уже разобрано с примерами использования в электронных таблицах. Теперь мы переложим ту же самую логику решения на программу в Python – только в другой форме!

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

В этой статье мы:

  • Разберём базовые подходы к экспорту данных из файла электронной таблицы
  • Научимся обрабатывать эти данные средствами языка Python
  • Рассмотрим основные методы, которые будем применять для решения заданий

В следующих статьях уже научимся применять эти методы к конкретным заданиям – на каждый тип выделим по отдельной статье.

Экспорт данных

Как вам уже известно, приложенные к заданию 9 файлы имеют расширение «ods». По умолчанию Python не умеет открывать файлы с таким расширением. Что же делать? Всё просто: необходимо сохранить все данные в файл с другим расширением, например, «txt».

Сохранение данных

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

-2

Затем выбираем самую первую ячейку A1 и нажимаем комбинацию клавиш: «Ctrl+Shift+Стрелка вправо». Таким образом мы выделим всю первую строку с данными.

-3

Теперь «перетащим» выделение вниз до конца. Для этого используем похожую комбинацию – «Ctrl+Shift+Стрелка вниз».

-4

Как только все данные выделены, скопируем их. Лучше всего использовать сочетание клавиш «Ctrl+C», но можно и правой кнопкой мыши нажать на выделенную область и выбрать в контекстном меню пункт «Копировать».

-5

Теперь нужно как-то сохранить эти данные в текстовом формате. Для этого откроем стандартное приложение Блокнот и вставим скопированные данные сочетанием клавиш «Ctrl+V».

-6

Всё почти готово! Можно либо закрыть файл (и Блокнот предложит сохранить его в выбранном формате), либо вручную открыть меню «Файл» и выбрать пункт «Сохранить» / «Сохранить как».

-7

Откроется диалоговое окно, в котором вам будет предложено выбрать имя и тип файла. Вписываем любое понравившиеся имя, не меняем ни тип файла, ни кодировку, просто нажимаем «Сохранить». Лучше всего сохранить этот файл в одной папке с тем python-файлом, в котором вы будете решать 9 задание.

-8

Готово, именно по такому алгоритму мы будем сохранять данные для каждого задания.

Чтение данных в Python

Файл с данными у нас имеется – отлично! Теперь давайте научимся считывать с него всю информацию в Python.

Прочитать данный файл в вашей программе можно двумя способами: просто вызовом функции open() или с помощью менеджера контекста with. Второй способ более предпочтителен – он гарантированно закрывает файловый поток, тем самым обеспечивая безопасное выполнение вашего кода.

Подробнее про файловые потоки можете почитать в этой статье.

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

Открываем файлы мы следующей конструкцией:

-9

Здесь «file.txt» – название вашего файла, а из переменной f мы и будем считывать данные.

Далее, внутри блока менеджера контекста (после двоеточия, с отступом), пишется код, в котором происходит обработка данных из файла: их чтение, запись или перемещение указателя внутри файла.

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

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

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

Например, если в файле первые 3 строки такие:

1 2 3 4

5 6 7 8

9 1 2 3

То в коде их следует хранить в виде такого списка списков:

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 1, 2, 3]]

Не стоит забывать и о том, что данные у нас считываются в виде обычного текста (тип str – строка). То есть их необходимо ещё перевести в целочисленный тип данных – применить функцию int() к каждому элементу списка (числу).

Все описанные выше действия можно выполнить всего одним списочным включением:

-10

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

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

Основа конструкции – это цикл for i in f, который проходит по открытому файлу. Когда мы итерируемся (проходимся) по файловому объекту f, Python автоматически читает его построчно. На каждой итерации (повторении цикла) переменная i получает очередную строку из файла в виде текста, например, «1 2 3 4\n» с символом переноса строки в конце.

Для каждой считанной строки i выполняется цепочка преобразований. Сначала применяется метод split(), который разбивает строку на отдельные элементы. Когда split() вызывается без аргументов, он использует любые пробельные символы (пробелы, табуляции, переносы строк) в качестве разделителей. Таким образом, строка «1 2 3 4\n» превращается в список строковых элементов [‘1’, ‘2’, ‘3’, ‘4’]. Важно понимать, что на этом этапе числа всё ещё представлены как текст, а не как числовые значения.

Далее к полученному списку строк применяется функция map(int, …). Она принимает два аргумента: функцию для применения (в нашем случае int, без скобок!) и последовательность элементов. Она применяет функцию int() к каждому элементу списка, преобразуя строковые представления чисел в целочисленные значения. Однако map() возвращает не список, а специальный объект-итератор, который вычисляет значения по мере необходимости.

Чтобы получить из map-объекта обычный список Python, используется функция list(). Она проходит по всем элементам итератора и собирает их в список. В результате из map-объекта с числами 1, 2, 3, 4 получается список [1, 2, 3, 4].

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

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

Давайте на примере разберём как работает этот код. Представим, что в файле с названием «file.txt» у нас находится три строки с числами:

-11

Считываем данные:

-12

Обратиться к первому списку [1, 2, 3, 4] можно с помощью индекса 0data[0]. Обратиться к первому элементу первого списка (единице) можно так: data[0][0].

-13

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

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

Количество условий

Ранее мы уже определили три функции LibreOffice Calc, которые позволяют определить, сколько условий одновременно выполняется в каждой строке:

  1. Функция И(), когда должны выполняться оба условия
  2. Функция ИЛИ(), когда должно выполняться хотя бы одно
  3. Функций ИСКЛИЛИ(), когда должно выполняться только одно условие

В Python все работает идентично – у нас есть три оператора, которые реализуют соответствующий функционал:

  1. Оператор and вместо И()
  2. Оператор or вместо ИЛИ()
  3. Оператор ^ вместо ИСКЛИЛИ()

Эти логические операторы помещаются между двумя условиями, которые проверяются числа в строке:

-14

Всю эту конструкцию нужно поместить в местный аналог функции ЕСЛИ() – оператор if:

-15

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

-16

Каждое условие мы будем проверять внутри отдельной функции. Эта функция возвращает логическое значение – истину, если условие выполняется в строке и ложь – если не выполняется.

Давайте далее рассмотрим типовые шаблоны таких функций.

Типовые шаблоны функций

Вспомним, с какими условиями мы сталкивались в прошлых статьях:

  1. В строке все числа различны
  2. Сумма наибольших чисел строки не больше суммы её оставшихся чисел
  3. Среди чисел нет чётного
  4. В строке есть N чисел, каждое из которых повторяется M раз, остальные числа различны
  5. Среднее арифметическое минимального и максимального чисел строки больше среднего арифметического оставшихся её чисел

То есть эти условия можно разделить на 5 групп:

  • Работа с уникальными числами
  • Работа с наибольшими или наименьшими числами
  • Работа с чётностью
  • Подсчёт повторений чисел
  • Работа со средним арифметическим

Именно под эти группы мы и составим типовые функции. Далее вы сможете с лёгкостью изменять их в зависимости от заданного условия.

Все числа различны

Начнём с первой группы – нам нужно определить, что числа в строке различны, то есть никакое число не повторяется дважды. В Python есть замечательный тип данных, который позволяет избавиться от дубликатов – множество (set).

Идея простая: если в исходном списке с числами строки и во множестве, полученном из этого списка, одинаковое количество элементов, то это означает, что дубликатов в исходном списке не было.

Множество автоматически удаляет повторяющиеся элементы. Поэтому если количество элементов в списке и множестве совпадает, значит, все числа в списке уникальны.

В коде это можно записать следующей функцией:

-17

Что здесь происходит:

  • set(line) – создаём множество из списка (дубликаты удаляются автоматически)
  • len(line) – количество элементов в исходном списке
  • len(set(line)) – количество уникальных элементов
  • Если они равны – все числа различны, функция вернёт True

Наибольшие и наименьшие числа

Теперь вторая группа, где мы работаем с наибольшими или наименьшими числами строки. Как вы помните, в Calc мы применяли сортировку исходных значений через функцию НАИБОЛЬШИЙ().

В Python для сортировки используется метод sort() или функция sorted().

Вот как выглядит сортировка по возрастанию через метод sort():

-18

Важно: метод sort() изменяет исходный список!

Для сортировки в обратном порядке следует добавить в вызываемый метод параметр reverse=True:

-19

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

В коде это записывается так:

-20

Разбор кода:

  • line.sort() – сортируем список по возрастанию
  • line[-2:] – срез, берущий два последних элемента (наибольших после сортировки)
  • line[:-2] – срез, берущий все элементы до двух последних (оставшиеся числа)
  • sum(line[-2:]) – сумма двух наибольших чисел
  • sum(line[:-2]) – сумма оставшихся чисел
  • Сравниваем их: если первая сумма не больше второй, возвращаем True

Чётность чисел

В третьей группе условий нам предстоит определять чётность чисел. Вспомним, что по определению чётными называются числа, которые делятся на 2 без остатка. Именно остаток от деления чисел на 2 и будем проверять.

Для проверки каждого числа в списке нам не обойтись без цикла for. Базовый подход такой: в цикле перебираем каждое число, вычисляем остаток от деления его на 2, если он равен 0, то число чётное, иначе – нечётное.

В явном виде подсчёт чётных чисел можно записать так:

-21

Пояснение:

  • num % 2 – остаток от деления числа num на 2
  • Если остаток равен 0 (num % 2 == 0), число чётное
  • count += 1 – увеличиваем счётчик чётных чисел на 1

В переменной count у нас хранится количество чётных чисел в списке. Если нужно, например, чтобы чётных чисел было ровно 2, то в виде функции это будет выглядеть так:

-22

Однако мы будем пользоваться более короткой и не менее эффективной записью:

-23

Что здесь происходит:

  1. x % 2 == 0 – проверяем чётность каждого числа x
  2. Это выражение возвращает True (истина) для чётных чисел и False (ложь) для нечётных
  3. for x in line – перебираем все числа в строке
  4. sum(…) – суммирует все True (считая их как 1) и False (считая их как 0)
  5. В итоге получаем количество чётных чисел
  6. == 2 – проверяем, что чётных чисел ровно 2

Эта функция делает те же действия, что и развёрнутый вариант: перебирает в цикле каждое число, вычисляет остаток от деления на 2, считает количество чётных чисел и проверяет, что их ровно 2.

Могут быть и условия, в которых требуется определить, что все числа в строке чётные или нечётные. В таком случае, когда условие должно выполняться для всех элементов списка, следует использовать функцию all(), которая возвращает True только тогда, когда все переданные в неё аргументы истинны.

Например, проверить, что среди всех чисел строки нет ни одного чётного можно такой записью:

-24

Здесь происходит следующее:

  1. num % 2 – вычисляем остаток от деления на 2 для каждого числа
  2. Для нечётных чисел остаток равен 1 (что считается истиной)
  3. Для чётных чисел остаток равен 0 (что считается ложью)
  4. all(…) – проверяет, что все остатки истинны (то есть не равны 0)
  5. Если среди них нет ни одного нуля, значит нет и чётного числа
  6. В функцию all() переданы только истинные аргументы (все числа, кроме 0, в Python истинны)
  7. Функция возвращает True

Подсчёт повторений

Для подсчёта количества вхождений элемента в список в Python существует метод count(). Давайте разберём его работу на примере.

Пусть у нас есть такой список чисел: 0, 0, 1, 2, 3. Будем перебирать в цикле каждое число и вычислять, сколько раз этот элемент встречается в списке. На основе этих данных сформируем новый список. Запишем это так:

-25

Что произошло:

  • Для первого элемента (0) метод count() нашёл 2 вхождения
  • Для второго элемента (тоже 0) снова 2 вхождения
  • Для элементов 1, 2, 3 – по 1 вхождению каждого

В получившемся списке мы видим, что есть число (ноль), которое встречается 2 раза – об этом говорят две двойки в списке occurrences. Остальные числа встречаются по одному разу, то есть они все различны.

Получить список всех различных чисел (встречающихся только один раз) можно такой записью:

-26

Объяснение:

  • Проходим по каждому элементу i в списке line
  • Проверяем условие: line.count(i) == 1 (элемент встречается ровно 1 раз)
  • Если условие истинно, добавляем элемент в новый список

Для нашего примера line = [0, 0, 1, 2, 3] получим: [1, 2, 3]

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

-27

Для нашего примера получим: {0} (множество с одним элементом – нулём)

Теперь давайте подумаем, как определить, что в списке какое-то число встречается ровно N раз? Логично предположить, что нужно проверить, что количество вхождений элемента в список равно N.

Например, определим число в нашем списке, которое встречается ровно 2 раза:

-28

Обратите внимание: в список попали оба нуля, потому что мы проходим по всем элементам и каждый раз проверяем условие.

Если бы таких чисел было больше, то в переменной cnt_2 было бы уже несколько различных чисел. Например, определим, что в списке [0, 0, 1, 1, 2, 3] ровно два числа встречаются два раза, а остальные числа различны.

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

Далее будем работать с количеством элементов в сформированных списках. В первом списке должно быть 4 элемента: 2 числа по 2 раза. Во втором – 2, то есть два оставшихся числа.

Резюмируем все наши умозаключения в виде функции:

-29

Разбор функции:

  1. cnt_2 – список всех элементов, которые встречаются ровно 2 раза. Для [0, 0, 1, 1, 2, 3] получим: [0, 0, 1, 1] (4 элемента)
  2. cnt_1 – список всех элементов, которые встречаются ровно 1 раз. Получим: [2, 3] (2 элемента)
  3. Проверяем оба условия через and:
    len(cnt_2) == 4 – в списке повторяющихся ровно 4 элемента (два числа по 2 раза)
    len(cnt_1) == 2 – в списке уникальных ровно 2 элемента
  4. Оба условия выполнены, функция возвращает True

Среднее арифметическое

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

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

  1. Вручную: просуммировать элементы и поделить сумму на их количество
  2. Через функцию: воспользоваться функцией mean() из встроенного модуля statistics

Для определения минимального или максимального числа нам доступны функции min() и max(), соответственно. Продемонстрируем их работу на простом примере:

-30

Теперь вручную найдём среднее арифметическое чисел из этого списка по знакомой всем школьной формуле:

-31

Аналогичный результат вернёт и функция mean():

-32

Мы готовы составить функцию для решения описанного ранее условия: «среднее арифметическое минимального и максимального чисел строки больше среднего арифметического оставшихся её чисел».

Распишем подробно каждый шаг вычислений в отдельной строке функции:

-33

Разбор по шагам:

  1. min_num = min(line) – находим минимальное число в строке
  2. max_num = max(line) – находим максимальное число в строке
  3. mean_min_max = (min_num + max_num) / 2 – вычисляем среднее арифметическое минимума и максимума
  4. mean_rest = (sum(line) — min_num — max_num) / (len(line) — 2) – вычисляем среднее арифметическое оставшихся чисел:
    sum(line) – сумма всех чисел
    Вычитаем минимум и максимум
    Делим на количество оставшихся чисел (
    len(line) — 2)
  5. mean_min_max > mean_rest – сравниваем средние значения

Да, такая запись выглядит немного громоздко, зато всё максимально прозрачно и очевидно!

Если вы хорошо работаете со срезами, то можно поступить иначе: отсортировать исходный список. Тогда первый элемент будет минимальным числом, последний – максимальным, а все остальные сможем получить таким срезом «[1:-1]».

В виде функции это выглядит так:

-34

Разбор:

  1. sort() – сортируем список по возрастанию
  2. После сортировки:line[0] – первый элемент (минимум)
    line[-1] – последний элемент (максимум)
    line[1:-1] – все элементы между первым и последним (оставшиеся числа)
  3. mean_min_max = (line[0] + line[-1]) / 2среднее минимума и максимума
  4. mean_rest = sum(line[1:-1]) / len(line[1:-1])среднее оставшихся чисел
  5. Сравниваем их

Итоги

В данной статье мы провели значительную подготовительную работу перед началом решения 9 заданий программным методом. Давайте подведём итог тому, чему мы научились:

  • Экспорт данных – научились сохранять данные из электронной таблицы в текстовый файл
  • Чтение файлов – освоили открытие файлов через менеджер контекста with и считывание данных
  • Преобразование данных – разобрались, как превратить текстовые данные в удобный формат списка списков
  • Логические операторы – познакомились с операторами and, or и ^ для работы с несколькими условиями
  • Типовые функции – изучили пять групп типовых функций для проверки самых распространённых условий

Теперь у вас есть полный набор инструментов для решения 9 заданий! Эти знания – фундамент, на котором мы будем строить решения конкретных задач.

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

<<< Последняя статья Следующая статья >>>