§ 8.8. Программирование циклических алгоритмов. Цикл с условием

Школьный курс python
Содержание

Алгоритмическая конструкция цикл с условием

Цикл – это алгоритмическая структура, в которой одни и те же инструкции выполняются периодически в зависимости от условия. В качестве условия (как и в условной инструкции) может быть любое выражение возвращающее числовое значение. Если оно отлично от нуля, то это интерпретируется как true. Если выражение вычисляется как 0 (в том числе, как действительный ноль 0.0), то это интерпретируется как false. Условие, которое вычисляется как true, позволяет выполнить один шаг цикла, который состоит в том, что будут выполнены одна или несколько инструкций, находящихся в теле цикла.

Существуют несколько разновидностей циклов. Все они сводятся к трем основным видам:

  • цикл с предусловием (или типа while),
  • цикл с постусловием и
  • цикл с параметром (или цикл по переменной).
Нужно сказать, что любой из этих типов цикла может быть заменен любым другим, а применяется тот или иной тип в зависимости от решаемой задачи и предпочтений разработчика. Цикл с предусловием иначе называется циклом “типа while“. Название цикла говорит о том, что в начале будет проверяться условие цикла, а затем будет выполняться (или нет) шаг цикла. Шаги цикла будут выполняться до тех пор, пока условие истинно. Из этого следует, что цикл может вообще не выполниться ни разу, если условие изначально ложно.

Инструкция while

В языке C++ цикл с предусловием реализован с помощью инструкции while. Инструкция while имеет следующий синтаксис:

while (Выражение) {
    Тело цикла
}

Этот тип цикла является универсальным, т. к. с помощью него может быть решена любая задача с циклом. Работа цикла должна завершаться по выполнению определенного количества шагов, а для этого необходимо изменять значения объектов в теле цикла так, чтобы условие могло, рано или поздно, принять значение false. В этом и есть основная сложность разработки циклического алгоритма с помощью while. Если алгоритм как следует не продумать, то цикл может уйти в бесконечное выполнение (“зацикливание“) и алгоритм никогда не завершит свою работу. Тем не менее, бесконечный цикл может быть организован намеренно (в программировании такое не редкость), скажем, для анализа вводимых данных. “Бесконечный цикл” можно организовать следующим условием: while (true) {}. Вместо true можно использовать ненулевое значение переменной. Приведем пример.
Задача 1. Составить программу, в которой новое значение переменной присваивается в цикле.

Программа 8.8.1
#include <iostream>
using namespace std;

int main() {
    int a = true;
	while (a) {
		cout << "a = "; cin >> a;
	}
	if (!a)
		cout << "Цикл завершил свою работу";
    return 0;
}

Ввод/вывод

a = 4
a = 10
a = 2
a = 11
a = 1000
a = 0
Цикл завершил свою работу

В этом случае, выход из цикла программируется в теле цикла, например, с помощью инструкций if и break. Выход из цикла в программе 8.8.1 обеспечивается проверкой вводимого значения переменной а. Цикл будет изменять значение этой переменной до тех пор, пока не будет введен 0.
Рассмотрим несколько типичных задач в которых используется цикл типа while.

Счетчики и накопители

Самым простым способом выхода из цикла – это проверка выполненных шагов цикла. Для этих целей используется переменная-счетчик. Эта переменная изменяет свое значение с шагом 1, начиная с 0. Нулевое значение переменной присваивается до входа в цикл, а в теле цикла переменной инкрементируется новое значение. В условии цикла сравнивается текущее значение счетчика с максимально возможным (количеством шагов).
Задача 2. Вывести на экран n-первых натуральных чисел.

Программа 8.8.2
#include <iostream>
using namespace std;

int main() {
    int n, i = 0;
    cout << "n = "; cin >> n;
    while (i < n) {
        i = i + 1; // или i += 1
        cout << i << " "; // добавляем после вывода числа - пробел
    }
    return 0;
}

Вывод

n = 10
1 2 3 4 5 6 7 8 9 10

Блок-схема

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

cout << ++i << " ";

Заметим, что счетчик не всегда нужен. Действительно, если известно общее количество шагов цикла n, то мы можем уменьшать значение n с каждым шагом цикла на единицу до тех пор, пока значение n не станет равным 0 (если, конечно, начальное значение n больше в программе не потребуется). Сформулировав логическое выражение сравнения n с 0, мы можем создать условие выхода из цикла. В добавок, операцию декремента можно поместить непосредственно в условие. Обратите внимание: если в теле цикла всего лишь одна инструкция, то фигурные скобки можно опустить (для цикла while, правда, это бывает крайне редко).
Задача 3. Вывести n раз число k.

Программа 8.8.3
#include <iostream>
using namespace std;

int main() {
    int n, k;
    cout << "n = "; cin >> n;
    cout << "k = "; cin >> k;
    while (n--)
        cout << k << " ";
    return 0;
}

Во многих задачах требуется "накапливать" значение переменной. Накапливать можно как сумму, так и произведение. Накопителю суммы, до входа в цикл, присваивается значение 0, а накопителю произведения - 1, т. е. значения, которые не изменят окончательный результат.
Примером задачи получения в цикле произведения является вычисление факториала числа: n! = 1 · 2 · 3 · ... · n. Факториал - величина, которая имеет очень быстрый рост, поэтому нужно предусмотреть переполнение типа, если n > 20. Будем использовать максимально широкий целый тип (unsigned long long).
Задача 4. Дано целое n. Получить значение n!

Программа 8.8.4
#include <iostream>
using namespace std;

int main() {
    int n, i = 0;
    unsigned long long f = 1;
    cout << "n = "; cin >> n;
    while (i < n) {
        ++i;       // или i += 1
        f = f * i; // или f *= i
    }
    cout << n << "! = " << f << endl;
    return 0;
}
n = 20
20! = 2432902008176640000
Эта программа будет определять правильное значение n! до значения n = 20. Сверх этого значения произойдет переполнение типа. Но как предупредить пользователя, чтобы он не вводил значение > 20? Необходима условная инструкция в теле цикла. Эти задачи мы разберем на следующем уроке.

Аналогично программируется и накопление суммы. При этом, можно использовать изменяемое значение счетчика для каких-либо других целей, например, для нумерации вводимых чисел.
Задача 5. С клавиатуры вводится n целых чисел. Определить среднее арифметическое введенных чисел.

Программа 8.8.5
#include <iostream>
using namespace std;

int main() {
    int n, i = 0;
    double s = 0;
    cout << "n = "; cin >> n;
    while (i < n) {
        ++i; 
        int d;      
		cout << "d" << i << " = "; 
		cin >> d; 
		s += d;     
    }
    cout << "Ср. арифметическое: "
		 << s / i << endl;
    return 0;
}

Ввод/вывод

n = 5
d1 = 4
d2 = 2
d3 = 8
d4 = 7
d5 = 3
Ср. арифметическое: 4.8

В последних двух задачах также можно обойтись и без счетчика. Попробуйте такие варианты задач составить самостоятельно. Ниже приведён пример решение задачи с Черепахой в которой цикл while реализован без счетчика.
Задача 6. Составьте программу рисования n квадратов c общим центром и шагом (расстояние между сторонами квадратов) равным d.

Программа 8.8.6
import turtle as t
##################### Параметры холста ######################
w = t.Screen()
w.reset()
w.title('Квадраты')
w.bgcolor('black')
w.setup(width = 600, height = 600, startx = -10, starty = 10)
#################### Параметры черепахи #####################
t.speed(10)
t.color('red')
t.pensize(2)
###################### Решение задачи #######################
n = int(input('n = ')) # Число квадратов
d = int(input('d = ')) # Шаг
r = d # Длина стороны квадрата
x, y = 0, 0 # Начальная позиция черепахи
while n > 0:  
    x -= 10 # Устанавливаем
    y -= 10 # новые координаты и
    t.up()
    t.goto(x, y) # смещаемся по гипотенузе
    t.down()
    t.fd(r)      # Рисуем квадрат
    t.lt(90)
    t.fd(r)
    t.lt(90)
    t.fd(r)
    t.lt(90)
    t.fd(r)
    t.lt(90)
    r += 2 * d  # Увеличиваем сторону и
    n -= 1      # переходим к рисованию следующего
w.exitonclick() # Выход по клику мыши на холсте
t.mainloop()    # Задержка холста

Вывод

Параметры: n = 20, d = 10

Счетчик с произвольным шагом

В программах выше использовался счетчик с целочисленным шагом равным единице для определения количества шагов цикла. Но, в общем случае, счетчик может иметь произвольный шаг, в том числе дробный. Покажем это на примере.
Задача 7. Составить программу вывода таблицы значений квадратного корня чисел от 0.2 до 10.0 с шагом 0.2 и точностью до 4 знаков после запятой.

Программа 8.8.7
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

int main() {
    double i = 0.;
    cout << "=====================================\n"
		 << "||  d -> sqrt(d) ||   d -> sqrt(d) ||\n"
		 << "=====================================\n";
    while (i < 5.0) {
        i += 0.2;
        cout << fixed
			 << "|| " << setprecision(1) 
			 << setw(3) << i << " -> " 
			 << setprecision(4) << setw(6) << sqrt(i)
			 << " || " << setprecision(1) 
			 << setw(4) << i + 5. << " -> " 
			 << setprecision(4) << setw(6) << sqrt(i + 5) 
			 << " ||\n";    
    }
    cout << "=====================================" << endl;
    return 0;
}
=====================================
||  d -> sqrt(d) ||   d -> sqrt(d) ||
=====================================
|| 0.2 -> 0.4472 ||  5.2 -> 2.2804 ||
|| 0.4 -> 0.6325 ||  5.4 -> 2.3238 ||
|| 0.6 -> 0.7746 ||  5.6 -> 2.3664 ||
|| 0.8 -> 0.8944 ||  5.8 -> 2.4083 ||
|| 1.0 -> 1.0000 ||  6.0 -> 2.4495 ||
|| 1.2 -> 1.0954 ||  6.2 -> 2.4900 ||
|| 1.4 -> 1.1832 ||  6.4 -> 2.5298 ||
|| 1.6 -> 1.2649 ||  6.6 -> 2.5690 ||
|| 1.8 -> 1.3416 ||  6.8 -> 2.6077 ||
|| 2.0 -> 1.4142 ||  7.0 -> 2.6458 ||
|| 2.2 -> 1.4832 ||  7.2 -> 2.6833 ||
|| 2.4 -> 1.5492 ||  7.4 -> 2.7203 ||
|| 2.6 -> 1.6125 ||  7.6 -> 2.7568 ||
|| 2.8 -> 1.6733 ||  7.8 -> 2.7928 ||
|| 3.0 -> 1.7321 ||  8.0 -> 2.8284 ||
|| 3.2 -> 1.7889 ||  8.2 -> 2.8636 ||
|| 3.4 -> 1.8439 ||  8.4 -> 2.8983 ||
|| 3.6 -> 1.8974 ||  8.6 -> 2.9326 ||
|| 3.8 -> 1.9494 ||  8.8 -> 2.9665 ||
|| 4.0 -> 2.0000 ||  9.0 -> 3.0000 ||
|| 4.2 -> 2.0494 ||  9.2 -> 3.0332 ||
|| 4.4 -> 2.0976 ||  9.4 -> 3.0659 ||
|| 4.6 -> 2.1448 ||  9.6 -> 3.0984 ||
|| 4.8 -> 2.1909 ||  9.8 -> 3.1305 ||
|| 5.0 -> 2.2361 || 10.0 -> 3.1623 ||
=====================================

Возможно, программный код выглядит не очень изящно, но это неизбежная плата за табличное оформление вывода. Приведем пример с черепахой.
Задача 8. Составить программу в которой черепаха рисует параболу

Программа 8.8.8
#################### Параметры черепахи #####################
import turtle as t
w = t.Screen()
w.reset()
w.title('Парабола')
w.bgcolor('black')
w.setup(width = 600, height = 600, startx = -10, starty = 10)
t.speed(10)
###################### Решение задачи #######################
x = -25
while x < 25:
    y = x ** 2 - 280  # Вершина в т. (0; -280)
    t.up()
    t.goto(10 * x, y) # Делаем ветви широкими
    t.down()
    t.dot(3, 'green2')
    x += 0.3
w.exitonclick() 
t.mainloop()   

Вывод

Проход циклом по разрядам числа

Обсудим еще один тип задач в которых производится действия с разрядами числа. В этих задачах для работы с отдельными разрядами числа применяются операции целочисленного деления, которые мы изучили ранее. Поскольку количество разрядов в числе заранее неизвестно, то исходное число, на каждом шаге цикла, делится на 10 (с каждым шагом удаляется младший разряд), пока частное не станет равным нулю. Это значение является основанием прекратить деление и выйти из цикла.
Усовершенствуем программу 8.5.4 (задача 2) так, чтобы в итоге обрабатывалось число произвольной разрядности.

Программа 8.8.9
#include <iostream>
using namespace std;

int main() {
    unsigned long long n;
    int s = 0, i = 0, p = 1;
    cout << "n = "; cin >> n;
    while (n > 0) {
        ++i;
        s = s + n % 10; // или s += n % 10
        p = p * (n % 10);
        n = n / 10; // или n /= 10
    }
    cout << "s = "   << s
         << "\np = " << p 
         << endl;
    return 0;
}
n = 123456789
s = 45
p = 362880

Одна из задач такого рода - это перевод числа из 10 системы счисления (далее - с.с.) в с.с. по основанию q.
Задача 9. Дано цело десятичное число n. Перевести это число в с.с. с основанием q (целое число в интервале 2..9)

Программа 8.8.10
#include <iostream>
using namespace std;

int main() {
	cout << "\nМаксимальное целое число для"
         "\nполучения двоичного представления:\n"
         << 1048575
         << endl;
    unsigned long long d = 0, p = 1;
    int q, n;
    cout << "n = "; cin >> n;
    cout << "q = "; cin >> q;
    while (n > 0) {
        d += n % q * p;
        p *= 10;
		n /= q;
		cout << d << endl;
    }
    cout << "d = " << d 
         << endl;        
    return 0;
}

Идея алгоритма основана на способе "деления" описанного здесь. Суть его заключается в следующем. Исходное число делим на основание с.с. и получаем остатки от этого деления в цикле. Первый остаток - это младший разряд q-ичного числа. Последний - старший разряд. Для того, чтобы повысить старшинство разряда, мы умножаем полученный на каждом шаге цикла остаток на значение переменной p, которое, с каждым шагом цикла, возрастает в 10 раз. Само же число получаем в накопителе суммы - d (т. е. фактически мы производим расчеты в 10 с.с.). Встроенные возможности языка по переводу чисел в с.с. по основаниям 8 и 16 описаны нами ранее.

Инструкция do-while

Алгоритмическую структуру "цикл с постусловием" в языке программирования C++ реализует инструкция цикла do while. Она имеет следующий синтаксис:

do {
    Тело цикла
} while (Выражение);


Поскольку условие проверяется в конце цикла, то тело цикла (одна или более инструкций) будет выполнено хотя бы один раз. В этом существенное отличие инструкций while и do while.
Пока логическое выражение в условии имеет значение, отличное от нуля или true выполняется тело цикла (это его отличает от соответствующего цикла в Pascal). Если проверяемое условие ложно изначально, то инструкции, входящие в тело цикла, будут выполнены один раз, затем произойдет выход из цикла и будут выполняться инструкции следующие за циклом.
Эту разновидность цикла удобно использовать, когда есть необходимость производить анализ входных данных. В общем случае, инструкции while и do while взаимозаменяемы. Приведем пример задачи:
Задача 10. Вывести n-первых четных положительных чисел (включая 0).

Программа 8.8.11
#include <iostream>
using namespace std;

int main() {
    int n, i = 0;
    cout << "n = "; cin >> n;
    do {
        cout << i * 2 << " ";
        ++i;
    } while (i < n);
    return 0;
}

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

Приложение

Примеры решения задач
Вопросы
Темы сообщений
Задания А
Задания Б
Задания С
Ссылки
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (2 оценок, среднее: 5,00 из 5)
Загрузка...

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.


Обсуждение закрыто.