Ранее мы уже не только освоили программные методы решения заданий 9, но и научились их применять к первому типу заданий 9. Теперь же перейдём ко второму типу и закрепим изученный материал.
Но для начала нам нужно познакомиться с такой полезной функцией Python, как enumerate. Давайте незамедлительно приступим к этому!
Функция enumerate
Как вы уже знаете, для решения второго типа заданий 9 нам потребуется определить наибольший или наименьший номер строки из файла. То есть необходимо как-то вести учёт индекса каждой проверяемой в цикле for строки.
Можно, конечно, создать отдельную переменную-счётчик, в которой будем хранить номер текущей строки. Но это не самый практичный и удобный способ. В Python для одновременного получения как индекса, так и значения текущего элемента используется специальная функция – enumerate.
Функция имеет следующий синтаксис:
enumerate(iterable, start=0)
Где:
iterable — любой итерируемый объект (список, кортеж, строка, файл и т.д.). В задании 9 мы работаем со списками.
start — начальное значение счётчика (по умолчанию 0). Этот параметр особенно важен для решения заданий ЕГЭ, так как нумерация строк обычно начинается с 1, а не с 0.
Функция enumerateвозвращает объект-перечислитель (enumerate object), который генерирует кортежи вида (индекс, элемент). При каждой итерации мы получаем два значения:
Порядковый номер элемента (начиная со значения параметра start)
Сам элемент последовательности
В большинстве заданий ЕГЭ нумерация строк в файле начинается с 1, а не с 0 (как принято в программировании). Параметр start=1 позволяет корректно соотнести номера строк в программе с номерами, указанными в условии задачи.
Давайте проверим работу этой функции на простом примере:
fruits = ['яблоко', 'банан', 'апельсин']
# По умолчанию start=0
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}", end = "; ")
>>>0: яблоко; 1: банан; 2: апельсин;
Теперь установим значение параметра start, равное 1:
fruits = ['яблоко', 'банан', 'апельсин']
# С параметром start=1
for index, fruit in enumerate(fruits, start=1):
print(f"{index}: {fruit}", end = "; ")
>>>1: яблоко; 2: банан; 3: апельсин;
И для закрепления рассмотрим пример посложнее. Представим, что у нас есть 5 строк по 3 числа в каждой:
Давайте найдём номера строк, в которых сумма чисел превышает 100. Создадим отдельный список, в котором будем хранить номера строк:
indexes = []
Теперь в цикле будем перебирать индексы строк и значения. Обратите внимание, что значения у нас будут в виде списков с числами каждой строки!
for idx, nums in enumerate(data, start=1):
Теперь в переменной total подсчитаем сумму чисел строки:
total = sum(nums)
Если эта сумма больше 100, то сохраняем номер строки (idx) в список indexes:
if total > 100:
indexes.append(idx)
В конце выведем значение переменной indexes. Весь код у нас будет такой:
data = [
[15, 23, 41],
[30, 72, 55],
[22, 18, 60],
[85, 33, 21],
[40, 40, 15]
]
indexes = []
for idx, nums in enumerate(data, start=1):
total = sum(nums)
if total > 100:
indexes.append(idx)
print(f'Сумма чисел >100: {indexes}')
>>>Сумма чисел >100: [2, 4]
Теперь усложним задачу: найдём наименьший номер строки, сумма чисел в которой больше 100. Конечно, можно просто взять первое число из списка indexes. Но на практике нецелесообразно создавать отдельный список для всех индексов, тем более если нам нужен только первый из них.
Давайте поступим так: как только выполнится условие, мы сразу выведем значение номера строки (idx) на экран и на этом завершим выполнение цикла оператором break. Выглядеть это будет следующим образом:
data = [
[15, 23, 41],
[30, 72, 55],
[22, 18, 60],
[85, 33, 21],
[40, 40, 15]
]
for idx, nums in enumerate(data, start=1):
total = sum(nums)
if total > 100:
print(f'Наименьший номер строки: {idx}')
break
>>>Наименьший номер строки: 2
Отлично, мы добились желаемого! А что, если потребуется не наименьший, а наибольший номер строки?
Тогда давайте просто «перевернём» список: будем идти не с первой строки, а с последней. В таком случае при первом же выполнении условия у нас уже будет номер самой последней подходящей строки.
Перевернуть список можно с помощью такого среза: [::-1]. Только вспомните, что функция enumerate возвращает нам не список, а специальный объект (enumerate object), к которому применять срезы нельзя!
Но не беда, можем просто получить из этого enumerate-объекта список, обернув его в функцию list:
list(enumerate(data, start=1))
Теперь мы готовы найти наибольший номер строки:
data = [
[15, 23, 41],
[30, 72, 55],
[22, 18, 60],
[85, 33, 21],
[40, 40, 15]
]
for idx, nums in list(enumerate(data, start=1))[::-1]:
total = sum(nums)
if total > 100:
print(f'Наибольший номер строки: {idx}')
break
>>>Наибольший номер строки: 4
Итак, давайте подведём итоги применения функции enumerate в рамках заданий 9. Если нам нужно найти наименьший индекс строки, то последний блок кода будем оформлять следующим образом:
for idx, line in enumerate(data, start=1):
if f1(line) and f2(line):
print(idx)
break
Здесь в line находятся числа каждой строки, а в data – все данные, считанные из файла. В функциях f1 и f2, как обычно, результаты выполнения условий.
Если же требуется найти наибольший номер строки, то блок кода будет выглядеть так:
for idx, line in list(enumerate(data, start=1))[::-1]:
if f1(line) and f2(line):
print(idx)
break
На этом можем закончить разбор функции enumerate и перейти к решению заданий 9.
Пример 1
Начнём с такой формулировки:
Откройте файл электронной таблицы, содержащей в каждой строке шесть натуральных чисел. Определите наименьший номер строки таблицы, для чисел которой выполнены оба условия:
— в строке есть только одно число, которое повторяется дважды, остальные четыре числа различны; — повторяющееся число строки не меньше, чем среднее арифметическое четырёх её неповторяющихся чисел.
Как уже говорили в прошлой статье, в решениях далее будем опускать первый и последний блоки кода, в которых происходят чтение данных и вывод ответа. Сразу будем писать функции для проверки условий.
Начнём с первого условия, в котором нужно найти одно повторяющееся дважды число и убедиться, что остальные числа встречаются в строке только по одному разу.
Давайте найдём все числа строки, которые повторяются дважды, вот такой конструкцией:
cnt_2 = {i for i in line if line.count(i) == 2}
Здесь у нас будет множество всех таких чисел. В подходящей нам строке в этом множестве должен быть всего один элемент!
А находить все неповторяющиеся числа мы уже умеем:
no_repeat = [i for i in line if line.count(i) == 1]
Осталось лишь потребовать, чтобы во множестве cnt_2 был только один элемент, а в списке no_repeat – 4:
len(cnt_2) == 1 and len(no_repeat) == 4
Соберём все вместе и оформим в виде функции:
def f1(line):
# Повторяется дважды
cnt_2 = {i for i in line if line.count(i) == 2}
# Все неповторяющиеся
no_repeat = [i for i in line if line.count(i) == 1]
return len(cnt_2) == 1 and len(no_repeat) == 4
Во втором условии нам снова предстоит работать всё с теми же повторяющимся числом и списком неповторяющихся чисел no_repeat.
Только теперь нужно найти среднее арифметическое неповторяющихся чисел. Сделаем это такой записью:
mean = sum(no_repeat)/len(no_repeat)
В конце потребуем, чтобы первый элемент списка cnt_2 — повторяющееся дважды число — был больше или равен значению переменной mean:
cnt_2[0] >= mean
В виде функции это записывается так:
def f2(line):
# Повторяется дважды
cnt_2 = [i for i in line if line.count(i) == 2]
# Все неповторяющиеся
no_repeat = [i for i in line if line.count(i) == 1]
# Среднее арифм. неповторяющихся
mean = sum(no_repeat)/len(no_repeat)
return cnt_2[0] >= mean
Добавим к нашим двум функциям блоки кода с чтением данных и с проверкой каждой строки и выводом ответа:
# Чтение данных из файла
with open('9016.txt') as f:
data = [list(map(int, i.split())) for i in f]
# Только одно число повторяется дважды
# остальные четыре числа различны
def f1(line):
# Повторяется дважды
cnt_2 = {i for i in line if line.count(i) == 2}
# Все неповторяющиеся
no_repeat = [i for i in line if line.count(i) == 1]
return len(cnt_2) == 1 and len(no_repeat) == 4
# Повторяющееся число строки не меньше,
# чем среднее арифметическое четырёх неповторяющихся
def f2(line):
# Повторяется дважды
cnt_2 = [i for i in line if line.count(i) == 2]
# Все неповторяющиеся
no_repeat = [i for i in line if line.count(i) == 1]
# Среднее арифм. неповторяющихся
mean = sum(no_repeat)/len(no_repeat)
return cnt_2[0] >= mean
for idx, line in enumerate(data, start=1):
if f1(line) and f2(line):
print(idx)
break
В результате работы программы получим ответ на это задание – число 34.
Но обратите внимание, что в обеих функциях мы используем одни и те же конструкции для поиска неповторяющихся чисел и числа, которое повторяется дважды. Так делать нехорошо: во-первых, нарушается принцип DRY (don’t repeat yourself), во-вторых, это замедляет выполнение кода, ведь мы дважды выполняем одинаковые действия.
Не для каждого задания есть необходимость отдельно писать функции под каждое условие. Порой этим можно пренебречь и проверять выполнение условия непосредственно внутри цикла for. Давайте перепишем наш код, исключив из него эти функции.
И, раз уж собрались вносить изменения, то избавимся и от ручного вычисления среднего арифметического – используем лучше готовуюфункциюmean из модуля statistics. В итоге придём к такому виду нашей программы:
from statistics import mean
with open('9016.txt') as f:
data = [list(map(int, i.split())) for i in f]
for idx, line in enumerate(data, start=1):
cnt_2 = [i for i in line if line.count(i) == 2]
no_repeat = [i for i in line if line.count(i) == 1]
if len(cnt_2) == 2 and len(no_repeat) == 4 and cnt_2[0] >= mean(no_repeat):
print(idx)
break
В результате её работы получаем всё тот же ответ – 34.
Пример 2
Следующим рассмотрим задание с такой формулировкой:
Откройте файл электронной таблицы, содержащей в каждой строке пять натуральных чисел. Определите наибольший номер строки таблицы, для которой выполнены оба условия:
– в строке все числа различны; – удвоенная сумма минимального и максимального чисел строки равна утроенной сумме трёх её оставшихся чисел.
Сразу приступим к созданию функции для проверки первого условия. Она будет достаточно простой и короткой: нам всего лишь нужно проверить, что количествоэлементов в списке такое же, как и во множестве, полученном из этого списка. Выглядеть это будет так:
def f1(line):
return len(line) == len(set(line))
В целом, можно это выражение написать сразу в условие внутри цикла или же заменить на такую лямбда-функцию:
uniqs = lambda line: len(line) == len(set(line))
Теперь перейдём ко второму условию. В прошлой статье мы уже работали с наибольшим и наименьшим значениями через функции max и min. В этот раз давайте пойдём через сортировку и срезы.
Отсортируем исходный список по возрастанию:
line.sort()
Теперь найти удвоенную сумму минимального и максимального числа можно сложив первый и последний элемент списка и умножив эту сумму на 2:
doubled = (line[0] + line[-1]) * 2
А утроенное значение суммы оставшихся чисел получим путём сложения чисел от второго до предпоследнего элемента списка и умножения этой суммы на 3:
tripled = sum(line[1:-1]) * 3
В конце потребуем равенства значений переменных doubled и tripled. Запишем все в одной функции:
Решение почти готово. Осталось не забыть, что нам нужен наибольший номер строки. Так что оборачиваем enumerate в список и инвертируем его порядок срезом [::-1]. Тогда весь код нашей программы будет выглядеть следующим образом:
# Чтение данных из файла
with open('9013.txt') as f:
data = [list(map(int, i.split())) for i in f]
# Все числа различны
def f1(line):
return len(line) == len(set(line))
# Удвоенная сумма минимального и максимального
# равна утроенной сумме трёх её оставшихся чисел.
def f2(line):
line.sort()
doubled = (line[0] + line[-1]) * 2
tripled = sum(line[1:-1]) * 3
return doubled == tripled
for idx, line in list(enumerate(data, start=1))[::-1]:
if f1(line) and f2(line):
print(idx)
break
Запустив программу, получим ответ на это задание — число 13412.
Итоги
Во втором типе задания 9 программа ищет номер строки. enumerate сразу даёт и строку, и её номер, поэтому остаётся правильно выбрать направление просмотра.
Шаблон решения
with open('*1.txt') as file:
data = [list(map(int, line.split())) for line in file]
answer = 0
for index, row in enumerate(data, start=1):
if *2:
answer = index
*3
print(answer)
Здесь *1 — имя файла, *2 — все условия для строки. Вместо *3 напишите break, если нужен первый номер. Если нужен последний, удалите эту строку: answer будет обновляться до конца файла. Либо сделайте срез [::-1] и оставьте break
В следующей статье завершим серию и разберём третий тип, где после поиска строки нужно выполнить действие с её числами.
Разберём эту тему на бесплатном пробном занятии
Преподаватель покажет, как решать такие задачи, и подскажет, с чего начать подготовку. Группы до 8 человек, очно в Калининграде и онлайн.
Мы используем файлы cookie. Необходимые обеспечивают работу сайта: корзину и вход в личный кабинет. С вашего согласия мы также используем сервис Яндекс Метрика, чтобы анализировать посещаемость и улучшать сайт.
Подробнее — в политике конфиденциальности.