Циклы: For, While и Do-While - Язык программирования C++ для спортивного программирования
Неделя 3Циклы, Счётчики и Накопители, Обработка цифр
Циклы: For, While и Do-While
Повторение кода шаг за шагом: цикл for по частям и как он выполняется, переменная цикла, счёт вниз, шаг больше единицы, подсчёт итераций без ошибки на единицу, цикл while, break, continue, do-while и циклы, которые не заканчиваются.
Теория
Урок 1 из 6
14 мин чтения
Вопросов: 5
Напечатать числа от 1 до 5 можно пятью строками с cout. От 1 до 1000? Тысячу
строк никто не пишет. А от 1 до n, где n читается из ввода, выписать вообще
невозможно: программа не знает n, пока не запустится.
Цикл повторяет кусок кода: по разу для каждого числа, для каждой строки
ввода или до тех пор, пока что-то остаётся истинным. В этом уроке три цикла C++
собираются шаг за шагом: сначала for, потом while, потом do-while.
Первый цикл
Вот цикл, который печатает числа от 1 до 5:
Он печатает 1 2 3 4 5. Прочитаем его по частям:
Часть
Что она говорит
for
«повторяй код в фигурных скобках»
int i = 1
начало: создай переменную i и положи в неё 1
i <= 5
проверка: продолжай, пока это истинно
i++
шаг: после каждого прохода увеличь i на единицу
{ cout << i << " "; }
тело: код, который повторяется
Три части в скобках разделяются точкой с запятой, а не запятыми.
Переменная i — это переменная цикла, или счётчик: она считает проходы.
Один проход по телу называется итерацией.
Как выполняется цикл for
дальше
Практика: Циклы
Диапазоны, шаги, кратные, квадраты, степени двойки и первый делитель — одним циклом.
Четыре части выполняются не в том порядке, в котором записаны. Начало
выполняется только один раз; дальше цикл ходит по одному и тому же кругу, пока
проверка не провалится:
Значит, с i <= 5 тело выполняется для i = 1, 2, 3, 4 и 5. Потом i становится
6, проверка 6 <= 5 ложна, и программа переходит к строке после цикла.
Пройдите по шагам цикл, который читает n и считает до него, здесь — с n = 3.
Следите, как подсвеченная строка после каждого прохода возвращается к строке for:
Цикл for шаг за шагом
C++
1#include <bits/stdc++.h>
2using namespace std;
3
4int main() {
5 ios_base::sync_with_stdio(false);
6 cin.tie(NULL);
7
8 int n;
9 cin >> n;
10 for (int i = 1; i <= n; i++) {
11 cout << i << " ";
12 }
13 cout << "\n";
14 return 0;
15}
Читаем n
строка 9
n равно 3: цикл должен напечатать 1 2 3.
ввод
3
n3i—
нажми пуск
1 / 13
Трасса показывает две вещи:
Проверка идёт перед каждым проходом, и перед первым тоже. Будь n равно 0,
проверка 1 <= 0 сразу провалилась бы, и тело выполнилось бы ноль раз.
Проверка выполняется на один раз больше, чем тело. Для n = 3 она
выполнилась четыре раза: три раза истинной и в последний раз ложной.
Используем переменную цикла
Тело может использовать i как угодно — печатать или вычислять с ним:
Прочитайте n и выведите его таблицу умножения: десять строк, от n x 1 = …
до n x 10 = ….
Ввод
Вывод
Здесь i — множитель: он идёт от 1 до 10, и каждый проход печатает одну строку.
Переменная, созданная в строке for, как int i, существует только внутри
своего цикла. После закрывающей скобки никакого i уже нет, так что следующий
цикл может создать свой.
Счёт вниз
Чтобы считать вниз, разверните все три части: начните с большого числа, шагайте
через i-- и проверяйте, что i ещё достаточно большое:
Он печатает 5 4 3 2 1. Проверка тоже должна смотреть в другую сторону.
Напишите там по ошибке i <= 1 — и 5 <= 1 сразу ложно: цикл выполнится ноль
раз и ничего не напечатает.
Шаг больше единицы
Шаг может сдвигать i на любую величину — лишь бы к концу:
Цикл
Значения i
for (int i = 1; i <= 9; i += 2)
1 3 5 7 9
for (int i = 20; i > 0; i -= 5)
20 15 10 5
for (int i = 1; i <= 100; i *= 2)
1 2 4 8 16 32 64
i += 2 перепрыгивает через чётные числа, поэтому первый цикл печатает нечётные.
В последнем i удваивается: 64 · 2 = 128 проверку не проходит.
Сколько раз он выполнится?
Большинство ошибок в циклах — на единицу: цикл выполняется на один раз
больше или меньше нужного. Считайте до запуска:
Цикл
Итераций
for (int i = 1; i <= n; i++)
n
for (int i = 0; i < n; i++)
n — столько же, но начиная с 0
for (int i = 1; i < n; i++)
n - 1
for (int i = a; i <= b; i++)
b - a + 1
Обычно виноваты < и <=. Когда цикл должен выполниться n раз, выберите
одну из первых двух форм и держитесь её. Когда он проходит диапазон от a до
b, проверьте вручную самый маленький случай: от 3 до 3 — это одно число, и
b - a + 1 даёт 1.
Цикл while
for — это короткая запись более простого цикла, while. Вот тот же цикл от 1
до 5, записанный через while:
У while в скобках только проверка. Он проверяет; если проверка истинна,
выполняет тело и возвращается проверить снова; как только она ложна, цикл
заканчивается. Начало стоит перед циклом, а шаг — внутри тела: забудьте шаг, и
i навсегда останется равным 1.
Зачем тогда оба? for — когда вы считаете: от 1 до n, n раз. while —
когда цикл крутится, пока что-то не случится, и заранее никто не скажет,
сколько на это уйдёт проходов:
Прочитайте n (n ≥ 2) и выведите его наибольший делитель, меньший n.
Ввод
Вывод
Если идти вниз от n - 1, первый делитель, который встретит цикл, и будет
наибольшим, поэтому цикл тут же останавливается. Он всегда остановится: 1 делит
любое число. Для 91 он пробует 90, 89, …, 14 и останавливается на 13, потому что
91 = 7 · 13.
Теперь циклы собираете вы. Задайте начало, проверку и шаг так, чтобы цикл
напечатал ровно то, что нужно, — а в последнем раунде запишите его через while:
Заставьте цикл напечатать этопопробуй!
Остановка посередине: break
break сразу заканчивает цикл, где бы ни стоял в теле, и программа идёт дальше
после цикла. Он полезен, когда остановиться нужно посреди тела, а не на проверке:
Улитка сидит на дне колодца глубиной h метров. Каждый день она поднимается на
u метров, а каждую ночь сползает на d метров (u > d). В какой день она
выберется?
Ввод
Вывод
У while (true) проверка всегда истинна, поэтому сам по себе он крутился бы
вечно; выход из него — break. Он стоит между подъёмом и сползанием: когда
улитка выбралась, ночного сползания уже нет. Для h = 10, u = 3, d = 1 за
первые четыре дня улитка добирается до 3, 5, 7 и 9 метров и каждую ночь сползает
обратно; на пятый день она поднимается до 11 и выбирается.
Пропустить проход: continue
continue пропускает остаток тела и сразу переходит к следующей итерации — в
for шаг при этом всё равно выполняется:
Этот код печатает числа от 1 до 20 без 5, 10, 15 и 20. То же сделал бы if
вокруг cout; continue удобен, когда тело длинное, а решение пропустить
принимается в его начале.
Цикл do-while
do-while — это while наоборот: он сначала выполняет тело и только потом
проверяет, поэтому тело выполняется хотя бы один раз.
Этот код печатает цифры n справа налево. Для n = 0 он печатает 0, а тот же
цикл через while (n > 0) { … } не напечатал бы ничего: его проверка проваливается
ещё до первого прохода. Не забудьте точку с запятой после while (…) —
do-while единственный цикл, который ею заканчивается.
Циклы, которые не заканчиваются
Цикл, проверка которого никогда не становится ложной, крутится вечно —
проверяющая система останавливает его и сообщает Time Limit Exceeded. Обычно
это происходит так:
Код
Почему он не заканчивается
while (i <= n) { cout << i; }
ничто не меняет i
for (int i = 10; i >= 1; i++)
шаг идёт не в ту сторону
for (int i = 1; i != 10; i += 2)
i перепрыгивает 10: 1, 3, …, 9, 11, …
В проверке предпочитайте < и <=, а не !=: они остановят цикл, даже если
счётчик перескочит границу.
Проверьте себя
Как выполняется цикл for
Начало — int i = 1 выполняется один раз, раньше всего остального.
Проверка — истинно ли i <= 5? Если ложно, цикл окончен.
Тело — выполняется код в фигурных скобках.
Шаг — выполняется i++, и цикл возвращается к проверке.
А вы знали?
31 декабря 2008 года тысячи музыкальных плееров Microsoft Zune зависли
одновременно. Код, который превращал «дни с 1980 года» в год, содержал цикл
while, вычитавший 366 дней в високосном году, — но только если дней оставалось
больше 366. В 366-й день високосного 2008 года их оставалось ровно 366: ничего не
вычиталось, и цикл крутился вечно. На следующий день, когда дата сменилась,
плееры снова заработали.
for(int i = 1; i <= 5; i++){ cout << i << " ";}
проверь себя1 балл
Сколько раз выполнится тело цикла for (int i = 3; i <= 10; i++)?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
При n = 0 тело цикла for (int i = 1; i <= n; i++) выполнится один раз.
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Чему равно s после этого цикла?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Чему равно c в конце?
Войдите, чтобы ответить и сохранить прогресс.Войти
проверь себя1 балл
Что напечатает этот код?
Выберите один ответ
Войдите, чтобы ответить и сохранить прогресс.Войти
7
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70
91
13
10 3 1
5
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int n; cin >> n; for(int i = 1; i <= 10; i++){ // i принимает значения 1, 2, ..., 10 cout << n << " x "<< i << " = "<< n * i << "\n";} return 0;}
for(int i = 5; i >= 1; i--){ cout << i << " ";}
int i = 1; // начало — перед цикломwhile(i <= 5){ // проверка cout << i << " "; i++; // шаг — в конце тела}
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int n; cin >> n; int d = n - 1; while(n % d != 0){ // первый делитель, найденный сверху вниз, — наибольший d--;} cout << d << "\n"; return 0;}
#include <bits/stdc++.h>using namespace std;int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); int h, u, d; cin >> h >> u >> d; int height = 0, day = 0; while(true){ day++; height += u; // днём поднимается if(height >= h){ break; // выбралась из колодца: ночью уже не сползёт} height -= d; // ночью сползает вниз} cout << day << "\n"; return 0;}
for(int i = 1; i <= 20; i++){ if(i % 5 == 0){ continue; // кратные 5 пропускаются} cout << i << " ";}
do{ cout << n % 10; n /= 10;} while(n > 0);
int s = 0;for(int i = 1; i <= 10; i++){ if(i == 5){ break;} s += i;}
int n = 0, c = 0;do{ c++; n /= 10;} while(n > 0);
for(int i = 20; i > 0; i -= 6){ cout << i << " ";}