ЕГЭ · Информатика · Основы программирования на Python
Циклы и перебор
Циклы for и while — основа почти всех вычислительных заданий ЕГЭ.
🎯 ЕГЭ информатика: этот урок закрывает задание(я) 16, 25.
- ⚠Перебирают делители до N, а не до √N — программа не укладывается по времени
- ⚠Путают наибольший делитель кроме самого числа с количеством делителей
| for i in range(a, b) | i от a до b−1; range(1,6) → 1,2,3,4,5 |
|---|---|
| range(0, 10, 2) | с шагом: 0,2,4,6,8 |
| while условие | повторяется, пока условие истинно |
Цикл while: когда число повторений заранее неизвестно
While повторяет тело, пока условие истинно. Его берут там, где невозможно заранее сказать, сколько шагов понадобится. Классический пример — разбор числа по цифрам: ```
n = 12345
s = 0
while n > 0:
s += n % 10 # последняя цифра
n //= 10 # отбрасываем её
print(s) # 15``` Сколько будет шагов, зависит от длины числа, и заранее она неизвестна — поэтому for здесь неудобен. Три вещи, без которых while зацикливается. 1. Переменная условия должна существовать до цикла. Если n не задана, программа остановится с ошибкой ещё до первого шага. 2. Внутри цикла условие должно приближаться к ложному. Строка n //= 10 здесь — не деталь, а сам смысл: без неё n никогда не станет нулём. 3. Проверка происходит перед каждым шагом, а не в середине. Если условие ложно с самого начала, тело не выполнится ни разу. Два слова управления циклом. break немедленно прекращает цикл. Нужен, когда искомое найдено и продолжать бессмысленно: именно так в задании 25 выводят ровно пять чисел. continue пропускает остаток тела и переходит к следующему шагу. Удобно, когда очередной элемент не подходит по условию. Частая ошибка на экзамене — забыть увеличить счётчик внутри while. Программа не падает, а зависает, и это выглядит как ошибка компьютера, хотя дело в одной пропущенной строке.
| n // 10 | целочисленное деление — отбрасывает последнюю цифру: 1234 → 123 |
|---|---|
| n % 10 | остаток — даёт последнюю цифру: 1234 → 4 |
| n % 2 | чётность: 0 у чётных, 1 у нечётных |
| n ** k | возведение в степень: 2 ** 10 равно 1024 |
| abs(n) | модуль числа |
| str(n) и int(s) | перевод числа в строку и обратно — самый короткий путь к работе с цифрами |
| bin(n), oct(n), hex(n) | перевод в двоичную, восьмеричную и шестнадцатеричную запись; результат — строка с приставкой, её убирают срезом [2:] |
| int(s, b) | чтение строки как записи в системе счисления с основанием b: int('1011', 2) равно 11 |
Определение
Проверка простоты «до корня» — если у n есть делитель d ≤ √n, то есть и парный n/d. Достаточно искать делители до int(math.isqrt(n)) — резко ускоряет перебор в задании 25.
Типовой шаблон ЕГЭ — «перебрать диапазон и найти подходящие»: for x in range(1, 1000000): if условие(x): .... Вложенные циклы перебирают пары элементов.
Разбор примера
Перебор с накоплением характеристик
Найти количество делителей числа n (делители — от 1 до n).
Показать решение по шагам
- 1.
n = 36 count = 0 for d in range(1, n + 1): if n % d == 0: # d делит n нацело count = count + 1 print(count) # у 36 девять делителей
Ответ: Программа печатает 9
Два цикла и когда какой брать
Цикл for используют, когда число повторений известно заранее или когда нужно перебрать элементы готовой последовательности.
for i in range(10):
print(i)Функция range задаёт последовательность целых чисел. Три её формы:
range(n) — числа от 0 до n − 1;
range(a, b) — от a до b − 1, то есть правая граница не включается;
range(a, b, step) — с заданным шагом; при отрицательном шаге счёт идёт вниз.
Невключение правой границы — источник ошибки «на единицу», самой частой в программировании вообще. Чтобы перебрать числа от 1 до 100 включительно, пишут range(1, 101).
Цикл while используют, когда число повторений заранее неизвестно, а известно условие продолжения.
while n > 0:
n = n // 10Такой цикл — стандартный способ разобрать число на цифры: пока число не исчерпано, отрезаем последнюю цифру.
Опасность бесконечного цикла. Если внутри while переменная условия не меняется, программа зависнет. Проверяйте: есть ли в теле цикла строка, которая приближает условие к ложному?
break и continue. Оператор break немедленно выходит из цикла — удобен, когда ответ найден и продолжать бессмысленно. Оператор continue переходит к следующей итерации, пропуская остаток тела.
Перебор с накоплением — главная схема заданий 25–27. У неё всегда одна и та же форма: до цикла создаётся накопитель, в цикле он обновляется, после цикла выводится.
total = 0
for x in data:
total = total + xНакопителем может быть сумма, произведение, счётчик, максимум, минимум, список подходящих элементов. Важная деталь: начальное значение накопителя выбирается осмысленно. Для суммы это 0, для произведения 1, для максимума — либо первый элемент, либо заведомо малое число.
Практическое правило выбора: for — когда известно, сколько шагов; while — когда известно, при каком условии остановиться. Перебор диапазона, обход списка, проход по строке — это for. Поиск первого подходящего числа, деление до нуля, повторение до схождения — это while. Попытка выразить второе через for с искусственно большой границей и break работает, но читается хуже и чаще приводит к ошибке в границе.
Вложенные циклы и перебор пар
Когда нужно рассмотреть все пары элементов, применяют вложенные циклы.
for i in range(n):
for j in range(n):
passТакой перебор рассматривает n² пар, включая пары элемента с самим собой и обе перестановки каждой пары. Если пары неупорядоченные и элемент с самим собой не нужен, внутренний цикл начинают позже:
for i in range(n):
for j in range(i + 1, n):
passЧисло рассмотренных пар здесь равно n·(n − 1) / 2.
Когда вложенные циклы допустимы, а когда нет. Всё решает размер данных. При n порядка тысячи n² — это миллион операций, что выполняется мгновенно. При n порядка миллиона n² — это 1012 операций, и программа не закончится никогда. Именно поэтому задание 27 требует одного прохода, а задание 26 с меньшим объёмом данных допускает сортировку и более тяжёлые приёмы.
Приём, заменяющий двойной цикл. Очень часто перебор пар не нужен: достаточно, идя по данным один раз, помнить нужную величину о предыдущих элементах. Например, чтобы найти наибольшую сумму двух элементов, не нужно перебирать пары — достаточно хранить максимум среди уже просмотренных и на каждом шаге пробовать сумму текущего элемента с этим максимумом. Из O(n²) получается O(n).
Этот приём — «идти один раз и помнить нужное» — ключ ко всей второй части, и к нему стоит возвращаться при каждой задаче на большие данные.
Вопрос на проверку
Сколько значений переберёт цикл for i in range(2, 8)?
Ответить и проверить себя — после бесплатной регистрации.
Вопрос на проверку
Что делает строка «n //= 10» в цикле разбора числа по цифрам?
Ответить и проверить себя — после бесплатной регистрации.
Вопрос с развёрнутым ответом
Объясни, почему при проверке простоты числа достаточно перебирать делители только до квадратного корня из n.
Ответить и проверить себя — после бесплатной регистрации.
Разбор примера
Проверка простоты с трассировкой
Написать проверку, простое ли число, и проследить её работу на числах 91 и 97.
Показать решение по шагам
- 1.
import math
def prostoe(n): if n < 2: return False d = 2 while d * d <= n: if n % d == 0: return False d += 1 return Trueprint(prostoe(91), prostoe(97))
- 2. Шаг 1. Почему хватает делителей до корня. Если n = a · b и оба множителя больше корня, их произведение было бы больше n. Значит, у любого составного числа есть делитель, не превосходящий корня, и дальше проверять нечего.
- 3. Шаг 2. Условие
d * d <= nвместоd <= math.isqrt(n)— то же самое, но без вычисления корня на каждом витке. Умножение дешевле, и на переборе в сотни тысяч чисел разница заметна. - 4. Шаг 3. Трассировка для n = 91. Корень из 91 равен примерно 9,5, значит цикл пойдёт до d = 9.
- 5. d = 2: 91 % 2 = 1, не делится. d = 3: 91 % 3 = 1. d = 4: остаток 3. d = 5: остаток 1. d = 6: остаток 1. d = 7: 91 % 7 = 0 → число составное, функция сразу возвращает
False. - 6. Шаг 4. Заметьте: до девятки цикл не дошёл —
returnобрывает его на первом же найденном делителе. Это важно не только для скорости: 91 = 7 · 13, и число 13 больше корня, но искать его не пришлось, потому что парный ему множитель 7 нашёлся раньше. - 7. Шаг 5. Трассировка для n = 97. d = 2: остаток 1. d = 3: остаток 1. d = 4: остаток 1. d = 5: остаток 2. d = 6: остаток 1. d = 7: остаток 6. d = 8: остаток 1. d = 9: остаток 7.
- 8. Шаг 6. Теперь d становится 10, и условие
10 * 10 <= 97ложно — цикл заканчивается. Ни одного делителя не найдено, функция возвращаетTrue: 97 простое. - 9. Шаг 7. Сколько операций. Для 97 понадобилось восемь витков вместо девяноста пяти при переборе до самого n. Для чисел порядка миллиона выигрыш ещё нагляднее: 1000 витков вместо миллиона.
- 10. Шаг 8. Две частые ошибки. Первая — забыть про
n < 2: единица и ноль простыми не считаются, а цикл для них просто не выполнится и функция вернётTrue. Вторая — писатьwhile d <= n // 2: это верно, но вдвое медленнее корня, и на линии 25 разница между ними — это разница между секундой и минутами.
Ответ: 91 составное (7 · 13), 97 простое
Разбор примера
Цикл while, когда число шагов неизвестно: цифровой корень
Складывать цифры числа до тех пор, пока не останется одна цифра. Разобрать на числе 9875.
Показать решение по шагам
- 1.
n = int(input())
while n > 9: s = 0 m = n while m > 0: s += m % 10 m //= 10 print(n, '->', s) n = sprint('цифровой корень:', n)
- 2. Шаг 1. Почему именно
while, а неfor. Число повторений заранее неизвестно: для одних чисел хватит одного шага, для других понадобится три.forтребует знать количество витков наперёд,while— только условие остановки. - 3. Шаг 2. Условие остановки —
n > 9, то есть «пока число не однозначное». Однозначные числа это 0…9, и как только n попадает в этот диапазон, цикл заканчивается. - 4. Шаг 3. Внутри стоит второй цикл, разбирающий число на цифры. Он работает с копией
m, а не с самимn— иначе внешний цикл потерял бы исходное значение до того, как оно понадобится для печати. - 5. Шаг 4. Трассировка. n = 9875. Внутренний цикл: 9875 % 10 = 5 (s = 5), 987 % 10 = 7 (s = 12), 98 % 10 = 8 (s = 20), 9 % 10 = 9 (s = 29). Печатается «9875 -> 29», n становится 29.
- 6. Шаг 5. n = 29, оно больше девяти — идём дальше. Цифры 9 и 2, сумма 11. Печатается «29 -> 11», n становится 11.
- 7. Шаг 6. n = 11, больше девяти. Цифры 1 и 1, сумма 2. Печатается «11 -> 2», n становится 2.
- 8. Шаг 7. n = 2, условие
2 > 9ложно — внешний цикл заканчивается. Ответ: 2. - 9. Шаг 8. Проверка теорией: цифровой корень числа равен остатку от деления на 9, а если остаток нулевой — девятке. 9875 % 9 = 2 ✔ Совпало. Это не совпадение, а следствие того, что 10 при делении на 9 даёт остаток 1, и потому число сравнимо с суммой своих цифр.
- 10. Шаг 9. Главная опасность любого
while— зацикливание. Убедитесь, что переменная, стоящая в условии, внутри цикла обязательно меняется в нужную сторону. Здесьn = s, и сумма цифр числа всегда меньше самого числа (для n > 9), поэтому остановка гарантирована.
Ответ: 2
Разбор примера
Вложенные циклы: перебор пар
В последовательности 4, 11, 7, 2, 9 найти все пары элементов (не обязательно соседних), сумма которых равна 13, и сосчитать их.
Показать решение по шагам
- 1.
a = [4, 11, 7, 2, 9]
kolichestvo = 0 for i in range(len(a)): for j in range(i + 1, len(a)): if a[i] + a[j] == 13: print(a[i], a[j]) kolichestvo += 1 print(kolichestvo) - 2. Шаг 1. Ключевая деталь —
range(i + 1, len(a))во внутреннем цикле. Старт сi + 1решает сразу две задачи: элемент не берётся в пару сам с собой, и пара не считается дважды в разном порядке. - 3. Шаг 2. Сколько будет проверок. При пяти элементах — 4 + 3 + 2 + 1 = 10 пар, то есть n(n−1)/2. Если бы внутренний цикл шёл с нуля, проверок было бы 25, каждая пара встретилась бы дважды, и ответ удвоился бы.
- 4. Шаг 3. Трассировка. i = 0 (элемент 4): пары с 11 (сумма 15), с 7 (11), с 2 (6), с 9 (13 ✔). Найдена пара 4 и 9.
- 5. Шаг 4. i = 1 (элемент 11): пары с 7 (18), с 2 (13 ✔), с 9 (20). Найдена пара 11 и 2.
- 6. Шаг 5. i = 2 (элемент 7): пары с 2 (9), с 9 (16). Ничего.
- 7. Шаг 6. i = 3 (элемент 2): пара с 9 (11). Ничего. i = 4: внутренний цикл пуст — у последнего элемента нет правых соседей.
- 8. Шаг 7. Ответ: две пары — (4, 9) и (11, 2).
- 9. Шаг 8. Сложность такого перебора — O(n²). Для пяти элементов это ничто, для ста тысяч — пять миллиардов операций, и задача перестаёт решаться. Тогда пары ищут через словарь: для каждого элемента проверяют, встречалось ли раньше число 13 − x. Это один проход вместо двух вложенных циклов.
- 10.
Шаг 9. Быстрый вариант выглядит так:
vstrechalis = set() kolichestvo = 0 for x in a: if 13 - x in vstrechalis: kolichestvo += 1 vstrechalis.add(x)Он даёт то же число 2, но за O(n). Обратите внимание на порядок: сначала проверка, потом добавление — иначе элемент составит пару сам с собой, когда 13 − x равно x.
Ответ: 2 пары: (4, 9) и (11, 2)
range: три формы и где на них ошибаются
Почти каждый цикл в решениях ЕГЭ начинается с range, и три его формы стоит знать наизусть.
range(n) — числа от 0 до n − 1, всего n значений. Единица в конце не входит: range(5) это 0, 1, 2, 3, 4.
range(a, b) — от a до b − 1 включительно, всего b − a значений. range(2, 8) это 2, 3, 4, 5, 6, 7 — шесть чисел, а не семь.
range(a, b, s) — от a с шагом s, пока не достигнут b. range(10, 0, -1) идёт по убыванию: 10, 9, …, 1. Ноль в конец не входит, и это ловушка обратных переборов: чтобы дойти до единицы, границу пишут нулём.
Отсюда три места, где ошибаются постоянно.
Правая граница не включается. Чтобы перебрать числа от 1 до 100 включительно, пишут range(1, 101). Забытая единица теряет последнее число, и в задачах вида «наибольшее подходящее» это меняет ответ.
Соседние пары. Индекс i идёт до len(a) - 1, потому что внутри цикла берут a[i + 1]. При range(len(a)) последний виток обратится за конец списка.
Обратный перебор. range(9999, 999, -1) перебирает четырёхзначные числа по убыванию и доходит до 1000 — граница 999 не включается. Написав range(9999, 1000, -1), вы потеряете само число 1000.
И отдельно про то, чего range не умеет: он работает только с целыми. Перебрать значения с шагом 0,1 им нельзя, и это правильно — дробный шаг накапливал бы погрешность. Если такое понадобилось, перебирают целые и делят: for i in range(0, 100): x = i / 10.
Наконец, полезное свойство: range не создаёт список в памяти, а выдаёт числа по одному. Поэтому for i in range(10**9) не съест память — он просто будет долго работать, и это уже вопрос сложности, а не памяти.
break, continue и чем они отличаются
Два оператора управляют ходом цикла, и путать их дорого.
break прерывает цикл целиком: выполнение переходит к строке после цикла. Нужен, когда ответ найден и продолжать незачем: первое подходящее число, первый разрыв цепочки, первое превышение лимита.
continue пропускает остаток текущего витка и переходит к следующему. Нужен, чтобы отсеивать неподходящие элементы, не загоняя всё тело цикла во вложенный if.
Сравните два способа записать один и тот же отбор. Через вложенные условия:
for x in a:
if x % 2 == 0:
if x > 100:
summa += x
и через continue:
for x in a:
if x % 2 != 0:
continue
if x <= 100:
continue
summa += x
Второй вариант длиннее на строку, но у него есть важное достоинство: каждое условие проверяется отдельно и на одном уровне отступа. При отладке легко поставить печать перед любым continue и увидеть, что именно отсеялось. Именно поэтому в решениях линий 3, 17 и 26 удобнее continue, а не лесенка из вложенных if.
Где ошибаются. Первое: ставят break там, где нужен continue, — и цикл обрывается на первом же неподходящем элементе, а ответ считается по началу файла. Второе: ставят break при поиске наибольшего значения — тогда находится первое подходящее, а не лучшее. Правило: break уместен, только когда дальнейший перебор заведомо бесполезен.
И полезное свойство, о котором мало кто знает: у цикла в Python бывает ветка else. Она выполняется, если цикл завершился без break. Это удобно для проверок вида «ни один элемент не подошёл»: тело else срабатывает ровно тогда, когда поиск ничего не нашёл.
И ещё об одном частом недоразумении: break выходит только из своего цикла. Во вложенных циклах он прерывает внутренний, а внешний продолжает работать. Если нужно выйти сразу из обоих, заводят флаг (nashli = True) и проверяют его в условии внешнего цикла, либо выносят поиск в отдельную функцию и выходят из неё через return — второй способ короче и читается лучше.
Задание №16 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.
Задание №25 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.