Лекция 4 1. Оператор цикла с предусловием Цикл с

advertisement
Лекция 4
1. Оператор цикла с предусловием
Цикл с предусловием реализует структурную схему, представленную на рисунке:
Формат оператора цикла с предусловием в С++:
while (<выражение>) <оператор>;
Выражение определяет условие повторения тела цикла, представленного простым или
составным оператором. Тип выражения должен быть арифметическим или приводимым к
нему. Выражение вычисляется перед каждой итерацией цикла.
Особенности цикла с предусловием:
1) В цикле с предусловием сначала вычисляется значение выражения – условия
продолжения цикла. После чего, если условие было истинно (не равно нулю), выполняется
тело цикла.
2) В теле цикла выполняется только один оператор. Если нужно выполнить несколько
операторов, то используется составной оператор.
while (<выражение>)
{
<оператор 1>;
<оператор 2>;
...
<оператор n>;
}
3) Если при первой проверке значение выражения равно 0 (или false), цикл не
выполнится ни разу, и управление передается оператору, следующему за телом цикла.
4) Условие в цикле whilе проверяется на один раз больше, по сравнению с числом
повторений тела цикла.
5) Для избегания бесконечного выполнения в теле цикла должен быть оператор,
влияющий на условие завершения цикла.
Пример 1. Найти сумму цифр произвольного целого числа с использованием цикла с
предусловием.
#include "windows.h"
#include <iostream>
1
using namespace std;
void main()
{
setlocale (LC_ALL, "Russian")
int n,S;
cout<< "Введите целое число: ";
cin>>n;
n=abs(n);
S=0;
while (n)
{
S+=n%10;
// n%10 – «выделяет» последнюю цифру числа
n/=10; ;
// n/10 – «зачеркивает» последнюю цифру
}
cout<< "Сумма цифр числа: "<<S<<"\n" ;
}
Построим трассировочную таблицу для n=123.
Условие
S
0
n
123
123
1230? Да
0+3=3
12
120? Да
3+2=5
1
10? Да
5+1=6
0
Команда
S=0
n=abs(n)
n0
S+=n%10
n/=10
n0
S+=n%10
n/=10
n0
S+=n%10
n/=10
00? Нет
n0
Оператор цикла или один из операторов, составляющих тело цикла, обязательно
должен изменить условие продолжения цикла. Особенность цикла с предусловием – тело
цикла может не выполниться ни разу в случае, когда n=0.
Пример 2. Найти все делители целого положительного числа.
#include <iostream>
using namespace std;
void main()
{
int num;
cin>>num;
int half=num/2;
int div=2;
while (div <= half)
{
if (!(num%div)) cout<<div<<"\n";
div++;
}
}
// половина числа
// кандидат на делитель
Поиск наибольшего общего делителя
Рассмотрим следующую задачу: требуется составить программу определения
наибольшего общего делителя (НОД) двух натуральных чисел. На английском языке
«наибольший общий делитель» пишется «greatest common divisor», и распространённым его
обозначением является gcd.
2
Наибольший общий делитель двух натуральных чисел — это самое большое
натуральное число, на которое они делятся нацело. Например, у чисел 32 и 24 имеются
общие делители: 2, 4, 8. Наибольшим общим делителем является число 8. Это записывается
так: НOД(32, 24) = 8.
Достаточно давно, задолго до появления ЭВМ, был известен алгоритмический способ
решения этой задачи. Называется он алгоритмом Евклида.
Идея этого алгоритма основана на следующей формуле:
1) Если числа равны, то взять любое из них в качестве ответа, в противном случае
продолжить выполнение алгоритма.
2) Заменить большее число разностью большего и меньшего из чисел;
3) Вернуться к выполнению п.1.
Рассмотрим этот алгоритм на примере М=32, N=24:
8
8
8
32
M
8
16
24
24
N
Получили: НОД(32, 24) = НОД(8, 24) = НОД(8, 16) = НОД(8, 8) = 8, что верно.
Программа нахождения НОД с использованием вычитания:
#include <iostream>
using namespace std;
void main()
{
int N,M;
cin>>N>>M;
/* НОД(M,N)=НОД(M-N,N), если M>N; НОД(M,N)=НОД(N,N-M), если M<N; НОД(M,M)=M */
while (M!=N)
if (M>N) M-=N;
else N-=M;
cout<< M<<"\n" ;
}
Модифицированные алгоритмы Евклида
Программу вычисления НОД можно составить иначе, если воспользоваться
операцией mod – получением остатка от деления.
Вариант 1
Идея алгоритма исходит из справедливости следующих равенств:
Рассмотрим этот алгоритм на примере М=32, N=24:
8
24
32
M
0
8
24
N
Получили: НОД(32, 24) = НОД(24, 8) = НОД(8, 0) = 8.
Программа нахождения НОД с использованием операции остатка от деления:
#include <iostream>
using namespace std;
void main()
{
int N,M,R;
cin>>N>>M;
/* НОД(M,N)=НОД(N,M mod N), НОД(M,0)=M */
3
while (N!=0)
{
R=M % N;
M=N;
N=R;
}
cout<<M<<"\n" ;
}
Вариант 2
Напишем еще один модифицированный вариант алгоритма Евклида, использующий
соотношения
Трассировка этого алгоритма на примере М=32, N=24:
8
8
32
M
0
24
24
N
Получили: НОД(32, 24) = НОД(8, 24) = НОД(8, 0) = 8.
Вариант программы:
#include <iostream>
using namespace std;
void main()
{
int N,M;
cin>>N>>M;
/* НОД(M,N)=НОД(M mod N,N), если MN; НОД(M,N)=НОД(M,N mod M), если M<N;
НОД(M,0)=НОД(0,N)=M+N */
while (N!=0 && M!=0)
if (M>N) M %=N;
else N%=M;
cout<< M+N<<"\n" ;
}
2. Оператор цикла с постусловием
Цикл с постусловием реализует структурную схему, изображенную на рисунке:
Формат оператора цикла с постусловием в С++:
do <тело цикла> while (<выражение>);
4
Или, если тело цикла состоит более чем из одного оператора, используется
конструкция:
do
{
<Оператор 1>;
<Оператор 2>;
<Оператор N>;
}
while (<выражение >);
Особенности цикла с постусловием:
1) Сначала выполняется простой или составной оператор, составляющий тело цикла, а
затем вычисляется выражение – условие продолжения цикла. Тип выражения должен быть
арифметическим или приводимым к нему.
2) Если условие истинно (или значение выражения не равно 0), то тело цикла
выполняется еще раз.
3) Цикл завершается, когда условие продолжения цикла будет равно false (или равно
0).
4) Условие в цикле do ... whilе проверяется столько же раз больше, сколько и
тела цикла.
5) Тело цикла выполнится минимум один раз.
6) Для избегания бесконечного выполнения в теле цикла должен быть оператор,
влияющий на условие завершения цикла.
Пример 3. Найти сумму цифр произвольного целого числа с использованием цикла с
предусловием.
#include <iostream>
using namespace std;
void main()
{
int n,S;
cin>>n;
n=abs(n);
S=0;
do
{
S+=n%10;
n/=10; ;
}
while (n);
cout<< S<<"\n" ;
}
// n%10 – «выделяет» последнюю цифру числа
// n/10 – «зачеркивает» последнюю цифру
Определение простоты числа
Простые числа – это натуральные числа, большие единицы, которые имеют только
два делителя: единицу и само это число. Примеры простых чисел: 2 , 3, 5, 7, 11, 13 …
Единица имеет только один делитель – само себя и поэтому она не может относиться ни к
простым, ни к составным числам.
Во все времена люди хотели найти как можно большее простое число. Пока люди
считали только при помощи карандаша и бумаги, им нечасто удавалось обнаружить новые
простые числа. До 1952 г. самое большое известное простое число состояло из 39 цифр.
Теперь поиском все больших простых чисел занимаются компьютеры.
5
Пример 4. Определить, является ли натуральное число N простым с использованием
цикла с постусловием.
Самый простой путь решения этой задачи – проверить, имеет ли данное число N
(N>=2) делители в интервале [2;
]. Здесь используется свойство: наименьшее число, на
которое делится натуральное число N, не превышает целой части квадратного корня из числа
N. Если делители есть, число N – составное, если – нет, то – простое. При реализации
алгоритма разумно делать проверку на четность введенного числа, поскольку все четные
числа делятся на 2 и являются составными числами, то, очевидно, что нет необходимости
искать делители для этих чисел. Из четных чисел только число 2 является простым.
#include <iostream>
#include “windows.h”
using namespace std;
void main()
{
setlocale (LC_ALL, "Russian")
int N,i;
cout<< "Введите натуральное число: ";
cin>>N;
if (N==2)cout<< "Число 2 - простое\n";
else
if (N%2 ==0) cout<< "Число " << N << " - составное\n";
else
{
i=1;
do
i+=1;
while (i * i < N && N % i != 0);
if (i * i < N) cout<< "Число "<<N<<" - составное \n";
else cout<< "Число "<<N<<" - простое\n";
}
}
Разложение числа на простые сомножители
Пример 5. Составить программу, печатающую разложение на простые множители
заданного натурального числа N > 0 (другими словами, требуется печатать только
простые числа и произведение напечатанных чисел должно быть равно N; если N = 1,
печатать ничего не надо).
Алгоритм разложения натурального числа на простые множители: берем первое
простое число - 2 и пробуем делить данное натуральное число на 2, если число делится на 2,
тогда надо 2 записать, а число разделить на 2 и снова полученный результат пробуем делить
на два, если делится, тогда повторяем процесс деления, если не делится, тогда надо
пробовать делить на следующее простое число - 3 и так далее.
Например, число 360 можно разложить на следующие простые множители:
360=222335.
Программа (вариант 1):
#include <iostream>
using namespace std;
void main()
{
int N,k,i;
cin>>N;
k=N;
/* инвариант: произведение напечатанных чисел и k равно N,
напечатаны только простые числа*/
while (k != 1)
{
6
i = 2;
// начинаем с первого простого числа - 2
/* инвариант: k не имеет делителей в интервале (1,i) */
while (k % i != 0)
i += 1;
/* инвариант: i - наименьший делитель k, больший 1, следовательно, простой */
cout<< i<<" " ;
k /=i ;
// делим число k на i
}
}
Предложенный варианты программ не является эффективными: он заставляет
компьютер выполнять много лишних проверок на деление.
Более совершенный алгоритм:
Сначала находим все делители, равные 2. Для этого последовательно делим число 360
на 2.
После этого, полученный результат делим на нечетные числа, т.к. все остальные
четные числа не являются простыми. Причем на каждое нечетное число делим до тех пор,
пока такое деление будет возможным.
Делим на 3: 45:3 = 15, 15:3 = 5.
Делим на 5: 5:5 = 1.
Делим на 7, не делится, пробуем делить на следующее нечетное число.
Делим на 9, не делится, переходим к следующему нечетному числу.
Делим на 11, не делится, и так далее.
Также можно получить более эффективный алгоритм, если использовать тот факт, что
составное число имеет делитель, не превосходящий квадратного корня из этого числа.
Для этого вместо i+=1; в варианте 2 программы можно написать следующий код:
if (i*i>N) i=N;
else i+=1;
3. Взаимозаменяемость трех видов циклов
Алгоритм, написанный с использованием цикла for, может быть легко переписан с
использованием цикла while или с использованием цикла do … while. Обратное не всегда
справедливо, т.к. специфика циклов while и позволяет решать более широкий круг задач,
чем цикл for. Задача, решенная с использованием цикла while, может быть легко решена с
использованием цикла do … while и наоборот.
При преобразовании цикла с параметром в цикл с предусловием важно учесть, что
параметр цикла нужно будет инициализировать и изменять самим, цикл while этого делать
не будет. Общая схема преобразования цикла for в цикл while:
i:=<нач.знач.>;
while (i<=<кон.знач.>)
{
<тело цикла>

for (i=<нач.знач.>; i<=<кон.знач.>;i+=d)
<тело цикла>
i +=d;
};
i:=<нач.знач.>;
while (i>=<кон.знач.>)
{
<тело цикла>

for (i=<нач.знач.>; i<=<кон.знач.>;i-=d)
<тело цикла>
i -=d;
};
7
Для того чтобы преобразовать цикл while в цикл do … while нужно учесть, что тело
цикла обязательно выполнится 1 раз, в то время как тело цикла while может не выполниться
ни разу.
Общая схема преобразование цикла while в цикл do … while:
Случай, когда цикл
while выполняется 0
раз невозможен
Do
{

<тело цикла>;
}
while (<выражение>)
{
<тело цикла>;
}
while (<выражение>);
Возможен случай, когда
цикл while выполняется
0 раз
If (<выражение>)
Do
{
<тело цикла>

}
while (<выражение>)
{
<тело цикла>;
}
while (<выражение>);
8
Related documents
Download