ЕГЭ · Информатика · Обработка последовательностей и файлов данных (задания 17, 25, 26, 27)
Чтение чисел из файла и накопители
Освоить базовый каркас, с которого начинается почти любое задание 17/26/27.
🎯 ЕГЭ информатика: задания 17, 25, 26, 27. №17 — обработка последовательности чисел (файл); №25 — программирование: перебор с проверкой свойства; №26 — обработка данных из файла: рейтинг; №27 — анализ данных: эффективная обработка.
- ⚠Читают файл не полностью или неверно разбирают строки в числа
- ⚠Забывают дополнительное условие (кратность, пара соседних, диапазон)
Как загрузить данные в программу
Как устроены задания с прилагаемым файлом
Четыре задания второй части — 17, 25, 26, 27 — объединены одним: к ним прилагается файл с данными, и решение состоит не в том, чтобы что-то придумать, а в том, чтобы правильно прочитать файл и провести по нему нужный проход. Форматы файлов, которые встречаются. Одно число в строке (задание 17): первая строка иногда содержит количество чисел, и её надо не забыть отбросить или учесть. Два числа в строке (задание 26): пара «объём — приоритет», «срок — цена», «вес — стоимость». Строка с несколькими числами через пробел (задания 26 и 27). Два файла — A и B (задание 27): A маленький, для отладки, B большой, на нём проверяется эффективность. Ответ записывают для обоих. Три способа прочитать файл, и когда какой брать. ```
a = [int(x) for x in open('input.txt')] # по одному числу в строке
a = list(map(int, open('input.txt').read().split())) # любой разделитель
for line in open('input.txt'): # построчно, не загружая всёx, y = map(int, line.split()) ``` Первый способ короче всего, но требует ровно одного числа в строке. Второй универсален: split() без аргументов режет по любым пробельным символам и сам отбрасывает пустые куски, так что лишние пробелы и пустые строки не мешают. Третий — потоковый: файл читается по строке, и в памяти одновременно лежит только одна строка. Именно он нужен в задании 27 для большого файла: загрузить целиком гигантский файл может просто не хватить памяти. Частая ошибка — забыть int. Метод split() возвращает строки, и сравнение «'100' > '99'» даст ложь, потому что строки сравниваются посимвольно.
| По одному в строке | a = [int(x) for x in open('input.txt')] |
|---|---|
| Через пробел / универсально | a = list(map(int, open('input.txt').read().split())) |
| split() без аргументов | разбивает по любым пробелам/переводам, без пустых элементов |
| Сумма | s = 0, внутри s += x |
|---|---|
| Максимум | mx = -10**18, чтобы первый элемент его перебил |
| Минимум | старт очень большим |
| Счётчик | cnt = 0, cnt += 1 по условию |
| Сумма | s = 0 до цикла, s += x внутри. Ноль — нейтральный элемент сложения, поэтому он и берётся стартовым |
|---|---|
| Произведение | p = 1, а не 0: с нуля произведение так и осталось бы нулём |
| Максимум | mx = None и проверка if mx is None or x > mx. Вариант с -10**18 тоже работает, но None надёжнее: он не ошибётся, если все числа окажутся ещё меньше |
| Минимум | симметрично: mn = None и x < mn |
| Счётчик | cnt = 0, cnt += 1 при выполнении условия |
| Второй максимум | две переменные, обновляются в правильном порядке: сначала сдвигаем прежний максимум во второй, потом записываем новый |
| Максимумы по группам | словарь вместо набора переменных: best[r] = max(best.get(r, -1), x) |
Где ошибаются при работе с накопителями
Ошибка 1. Заводят накопитель внутри цикла. Строка s = 0 должна стоять до цикла: внутри она обнуляла бы сумму на каждом шаге, и в ответе оказалось бы последнее число. Ошибка 2. Берут ноль стартовым значением для максимума. Если все числа отрицательные, ответом окажется ноль, которого в данных нет. Безопасно брать None или заведомо малое значение. Ошибка 3. Берут ноль стартовым значением для произведения. Произведение останется нулём навсегда. Ошибка 4. Путают «количество» и «сумму». В задании 17 почти всегда спрашивают оба числа, и каждое требует своего накопителя. Ошибка 5. Обновляют максимум не в той ветке. Если максимум ищется среди подходящих элементов, обновление должно стоять внутри проверки условия, а не после неё. Ошибка 6. Выходят за границу списка. Для пар соседних элементов цикл идёт до len(a) − 1: при i = len(a) − 1 обращение к a[i+1] выйдет за конец и программа остановится с ошибкой индекса. Ошибка 7. Не проверяют программу на маленьком примере. Прежде чем запускать её на файле из десяти тысяч чисел, прогоните на списке из пяти, ответ для которого можно посчитать в уме.
Заводи накопители ДО цикла, обновляй ВНУТРИ. Отступ 4 пробела задаёт тело цикла и веток.
Разбор примера
Сумма чётных и максимум нечётных
Дан список чисел a. Найти сумму чётных элементов и максимальный нечётный.
Показать решение по шагам
- 1.
s = 0 mx = None for x in a: if x % 2 == 0: s += x elif mx is None or x > mx: mx = x print(s, mx) - 2. Накопители заводим до цикла: s собирает сумму чётных, mx — максимум среди нечётных. Значение None означает «подходящих чисел ещё не встречалось».
- 3. Ветка elif срабатывает только для нечётных: если максимума ещё нет или очередное число больше сохранённого, обновляем mx.
- 4. После цикла s — искомая сумма, mx — искомый максимум; оба получены за один проход.
Ответ: s и mx получены за один проход
Разбор примера
Разбор: два накопителя в одном проходе
В файле по одному числу в строке. Найти количество элементов, которые больше 1000 и оканчиваются на 7, и сумму таких элементов.
Показать решение по шагам
- 1.
a = [int(x) for x in open('input.txt')]
cnt = 0 s = 0 for x in a: if x > 1000 and x % 10 == 7: cnt += 1 s += xprint(cnt, s)
- 2. Оба накопителя заведены до цикла: cnt считает подходящие элементы, s собирает их сумму.
- 3. Оба обновляются внутри одной проверки: элемент, прошедший условие, должен попасть и в счётчик, и в сумму. Если вынести s += x из-под if, сложатся все числа подряд.
- 4. Условие соединено словом and: элемент должен удовлетворять обоим требованиям. Если бы в задании стояло «или», ставилось бы or.
- 5. Проверка на малом примере: для списка [1007, 500, 2017, 7] условию удовлетворяют 1007 и 2017 — это 2 элемента и сумма 3024. Число 7 не проходит, потому что не больше 1000; 500 не оканчивается на 7.
- 6. Печать через запятую выводит два числа в одной строке через пробел — именно так и просят в задании 17.
Ответ: cnt и s печатаются в одной строке через пробел
Вопрос на проверку
Каким стоит взять стартовое значение переменной для поиска МИНИМУМА последовательности?
Ответить и проверить себя — после бесплатной регистрации.
Вопрос на проверку
Что вернёт open('data.txt').read().split() для файла с числами через пробелы и переводы строк?
Ответить и проверить себя — после бесплатной регистрации.
Разбор примера
Четыре накопителя в одном проходе
В файле записаны числа 14, 7, 22, 9, 30, 3, 18, 25. За один проход найти: сумму чётных, максимум среди нечётных, количество кратных трём и минимум всей последовательности.
Показать решение по шагам
- 1.
a = [int(x) for x in open('dannye.txt')]
summa_chet = 0 maks_nechet = None kratnyh_3 = 0 minimum = a[0]for x in a: if x % 2 == 0: summa_chet += x else: if maks_nechet is None or x > maks_nechet: maks_nechet = x if x % 3 == 0: kratnyh_3 += 1 if x < minimum: minimum = xprint(summachet, maksnechet, kratnyh3, minimum)
- 2. Шаг 0. Все четыре величины считаются в одном цикле, а не в четырёх. Это не только быстрее — это принципиально для линии 27, где данных сотни тысяч и лишний проход стоит времени.
- 3. Начальное состояние: сумма чётных 0, максимум нечётных неизвестен, счётчик кратных трём 0, минимум взят равным первому элементу (14).
- 4. x = 14. Чётное → сумма чётных 14. На 3 не делится. 14 не меньше 14, минимум не меняется.
- 5. x = 7. Нечётное → максимум нечётных был неизвестен, становится 7. На 3 не делится. 7 < 14 → минимум 7.
- 6. x = 22. Чётное → сумма 14 + 22 = 36. На 3 не делится. 22 не меньше 7.
- 7. x = 9. Нечётное, но 9 > 7 → максимум нечётных 9. Кратно трём → счётчик 1. 9 не меньше 7.
- 8. x = 30. Чётное → сумма 36 + 30 = 66. Кратно трём → счётчик 2. 30 не меньше 7.
- 9. x = 3. Нечётное, но 3 < 9 — максимум не меняется. Кратно трём → счётчик 3. 3 < 7 → минимум 3.
- 10. x = 18. Чётное → сумма 66 + 18 = 84. Кратно трём → счётчик 4. Не меньше 3.
- 11. x = 25. Нечётное, 25 > 9 → максимум нечётных 25. На 3 не делится. Не меньше 3.
- 12. Итог: сумма чётных 84, максимум нечётных 25, кратных трём 4, минимум 3.
- 13. Шаг 1. Два разных способа завести накопитель. Для суммы и счётчика стартовое значение — ноль: они только растут. Для минимума и максимума ноль не годится: если все числа положительны, минимум так и останется нулём. Правильно либо взять первый элемент (как для минимума), либо
Noneс проверкой (как для максимума нечётных) — второе надёжнее, потому что нечётных чисел в файле может не оказаться вовсе. - 14. Шаг 2. Обратите внимание на
elseу проверки чётности и на отдельныйifу кратности трём. Чётность и нечётность взаимоисключающие, поэтому тамelse. А «кратно трём» не исключает ни того ни другого — проверка идёт отдельно, и 30 попадает и в сумму чётных, и в счётчик кратных.
Ответ: 84 25 4 3
Разбор примера
Когда файл устроен не как обычно
Прочитать файл, в первой строке которого записано количество чисел, а дальше идут строки по несколько чисел через пробел.
Показать решение по шагам
- 1.
f = open('dannye.txt') n = int(f.readline()) # первая строка — счётчик a = [int(x) for line in f for x in line.split()]print(n, len(a)) # проверка: должно совпасть print(a[:5], a[-5:]) # проверка: начало и конец - 2. Шаг 1.
readline()читает одну строку и сдвигает позицию чтения. После него циклfor line in fпродолжает со второй строки — файловый объект помнит, где остановился. Это не фокус, а обычное его поведение. - 3. Шаг 2. Двойной генератор
for line in f for x in line.split()разбирает каждую строку по пробелам и вытягивает все числа в один плоский список. Порядок частей важен: сначала внешний цикл по строкам, потом внутренний по словам — так же, как во вложенныхfor. - 4. Шаг 3. Почему нельзя просто
[int(x) for x in f]. Такой разбор считает, что в строке ровно одно число, и упадёт с ошибкой на строке «12 34 56». Универсальный вариант, работающий в обоих случаях, —open('dannye.txt').read().split(): методsplit()без аргументов режет по любым пробелам и переводам строк сразу. - 5. Шаг 4. Зачем две печати проверки. Первая сверяет количество: если объявлено 2693 числа, а прочиталось 2694, значит счётчик из первой строки попал в данные. Вторая показывает начало и конец: если первым элементом стоит подозрительно круглое число, равное длине файла, ошибка та же.
- 6. Шаг 5. Что будет, если счётчик не отделить. Он станет первым элементом списка и испортит всё, что зависит от всей последовательности: сумму, среднее, максимум. Причём результат останется правдоподобным — именно поэтому ошибку так трудно заметить без проверки.
- 7. Шаг 6. Обе строки проверки убирают перед записью ответа. Их задача — поймать ошибку чтения, а не украсить вывод. Привычка печатать
len(a)и края списка стоит пяти секунд и спасает балл в любой задаче с прилагаемым файлом.
Ответ: Список чисел, длина которого совпала со счётчиком из первой строки
Пять накопителей и их стартовые значения
Почти всё, что спрашивают в заданиях с прилагаемым файлом, сводится к пяти накопителям. Отличаются они не столько обновлением внутри цикла, сколько тем, чем их инициализировать до цикла, — и именно там ошибаются.
Сумма. Стартовое значение 0, внутри цикла summa += x. Ноль здесь безопасен всегда: прибавление ничего не портит.
Счётчик. Тоже 0, внутри if условие: schetchik += 1. Обратите внимание, что счётчик увеличивается на единицу, а не на значение элемента — путать их легко, если условие сложное.
Максимум. Ноль опасен: если все числа отрицательные, максимум так и останется нулём, хотя нуля в данных нет. Надёжных вариантов два: взять первый элемент (maksimum = a[0]) или начать с None и проверять if maksimum is None or x > maksimum. Второй способ работает и тогда, когда подходящих элементов может не оказаться вовсе.
Минимум. Зеркально: нельзя стартовать с нуля, если числа положительные. Первый элемент или None.
Лучшая пара, лучшая сумма, лучший объект. Здесь накопителей два: само лучшее значение и то, что его даёт (номер, пара, объект). Обновлять их надо вместе и в одном if, иначе значение и объект разойдутся, и ответ будет составлен из разных строк файла.
Отдельно про второй максимум. Его нельзя получить, просто выбросив первый: если наибольшее значение встречается дважды, вопрос «второй по величине» обычно означает второе различное значение. Читайте условие: «второй по величине элемент» и «второе по величине значение» — это разные вещи, и разница в один элемент.
И общее правило, из-за которого рушится больше решений, чем из-за всех остальных вместе: накопители заводят до цикла, обновляют внутри, печатают после. Объявление внутри цикла обнуляет их на каждом шаге, и ответ получается посчитанным по последнему элементу.
Разбор примера
Второй по величине элемент за один проход
В последовательности 14, 7, 22, 9, 30, 3, 18, 25 найти второе по величине различное значение, не сортируя список.
Показать решение по шагам
- 1.
pervyy = None # наибольшее vtoroy = None # второе по величине, отличное от наибольшегоfor x in a: if pervyy is None or x > pervyy: vtoroy = pervyy # бывший лидер уходит на второе место pervyy = x elif x != pervyy and (vtoroy is None or x > vtoroy): vtoroy = xprint(pervyy, vtoroy)
- 2. Шаг 0. Идея. Держим две переменные — лидера и вице-лидера. Новый элемент либо обгоняет лидера (тогда прежний лидер становится вторым), либо не обгоняет, но претендует на второе место.
- 3. x = 14. Лидера нет → второй становится None, лидер 14.
- 4. x = 7. Лидера не обгоняет. Не равен лидеру, а второго нет → второй 7.
- 5. x = 22. Обгоняет 14 → второй становится 14, лидер 22.
- 6. x = 9. Не обгоняет 22; 9 не равен лидеру, но 9 < 14 — второй не меняется.
- 7. x = 30. Обгоняет 22 → второй становится 22, лидер 30.
- 8. x = 3. Не обгоняет и меньше 22 — без изменений.
- 9. x = 18. Не обгоняет 30; 18 < 22 — второй не меняется.
- 10. x = 25. Не обгоняет 30, но 25 > 22 и 25 не равен лидеру → второй 25.
- 11. Итог: лидер 30, второй 25 ✔ Проверка сортировкой: 30, 25, 22, 18, 14, 9, 7, 3 — второе значение действительно 25.
- 12. Шаг 1. Зачем
x != pervyyво второй ветке. Без этой проверки повторное вхождение максимума заняло бы второе место, и ответ на вопрос «второе различное значение» был бы неверным. Если же в условии сказано «второй по величине элемент», проверку убирают — и тогда у последовательности 30, 30, 25 ответом будет 30. - 13. Шаг 2. Зачем это вообще, если есть сортировка.
sorted(a)[-2]короче и для линии 17 вполне годится. Но сортировка стоит O(n log n) и требует держать весь файл в памяти, а такой проход — O(n) и работает потоково. В линии 27, где данных сотни тысяч, разница становится существенной.
Ответ: 30 и 25
Список или поток: когда файл можно не хранить
Прочитать файл можно двумя способами, и выбор между ними — это выбор между удобством и расходом памяти.
В список. a = [int(x) for x in open('f.txt')] — весь файл оказывается в памяти. Сто тысяч целых чисел занимают несколько мегабайт, и ничего страшного в этом нет. Список нужен, когда данные предстоит сортировать, обращаться к ним по индексу или проходить дважды (как в задачах со средним арифметическим).
Потоком. for line in open('f.txt'): — строки читаются по одной и тут же забываются. Память не растёт, сколько бы ни было данных. Так поступают, когда от файла нужны только накопители: сумма, максимум, счётчики, корзины остатков.
В линии 17 файлы небольшие, и годится любой способ. В линии 27, где чисел сотни тысяч, потоковое чтение — часть правильного решения, потому что задача там про эффективность.
Есть и третий вариант, о котором стоит знать: open('f.txt').read().split(). Он читает файл целиком одной строкой и режет её по любым пробелам и переводам строк. Это самый универсальный разбор — он одинаково работает и когда число в строке одно, и когда их несколько, — но память он расходует как список.
И отдельное предупреждение про повторное чтение. Файловый объект помнит позицию: если пройти по нему циклом до конца, второй цикл по тому же объекту не даст ни одной строки. Нужны два прохода — либо считайте данные в список один раз, либо откройте файл заново. Пустой результат второго прохода при верной в остальном программе — почти всегда именно это.
Задание №17 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.
Задание №17 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.
Задание №17 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.
Задание №17 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.