Чему научитесь
- Писать цикл, когда число повторений заранее неизвестно
- Читать данные до признака конца
- Формулировать условие остановки и доводить до него
- Узнавать вечный цикл по вердикту и чинить его за минуту
Как устроено занятие
Сначала разбор с примерами и короткими проверками понимания — они нужны, чтобы поймать непонятое сразу, а не через три темы.
Дальше 15 задач лестницей: разминка, основа, со звёздочкой. Занятие засчитывается, когда решено 10 — остальные не пропадают и учитываются отдельно.
После занятия — вторая часть, ещё 15 задач на те же приёмы в новых сюжетах.
Сколько это займёт
Примерно час-полтора вместе с задачами. Сроков нет: можно закрыть вкладку и вернуться когда удобно — прогресс сохранится.
Когда неизвестно, сколько раз
Все прошлые циклы начинались одинаково: «повтори раз». Количество повторений было известно заранее — из условия или из входных данных.
А теперь три задачи, где это не так:
- вводятся числа, пока не встретится ноль — сколько их будет, не знает никто;
- число делят пополам, пока оно не станет нулём — сколько раз, зависит от числа;
- складывают 1 + 2 + 3 + …, пока сумма не превысит миллион — количество слагаемых и есть ответ.
Общее у них одно: условие остановки известно, а число шагов — нет. Для таких случаев в Python есть второй цикл — while.
value = 1
while value < 100: # пока условие истинно
value *= 2 # тело повторяется
print(value) # 128
Читается буквально: «пока value меньше ста — удваивать». Условие проверяется перед каждым шагом, в том числе перед самым первым: если оно ложно сразу, тело не выполнится ни разу.
Три части, без которых он не работает
У for всё нужное встроено: переменная создаётся сама и сама меняется. У while за это отвечаете вы, и частей ровно три.
steps = 0 # 1. подготовка — до цикла
n = int(input())
while n > 0: # 2. условие продолжения
n //= 2
steps += 1 # 3. изменение — внутри тела
print(steps)
Пропустите первую часть — переменной не будет вовсе. Пропустите третью — условие никогда не изменится, и цикл не кончится.
for |
while |
|
|---|---|---|
| когда брать | число шагов известно | известно только условие остановки |
| переменная | создаётся сама | заводится вручную |
| движение | само по range |
ваша строка в теле |
| типичная ошибка | сбитая граница | цикл не заканчивается |
Любой for можно переписать через while, но не наоборот. Это не повод писать всё через while: где количество шагов известно, for короче и безопаснее.
Проверка: сколько шагов
Сколько раз выполнится тело цикла?
x = 100
while x > 100:
x -= 1
Чтение до признака конца
Самое частое применение while в олимпиадных задачах — когда количество входных чисел не сообщают, а вместо этого договариваются о признаке конца: «вводятся числа, ввод заканчивается нулём».
Прямолинейная запись выглядит неуклюже — число приходится читать в двух местах:
x = int(input()) # читаем первое
while x != 0:
...
x = int(input()) # и точно так же следующее
Поэтому чаще пишут иначе — с вечным циклом и выходом из середины:
s = 0
while True: # условие всегда истинно
x = int(input())
if x == 0:
break # выход немедленно
s += x
print(s)
break прекращает цикл целиком и переходит к строке после него. Здесь он не роскошь: чтение и проверка стоят на своих местах, ничего не дублируется.
Есть и родственная команда continue — она пропускает остаток тела и переходит к следующему шагу. Пользуйтесь ею умеренно: почти всегда то же самое читается понятнее через if.
Цикл, который не заканчивается
У while есть своя фирменная ошибка, которой не бывает у for: цикл не завершается. Три способа её написать.
Забыть изменение.
n = int(input())
while n > 0:
print(n) # n не меняется — печать навсегда
Менять не в ту сторону. while n > 0: n += 1 — условие только укрепляется.
Менять не то. В теле обновляется другая переменная, а та, что в условии, остаётся прежней. Самый неприятный случай: код выглядит правильным.
Как это выглядит на проверке
Программа не зависает навсегда — судья прерывает её и ставит превышение времени. И вот здесь ученики теряют больше всего времени: вердикт читается как «слишком медленно», начинается оптимизация, а причина в одной пропущенной строке.
Простое правило: увидели превышение времени в задаче на while — сначала проверьте, что переменная из условия вообще меняется в теле, и только потом думайте про скорость.
Проверять удобно так: возьмите самый маленький вход и мысленно пройдите два-три шага, выписывая значения. Если после шага ничего не сдвинулось к выходу — цикл вечный.
Проверка: делим пополам
Сколько раз выполнится тело цикла при n = 20?
steps = 0
while n > 0:
n //= 2
steps += 1
Введите целое число.
Три ошибки этого занятия
Условие не сдвигается. Разобрано выше. Признак — превышение времени.
Признак конца попал в ответ. Ноль означал «конец ввода», а его прибавили к сумме или посчитали как число. На сумме это не видно — ноль ничего не меняет, — зато количество и минимум сразу становятся неверными. Проверка if x == 0: break должна стоять до обработки.
Пустая последовательность. Ноль пришёл первым, чисел не было вовсе. Сумма и количество дадут ноль сами собой, а вот максимум искать не из чего — условие всегда говорит, что делать в этом случае, и это надо прочитать.
Как проверять себя
- самый маленький вход — одно число и сразу признак конца;
- пустой вход — только признак конца, если условие такое допускает;
- два-три шага на бумаге — сдвигается ли переменная из условия.
Третий пункт занимает полминуты и экономит час на неверных попытках.
Практика: пятнадцать задач
Лестница прежняя: пять разминочных, семь основных, три со звёздочкой. Зачёт при десяти решённых.
Задачи двух видов. В одних читается последовательность до признака конца — там while True с break. В других число превращают шаг за шагом: удваивают, делят пополам, накапливают сумму — там условие пишется прямо в заголовке цикла.
В задачах со звёздочкой числа доходят до . Для Python это не проблема: целые в нём неограниченные, переполнения не бывает.
Почему программа не отвечает
Задача: «вводятся числа, признак конца — ноль; найдите их количество».
Ученик написал:
count = 0
x = int(input())
while x != 0:
count += 1
print(count)
Решение получило вердикт «превышено время». Ученик считает, что программа слишком медленная, и хочет её ускорить. Объясните, в чём он не прав, что на самом деле происходит и как это исправить.