Числовые последовательности

Как читать числовые данные и находить отдельные элементы, соседние пары и тройки по условиям задания 17.

Задание 17

Теория

Числа в файле

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

Такой упорядоченный ряд значений называют числовой последовательностью. Здесь «последовательность» нужно понимать буквально: важны и сами числа, и их очередь. Например, для строк 4, −2, 7 последовательность именно такая: 4, −2, 7. Если поменять строки местами, значения останутся, но соседства изменятся.

Python сначала читает строки, затем превращает каждую в целое число. Этот способ подробно разобран в уроке мини-курса «Чтение данных из файла». Здесь также важно сохранить порядок прочитанных чисел, поэтому соберём их в список. Квадратные скобки создают список; если нужно освежить эту тему, смотри урок «Списки».

В записи for line in file слова означают: повторить действие для каждой строки файла. Такой повторяющийся порядок команд называется циклом. Ниже цикл будет брать по очереди числа из уже созданного списка.

В записи целиком это читается так: для каждой строки выполнить int(line) и положить получившееся число в новый список. Строки не склеиваются и не сортируются — каждое значение превращается в число и сохраняет своё место.

with open("numbers.txt", encoding="utf-8") as file:    numbers = [int(line) for line in file] print(numbers)

Если в файле стоят строки 12, −3 и 8, переменная numbers получит список чисел в том же порядке. Цикл потом сможет проверять каждое значение. О том, как читать файл строка за строкой и получать из текста числа, рассказывает связанный урок по работе с файлами.

Разберём на примере
Переведите строки файла в числа
В файле записаны 12, −3 и 8 — по одному числу на строку. Что окажется в списке?
  1. Первая строка станет целым числом 12.
  2. Вторая строка станет целым числом −3.
  3. Третья строка станет целым числом 8.
  4. Получится список [12, −3, 8]; отрицательное число и порядок сохранились.

Отдельные числа

Иногда условие относится к одному числу: например, нужно посчитать положительные числа, которые делятся на три. Берём числа по одному, проверяем каждое и увеличиваем счётчик, если ответ на проверку «да». Счётчик — переменная, где хранится, сколько подходящих чисел уже встретилось. О нём и переменных, которые меняются в цикле, подробнее рассказывает урок о счётчиках и накопителях.

Проверку «подходит или нет» назовём условием отбора. Название новое, но смысл знакомый: для каждого числа Python задаёт вопрос с ответом «да» или «нет». Например, сразу после словесного условия «положительное и делится на три» можно записать: number > 0 and number % 3 == 0.

Разберём на примере
Посчитайте положительные числа, кратные трём
В списке [−6, 3, 8, 12, −3] найдите количество положительных чисел, которые делятся на 3 без остатка.
  1. Начинаем со счётчика count = 0: пока ещё ничего не проверили.
  2. Число −6 не положительное — пропускаем его.
  3. Число 3 подходит, счётчик становится равен 1.
  4. Число 8 не делится на три, а 12 подходит: теперь счётчик равен 2.
  5. Число −3 отрицательное. Ответ — 2.
numbers = [-6, 3, 8, 12, -3]count = 0 for number in numbers:    if number > 0 and number % 3 == 0:        count += 1 print(count)  # 2

Остаток от деления на три записывается знаком %; нулевой остаток означает, что число делится нацело. Слово and требует, чтобы обе проверки оказались верными. Запись count += 1 прибавляет к счётчику единицу.

В другой задаче нужно найти не количество, а самое большое или самое маленькое подходящее число. Эти значения называют максимумом и минимумом; вместе — экстремумом. Начни с первого числа, которое прошло условие. Затем сравнивай с ним остальные подходящие значения и заменяй ответ, если встретилось большее или меньшее. Такой выбор результата разбирается и в уроке «Отбор результата».

Разберём на примере
Найдите наибольшее положительное чётное число
Даны числа [−8, 14, 6, −2, 11]. Ответ нужно искать среди положительных чисел, делящихся на 2.
  1. Число −8 чётное, но не положительное — оно не подходит.
  2. Первое подходящее число — 14. Запишем его как текущий максимум.
  3. Число 6 меньше 14; максимум не меняется.
  4. Число −2 отрицательное, а 11 нечётное. Ответ остаётся равен 14.
Неверно
Начну поиск максимума с нуля: подходящие числа положительные.
Как правильно
Здесь ноль случайно сработает, но при поиске среди отрицательных чисел он мог бы оказаться больше всех подходящих значений и сам не встретиться в файле. Надёжнее записать первым результатом первое число, которое прошло условие отбора. Для подсчёта такая проблема не возникает: счётчик действительно начинается с нуля.

Соседние пары

Иногда условие касается двух чисел, которые стоят рядом в файле. Такую пару называют соседней. В списке [5, 2, 9, 4] пары такие: (5, 2), (2, 9) и (9, 4). Пара (5, 9) не соседняя: между числами в файле стоит 2.

Чтобы записать пару в коде, нужно знать порядковый номер числа в списке. Такой номер называется индексом. В Python индексы начинаются с нуля: у первого числа индекс 0, у второго — 1. Если индекс первого числа пары — i, то второе находится по индексу i + 1. Подробнее о списках и обращении к их элементам — в уроке «Списки».

У последнего числа уже нет соседа справа. Поэтому начинаем пару с индексов от нуля до предпоследней позиции. Функция len сообщает длину списка, а range(len(numbers) - 1) выдаёт все индексы начала пары.

Разберём на примере
Посчитайте пары с разной чётностью
Для [5, 2, 9, 4] посчитайте соседние пары, где одно число чётное, а другое нечётное.
  1. С индекса 0 получаем (5, 2). Пара подходит.
  2. С индекса 1 получаем (2, 9). Она тоже подходит.
  3. С индекса 2 получаем (9, 4). Это последняя пара; она подходит.
  4. Всего три пары, значит ответ — 3.
numbers = [5, 2, 9, 4]count = 0 for i in range(len(numbers) - 1):    left = numbers[i]    right = numbers[i + 1]    if left % 2 != right % 2:        count += 1 print(count)  # 3

Переменные left и right временно называют числа в паре так, чтобы проверку было легче читать. Пара (5, 2) и пара (2, 9) используют число 2 обе — это правильно: мы считаем все соседства, а не стараемся избегать повторов.

Разберём на примере
Найдите наибольшую сумму соседней пары
В списке [4, 10, −3, 8] найдите наибольшую сумму двух соседних чисел.
  1. Сумма пары (4, 10) равна 14. Пока это лучший результат.
  2. Пара (10, −3) даёт 7, меньше 14.
  3. Пара (−3, 8) даёт 5. Ответ остаётся равен 14.
Неверно
Проверю пары (5, 2) и (9, 4), чтобы числа в ответе не повторялись.
Как правильно
Так теряется пара (2, 9). Если условие говорит о любых соседних числах, каждое соседство нужно проверить. Пары пересекаются: число может участвовать сначала в паре с предыдущим, затем — со следующим.

Соседние тройки

Если условие касается трёх чисел подряд, передвигаем начало группы по тем местам, где справа помещаются ещё два числа. Для списка [2, 7, 4, 9, 6] это тройки (2, 7, 4), (7, 4, 9) и (4, 9, 6). Они тоже пересекаются: число 4 входит в каждую.

Начало тройки имеет индекс i, а остальные числа стоят на местах i + 1 и i + 2. Последний допустимый индекс начала — за два места до конца. Поэтому цикл записывают как range(len(numbers) - 2).

Разберём на примере
Посчитайте тройки с положительной суммой
Для списка [−5, 8, 2, −4, 7] посчитайте соседние тройки, сумма которых больше нуля.
  1. Первая тройка (−5, 8, 2) имеет сумму 5 и подходит.
  2. Следующая тройка (8, 2, −4) имеет сумму 6 и подходит.
  3. Последняя тройка (2, −4, 7) имеет сумму 5 и подходит.
  4. Найдено три тройки, ответ — 3.
numbers = [-5, 8, 2, -4, 7]count = 0 for i in range(len(numbers) - 2):    total = numbers[i] + numbers[i + 1] + numbers[i + 2]    if total > 0:        count += 1 print(count)  # 3

Здесь условие «сумма больше нуля» записано как total > 0. Если бы подошёл и ноль, нужно было бы написать total >= 0. Внимательно различай слова «больше» и «не меньше», «меньше» и «не больше»: строгие и нестрогие сравнения дают разные ответы.

В следующей задаче проверяется не сумма всей тройки: важно, насколько её середина выше двух соседей. Для средней позиции списка удобнее оставить три имени. Если середина стоит под индексом i, то левое число лежит под индексом i - 1, сама середина — под i, а правое число — под i + 1. Первый элемент не может быть серединой: у него нет левого соседа. Последний тоже не подходит, потому что у него нет правого.

Разберём на примере
Найдите строгие вершины среди троек
В последовательности [4, 9, 3, 7, 7, 2, 10, 1] посчитайте тройки, у которых середина больше обоих соседей.
  1. При i = 1 числа слева, в середине и справа — 4, 9, 3. Проверки 9 > 4 и 9 > 3 обе верны: считаем эту тройку.
  2. При i = 2 середина равна 3, она не больше ни одного из соседей.
  3. При i = 3 середина 7 больше левого числа 3, но равна правому числу 7. Строгое сравнение не проходит.
  4. При i = 4 середина 7 равна левому числу 7, поэтому эта тройка тоже не подходит.
  5. При i = 5 середина 2 меньше обоих соседей. При i = 6 тройка 2, 10, 1 подходит. Ответ — 2.
numbers = [4, 9, 3, 7, 7, 2, 10, 1]count = 0 for i in range(1, len(numbers) - 1):    left = numbers[i - 1]    middle = numbers[i]    right = numbers[i + 1]    if middle > left and middle > right:        count += 1 print(count)  # 2

Знак > означает «строго больше»: равенство соседу не подходит. В цикле начало равно 1, потому что только со второго числа появляется левый сосед; остановка перед последним индексом оставляет правого соседа. Поэтому в каждой строке кода существуют все три значения left, middle и right.

Разберём на примере
Найдите наибольшую сумму соседней тройки
Сравните суммы троек в [1, 5, −2, 8].
  1. Первая тройка (1, 5, −2) имеет сумму 4. Запишем её как лучший результат.
  2. Последняя тройка (5, −2, 8) имеет сумму 11.
  3. 11 больше 4, поэтому ответ — 11.
Неверно
Для троек оставлю ту же границу range(len(numbers) - 1), что и для пар.
Как правильно
Тройке нужны позиции i, i + 1 и i + 2. Слишком позднее начало заставит Python обратиться к позиции, которой нет. Для группы из трёх чисел оставь после начала ещё два места.

Когда нужны два прохода

Бывает, что пару нужно сравнить со средней величиной всех чисел файла. Среднее арифметическое — это сумма всех чисел, разделённая на их количество. Пока не просмотрен весь список, среднее неизвестно, а значит, первую пару ещё нельзя проверить.

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

В первом цикле можно накопить сумму, а потом разделить её на количество чисел. Функция sum(numbers) складывает значения списка, а len(numbers) сообщает, сколько их. Это приём из урока о счётчиках и накопителях. Во втором цикле будут проверяться пары. Если нужен разбор того, как после цикла оставить правильный результат, поможет урок «Отбор результата».

Разберём на примере
Посчитайте пары, сумма которых больше среднего файла
Для [1, 4, 0, 2, 13] найдите среднее всех чисел, затем посчитайте соседние пары с суммой строго больше среднего.
  1. В первом проходе складываем все значения: 1 + 4 + 0 + 2 + 13 = 20.
  2. В списке пять чисел, поэтому среднее равно 20 / 5 = 4.
  3. Во втором проходе проверяем пары по очереди: сумма пары (1, 4) равна 5 и подходит; сумма (4, 0) равна 4 — это ровно среднее, поэтому пара не подходит; сумма (0, 2) равна 2 и меньше среднего; сумма (2, 13) равна 15 и подходит.
  4. Счётчик увеличился для первой и последней пары. Ответ и вывод программы — 2.
numbers = [1, 4, 0, 2, 13]average = sum(numbers) / len(numbers)  # 4count = 0 for i in range(len(numbers) - 1):    if numbers[i] + numbers[i + 1] > average:        count += 1 print(count)  # 2

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

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

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

Проверка границ и ответа

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

Проверь границы цикла
  1. Отдельные числа. Проверь все значения, от первого до последнего.
  2. Пара. Первое число пары может стоять до предпоследней позиции; второе — справа от него.
  3. Тройка. После начала группы должны оставаться ещё две позиции.
  4. Общая характеристика. Сначала вычисли её по всем нужным данным, потом используй для отбора.
  5. Максимум или минимум. Начни с подходящего значения из файла, а не с придуманного числа.

Проверим тройки на коротком наборе [1, 8, 3, 10]. Нужно найти наибольшую положительную сумму соседней тройки. У первой тройки начало с индексом 0, у второй — с индексом 1. Начало с индексом 2 уже не подходит: от этой позиции до конца есть только два числа всего, а для тройки нужны три. Ручной перебор — простой способ увидеть все группы до запуска программы.

Разберём на примере
Проверьте перебор троек по шагам
Для [1, 8, 3, 10] найдите наибольшую сумму соседней тройки, если она должна быть положительной.
  1. С начала 0 получаем (1, 8, 3); сумма равна 12.
  2. С начала 1 получаем (8, 3, 10); сумма равна 21.
  3. Другого допустимого начала нет: для позиции 2 не останется трёх чисел.
  4. Обе суммы положительные, а 21 больше 12. Ответ — 21.

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

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

На экзамене

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

Рабочий порядок решения
  1. Прочитай данные. Одна строка — одно целое число; порядок строк может быть важен.
  2. Запиши проверки. Отдельно отметь требования к числам и к паре или тройке.
  3. Выбери границу. Для группы оставь справа место для каждого её числа.
  4. Найди ответ. Считай подходящие группы или обновляй максимум/минимум после проверки условия.
  5. Проверь вручную. На коротком списке выпиши кандидатов и сравни с перебором программы.

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

Практика

Практика временно недоступна. Можно продолжить читать теорию.

Итог

Что получилось

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

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

Проверьте себя
Если поменять две строки местами, могут ли измениться соседние пары?
Да. Значения останутся теми же, но их порядок и соседства изменятся.
Сколько соседних пар можно составить из семи чисел?
Шесть: у каждого из первых шести чисел есть сосед справа.
Какой последний допустимый индекс начала тройки в списке из семи чисел?
Индекс 4: после него остаются индексы 5 и 6. Всего пять троек.
Когда вычислить среднее всех чисел, если оно нужно для отбора соседних пар?
Перед перебором пар: сначала нужен весь список, чтобы найти общее среднее.
Почему нельзя всегда начинать поиск максимума с нуля?
Если подходящие числа отрицательные, ноль может оказаться больше всех и не встретиться в файле. Начни с первого подходящего числа.