Лекция 4 Циклы с предусловием и постусловием. Типовые задачи на использование циклов с предусловием и постусловием Цикл с предусловием Цикл с предусловием обычно используется в том случае, когда количество повторений цикла заранее неизвестно. Как и в случае цикла с параметром, цикл с предусловием также выполняется до тех пор, пока условие истинно. Формат оператора цикла с предусловием: while (<уcловие>) { <тело цикла>; } Если тело цикла содержит один оператор, то операторные скобки можно не ставить. Обычно перед началом цикла выполняются некоторые начальные установки переменных, которые затем будут использоваться при проверке условия. Пример. С клавиатуры вводятся целые числа до тех пор, пока не встретится 0. Суммировать все вводимые положительные четные числа и вывести результат на экран. #include <iostream> using namespace std; void main() { int S, x; S = 0; cin >> x; while (x!=0) { if (x > 0 && x % 2 == 0) S = S + x; cin >> x; } cout << S; } Особенности выполнения цикла с предусловием аналогичны особенностям цикла с параметром. Цикл с постусловием Цикл с постусловием отличается от цикла с предусловием тем, что сначала выполняется тело цикла, а затем происходит проверка условия. Формат оператора цикла с постусловием: do { <тело цикла>; } while (<уcловие>); Если тело цикла содержит один оператор, то операторные скобки можно не ставить. Пример. Вводить с клавиатуры числовой пароль до тех пор, пока он не будет верно введен, либо пока не будет исчерпано 5 попыток. #include <iostream> #define PAROL 12345 using namespace std; void main() { //использование русских букв для вывода (второй вариант) setlocale(LC_ALL, "Russian"); int x, i = 1; do { cout << "Введите пароль"; cin >> x; i++; } while (x != PAROL && i <= 5); if (i <= 5) cout << "Вход в систему разрешен"; else cout << "Доступ запрещен! Пароль неверен"; } В данном примере пароль задан в виде константы. В цикле сначала осуществляется ввод пароля, а затем происходит проверка введенного пароля и проверяется количество выполненных попыток. После выхода из цикла проверяется, по какому из условий осуществлен выход из цикла. Если количество попыток было меньше либо равно 5, то, значит, пароль был введен верно и доступ к системе разрешен, иначе выдается сообщение, что пароль неверен и доступ запрещен. Особенности выполнения цикла с постусловием: 1) Количество проверок условия цикла равно количеству итераций цикла. 2) Цикл с постусловием обязательно выполняется хотя бы один раз. 3) Может произойти зацикливание, если условие никогда не станет ложным Нахождение наибольшего общего делителя. Алгоритм Евклида Рассмотрим следующую задачу: требуется составить программу определения наибольшего общего делителя (НОД) двух натуральных чисел. На английском языке «наибольший общий делитель» пишется «greatest common divisor», и распространённым его обозначением является gcd. Наибольший общий делитель двух натуральных чисел — это самое большое натуральное число, на которое они делятся нацело. Например, у чисел 32 и 24 имеются общие делители: 2, 4, 8. Наибольшим общим делителем является число 8. Это записывается так: НOД(32, 24) = 8. Алгоритм решения этой задачи был известен еще с древних времен и назван в честь его автора алгоритмом Евклида. Идея этого алгоритма основана на следующей формуле: 1) Если числа равны, то взять любое из них в качестве ответа, в противном случае продолжить выполнение алгоритма. 2) Заменить большее число разностью большего и меньшего из чисел; 3) Вернуться к выполнению п.1. Рассмотрим этот алгоритм на примере М=32, N=24: M N 32 24 8 24 8 16 8 8 Получили: НОД(32, 24) = НОД(8, 24) = НОД(8, 16) = НОД(8, 8) = 8, что верно. Программа для вычисления НОД: #include <iostream> using namespace std; void main() { cin >> N >> M; while (M != N) if (M > N) M -= N; else N -= M; cout << N; } Существуют еще два варианта решения этой задачи, которые получили название модифицированные алгоритмы Евклида. В этих вариантах используется остаток от деления вместо вычитания, поэтому они более эффективны. Вариант 1 Вариант 2 Определение простоты числа Рассмотрим задачу определения простоты числа. Натуральное число N (N>1) является простым, если оно делится только само на себя и на 1. Самый простой путь решения этой задачи – проверить, имеет ли данное число N (N>1) делители в интервале √ . Здесь используется известное свойство: наименьшее число, на которое делится натуральное число N, не превышает целой части квадратного корня из числа N. Если делители есть, число N – составное, если – нет, то – простое. При реализации алгоритма можно делать проверку на четность введенного числа, поскольку все четные числа делятся на 2 и являются составными числами. Если число четное, то нет необходимости искать делители для этих чисел. Из четных чисел только число 2 является простым. Программа для решения задачи: #include <iostream> using namespace std; void main() { int N, i = 2; cin >> N; if (N == 2) cout << "Простое"; else { if (N % 2 == 0) cout << "Составное"; else { do i++; while (i*i <= N && N % i != 0); if (i * i < = N) cout << "Составное"; else cout << "Простое"; } } } Если число равно 2, то сразу возвращаем значение «Простое» и выходим из функции. Далее идет проверка на четность числа. Если число четное, то сразу возвращаем значение «Составное». Если число нечетное, то заходим в цикл, в котором будет перебираться все числа по порядку, начиная с 3, и который выполняется до тех пор, пока либо не будет найден делитель числа, либо квадрат счетчика цикла не станет больше N. После цикла осуществляется проверка, по какому именно из двух условий был произведен выход из цикла. Если найден делитель, то число составное, иначе - простое. Перевод числа из десятичной системы в систему счисления с основанием N Рассмотрим задачу перевода числа x из десятичной системы в систему счисления с основанием N. Как известно, данный перевод выполняется делением «столбиком» на основание N, причем каждая очередная цифра результата является остатком от деления (кроме первой цифры, которая является окончательным результатом деления). Деление производится до тех пор, пока результат не станет меньше чем N. Цифры получаются справа налево. Чтобы получить корректный результат, необходимо каждую новую цифру умножать на соответствующий разряд (степень числа 10). Напишем фрагмент программы, которая выполняет перевод. Все цифры результата, кроме последней, вычисляются в цикле. Последняя цифра добавляется к результату вне цикла. cin >> x >> N; int step = 1, s = 0;//step – степень числа 10, s - результат while (x >= N) { s = x % N * step + s; step *= 10; x = x / N; } s = x * step + s; Построим трассировочную таблицу для x=415, N=8: итерация До цикла 1 2 После цикла s 0 7 3*10+7=37 6*100+37=637 step 1 10 100 x 415 51 6 Разложение числа на простые сомножители Рассмотрим программу, печатающую разложение на простые множители заданного натурального числа N > 0. Другими словами, требуется печатать только простые числа и произведение напечатанных чисел должно быть равно N. Если N = 1, печатать ничего не надо. Алгоритм разложения натурального числа на простые множители. Берем первое простое число 2 и пробуем делить данное натуральное число на 2. Если число делится на 2, тогда надо 2 вывести, а число разделить на 2. Полученный результат снова пробуем делить на два. Если делится, тогда повторяем процесс деления, а если не делится, тогда надо пробовать делить на следующее простое число 3 и так далее. Например, число 360 можно разложить на следующие простые множители: 360=2*2*2*3*3*5. #include <iostream> using namespace std; void main() { int N,k,i; cin >> N; k = N; while (k != 1) { i = 2; while (k % i != 0) i += 1; cout<< i<<" " ; k /=i ; } } Обращаем внимание, что проверку на простоту числа выполнять не нужно, так как если, например, число уже не делится на 2, то оно в дальнейшем не будет делиться и на 4, 6, 8 и т.д., то есть все составные числа будут пропускаться автоматически. Предложенный вариант программы не является эффективным: он заставляет компьютер выполнять много лишних проверок на деление. Можно получить более эффективный алгоритм, если использовать тот факт, что составное число имеет делитель, не превосходящий квадратного корня из этого числа. Для этого вместо i+=1; можно написать следующий код: if (i*i>N) i=N; else i+=1; Если число простое, то в качестве делителя будет выводиться само это число (i=N). Операторы break и continue Достаточно часто при использовании циклов и почти всегда при использовании оператора switch программисту необходимо прервать выполнение тела цикла или тела оператора switch соответственно. Оператор break, как раз используется в таких случаях. Также с помощью этого оператора прерывается бесконечный цикл. Когда его нужно прервать, в тело цикла добавляется условие, при выполнении которого сработает оператор break, а цикл завершит работу. Пример. Запрашивать у пользователя и суммировать числа до тех пор, пока либо не будет введено 5 чисел, либо не будет введен 0 . #include <iostream> #include <iomanip> using namespace std; void main() { int x,s=0; for (int i = 1; i <=5; i++) { cin>>x; if (x == 0) // если число 0 { break; // прервать цикл } s+=x; } cout << s << endl; } Оператор continue применяется тогда, когда необходимо досрочно прервать (пропустить) текущую итерацию цикла и приступить к следующей итерации. При его выполнении в цикле for происходит остановка текущей итерации, переход к изменению управляющей переменной, а затем проверка условия продолжения выполнения цикла. Пример. Необходимо вывести на экран числа из диапазона от 10 до 99, у которых в составе есть цифра 7 или 3. #include <iostream> using namespace std; int main() { int a,b; for (int i = 1; i <=100; i++) { a=i%10; b=i/10; if (a!=7 && b!=3 && a!=3 && b!=7) continue; cout << i << endl; } return 0; } Вычисление суммы ряда с заданной точностью Иногда при решении задач на вычисление суммы ряда требуется производить вычисления до тех пор, пока не будет достигнута требуемая точность. Вычисление суммы ряда с определенной точностью ε означает, что сумма ряда вычисляется до тех пор, пока очередной член последовательности по модулю больше ε. Пример. Вычислить сумму ряда точностью ε=0.001 #include <iostream> #include <cmath> using namespace std; void main() { S 1 1 1 1 ... ... 2 3 n с int i=1; float s=0,eps=0.001,a=1; while (abs(a)>eps) { s=s+a; i++; a=1/float(i); } cout <<s; }