EduBrick
классная работа

Цикл while

Программирование на Python: от нуля до олимпиад

0/18решено 0 из 18до зачёта осталось 10

Чему научитесь

  • Писать цикл, когда число повторений заранее неизвестно
  • Читать данные до признака конца
  • Формулировать условие остановки и доводить до него
  • Узнавать вечный цикл по вердикту и чинить его за минуту

Как устроено занятие

Сначала разбор с примерами и короткими проверками понимания — они нужны, чтобы поймать непонятое сразу, а не через три темы.

Дальше 15 задач лестницей: разминка, основа, со звёздочкой. Занятие засчитывается, когда решено 10 — остальные не пропадают и учитываются отдельно.

После занятия — вторая часть, ещё 15 задач на те же приёмы в новых сюжетах.

Сколько это займёт

Примерно час-полтора вместе с задачами. Сроков нет: можно закрыть вкладку и вернуться когда удобно — прогресс сохранится.

теория

Когда неизвестно, сколько раз

Все прошлые циклы начинались одинаково: «повтори nn раз». Количество повторений было известно заранее — из условия или из входных данных.

А теперь три задачи, где это не так:

  • вводятся числа, пока не встретится ноль — сколько их будет, не знает никто;
  • число делят пополам, пока оно не станет нулём — сколько раз, зависит от числа;
  • складывают 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. В других число превращают шаг за шагом: удваивают, делят пополам, накапливают сумму — там условие пишется прямо в заголовке цикла.

В задачах со звёздочкой числа доходят до 101810^{18}. Для Python это не проблема: целые в нём неограниченные, переполнения не бывает.

лестница задач
развёрнутый ответ

Почему программа не отвечает

Задача: «вводятся числа, признак конца — ноль; найдите их количество».

Ученик написал:

count = 0
x = int(input())

while x != 0:
    count += 1

print(count)

Решение получило вердикт «превышено время». Ученик считает, что программа слишком медленная, и хочет её ускорить. Объясните, в чём он не прав, что на самом деле происходит и как это исправить.

Войдите, чтобы ответить.