В практике программирования часто складываются ситуации, когда одну и ту же группу операторов, реализующих определённую цель, требуется повторить без изменений в нескольких местах программы. Для избавления от столь нерациональной траты времени была предложена концепция подпрограммы.
Подпрограмма — именованная, логически законченная группа операторов языка, которую можно вызвать для выполнения любое количество раз из различных мест программы. В языке C++ подпрограммы реализованы в виде функций [4 ]
Функция — это поименованный набор описаний и операторов, выполняющих определённую задачу. Функция может принимать параметры и возвращать значение. Информация, передаваемая в функцию для обработки, называется параметром, а результат вычисления функции её значением. Обращение к функции называют вызовом. Как известно (п. 2.8), любая программа на C++ состоит из одной или нескольких функций. При запуске программы первой выполняется функция main. Если среди операторов функции main встречается вызов функции, то управление передаётся операторам функции. Когда все операторы функции будут выполнены, управление возвращается оператору, следующему за вызовом функции.
Перед вызовом функция должна быть обязательно описана. Описание функции состоит из заголовка и тела функции:
тип имя_функции(список_переменных)
{
тело_функции
}
Заголовок функции содержит:
void;имя_функции;список_переменных — перечень передаваемых в функцию величин (аргументов), которые отделяются друг от друга запятыми; для каждой переменной из списка указывается тип и имя; если функция не имеет аргументов, то в скобках указывают либо тип void, либо ничего.Тело функции представляет собой последовательность описаний и операторов, заключённых в фигурные скобки.
В общем виде структура программы на C++ может иметь вид:
директивы компилятора
тип имя_1(список_переменных)
{
тело_функции_1;
}
тип имя_2(список_переменных)
{
тело_функции_2;
}
...
тип имя_n(список_переменных)
{
тело_функции_n;
}
int main (список_переменных)
{
//Тело функции может содержать операторы вызова функций имя_1, имя_2, ..., имя_n
тело_основной_функции;
}
Однако допустима и другая форма записи программного кода :
директивы компилятора
тип имя_1(список_переменных);
тип имя_2(список_переменных);
...
тип имя_n(список_переменных);
int main (список_переменных)
{
//Тело функции может содержать операторы вызова функций имя_1, имя_2, ..., имя_n
тело_основной_функции;
}
тип имя_1(список_переменных)
{
тело_функции_1;
}
тип имя_2(список_переменных)
{
тело_функции_2;
}
...
тип имя_n(список_переменных)
{
тело_функции_n;
}
Здесь функции описаны после функции main(), однако до неё перечислены заголовки всех функций. Такого рода опережающие заголовки называют прототипами функций. Прототип указывает компилятору тип данных, возвращаемых функцией, тип переменных, выступающих в роли аргументов, и порядок их следования. Прототипы используются для проверки правильности вызова функций в основной программе. Имена переменных, указанные в прототипе функции, компилятор игнорирует:
//Записи равносильны. int func ( int a, int b ); int func ( int, int );
Вызвать функцию можно в любом месте программы. Для вызова функции необходимо указать её имя и в круглых скобках, через запятую перечислить имена или значения аргументов, если таковые имеются:
имя_функции(список_переменных);
Рассмотрим пример. Создадим функцию f(), которая не имеет входных значений и не формирует результат. При вызове этой функции на экран выводится строка символов "С Новым Годом, ".
#include <iostream>
using namespace std;
void f ( ) //Описание функции.
{
cout << "С Новым Годом, ";
}
int main ( )
{
f ( ); //Вызов функции.
cout <<"Студент!" << endl;
f ( ); //Вызов функции.
cout <<"Преподаватель!" << endl;
f ( ); //Вызов функции.
cout <<"Народ!" << endl;
}
Результатом работы программы будут три строки:
С Новым Годом, Студент! С Новым Годом, Преподаватель! С Новым Годом, Народ!
Далее приведён пример программы, которая пять раз выводит на экран фразу "Здравствуй, мир!". Операция вывода строки символов оформлена в виде функции fun(). Эта функция также не имеет входных значений и не формирует результат. Вызов функции осуществляется в цикле:
#include <iostream>
using namespace std;
void fun ( )
{
cout << "Здравствуй, мир!" << endl;
}
int main ( )
{
for ( int i =1; i <=5; fun ( ), i ++);
}
Если тип возвращаемого значения не void, то функция может входить в состав выражений. Типы и порядок следования переменных в определении и при вызове функции должны совпадать. Для того чтобы функция вернула какое-либо значение, в ней должен быть оператор:
return (выражение);
Далее приведён пример программы, которая вычисляет значение выражения $$\sin^2(\alpha)+\cos^2(\alpha)$$ при заданном значении α. Здесь функция radian выполняет перевод градусной меры угла в
#include <iostream>
#include <math.h>
#define PI 3.14159
using namespace std;
double radian ( int deg, int min, int sec )
{
return ( deg * PI/180+min* PI /180/60+ sec * PI /180/60/60);
}
int main ( )
{
int DEG, MIN, SEC; double RAD;
//Ввод данных.
cout<<" Input :"<<endl; //Величина угла:
cout<<" DEG ="; cin >>DEG; //градусы,
cout<<" MIN ="; cin >>MIN; //минуты,
cout<<" SEC ="; cin >>SEC; //секунды.
//Величина угла в радианах.
RAD=radian (DEG, MIN, SEC); //Вызов функции.
cout << " Value in radian A="<<RAD << endl;
//Вычисление значения выражения и его вывод.
cout << " sin (A) ^2+ cos (A) ^2= ";
cout << pow( sin (RAD), 2 )+pow( cos (RAD), 2 ) << endl;
return 0;
}
Переменные, описанные внутри функции, а также переменные из списка аргументов, являются локальными. Например, если программа содержит пять разных функций, в каждой из которых описана переменная $$N$$, то для C++ это пять различных переменных. Область действия локальной переменной не выходит за рамки функции. Значения локальных переменных между вызовами одной и той же функции не сохраняются.
Переменные, определённые до объявления всех функций и доступные всем функциям, называют глобальными. В функции глобальную переменную можно отличить, если не описана локальная переменная с теми же именем. Глобальные переменные применяют для передачи данных между функциями, но это затрудняет отладку программы. Для обмена данными между функциями используют параметры функций и значения, возвращаемые функциями.
Обмен информацией между вызываемой и вызывающей функциями осуществляется с помощью механизма передачи параметров.Список_переменных, указанный в заголовке функции, называется формальными параметрами или просто параметрами функции. Список_переменных в операторе вызова функции — это фактические параметры или аргументы.
Механизм передачи параметров обеспечивает замену формальных параметров фактическими параметрами и позволяет выполнять функцию с различными данными. Между фактическими параметрами в операторе вызова функции и формальными параметрами в заголовке функции устанавливается взаимно однозначное соответствие. Количество, типы и порядок следования формальных и фактических параметров должны совпадать.
Передача параметров выполняется следующим образом. Вычисляются выражения, стоящие на месте фактических параметров. В памяти выделяется место под формальные параметры в соответствии с их типами. Затем формальным параметрам присваиваются значения фактических. Выполняется проверка типов и при необходимости выполняется их преобразование.
Передача параметров в функцию может осуществляться по значению и по адресу.
При передаче данных по значению функция работает с копиями фактических параметров, и доступа к исходным значениям аргументов у неё нет. При передаче данных по адресу в функцию передаётся не переменная, а её адрес, и, следовательно, функция имеет доступ к ячейкам памяти, в которых хранятся значения аргументов. Таким образом, данные, переданные по значению, функция изменить не может, в отличие от данных, переданных по адресу.
Если требуется запретить изменение параметра внутри функции, используют модификатор const. Заголовок функции в общем виде будет выглядеть так:
тип имя_функции ( const тип_переменной* имя_переменной, ...)
Например:
#include <iostream>
using namespace std;
int f1 ( int i ) //Данные передаются по значению
{
return ( i ++);
}
int f2 ( int * j ) //Данные передаются по адресу. При подстановке фактического параметра,
//для получения его значения, применяется операция разадресации * .
{
return ( ( * j )++);
}
int f3 ( const int * k ) //Изменение параметра не предусмотрено .
{
return ( ( * k )++);
}
int main ( )
{
int a;
cout<<"a="; cin >>a;
f1( a );
cout<<"a="<<a<<"\n";
f2(a ); //Для передачи фактического параметра используется операция взятия адреса .
cout<<"a="<<a<<"\n";
f3(a );
cout<<"a="<<a<<"\n";
return 0;
}
Результат работы программы:
Введено значение переменной $$a$$.
a=5
Значение переменной a после вызова функции $$f1$$ не изменилось.
a=5
Значение переменной a после вызова функции $$f2$$ изменилось.
a=6
Значение переменной a после вызова функции $$f3$$ не изменилось.
a=6
Удобно использовать передачу данных по адресу, если нужно чтобы функция изменяла значения переменных в вызывающей программе.
Далее приведён пример программы, в которой исходя из радианной меры некоторого угла вычисляется величина смежного с ним угла. На экран выводятся значения углов в градусной мере. Функция degree выполняет перевод из радианной меры в rad, определяющее величину угла в радианах, и адреса переменных deg, min, sec, в которых будут храниться вычисленные результаты — градусная мера угла.
#include <iostream>
#include <math.h>
#define PI 3.14159
using namespace std;
void degree ( double rad, int * deg, int * min, int * sec )
{
* deg= floor ( rad * 180/ PI );
* min=floor ( ( rad * 180/ PI -(* deg ) ) * 60);
* sec=floor ( ( ( rad * 180/ PI -(* deg ) ) *60 -(* min ) ) * 60);
}
int main ( )
{
int DEG, MIN, SEC; double RAD;
cout<<" Inpout :"<<endl;
cout << " Value in radian A="; cin >>RAD;
degree (RAD,DEG,MIN,SEC);
cout << DEG<<" "<<MIN<<" "<<SEC << endl;
degree ( PI- RAD,DEG,MIN,SEC);
cout << DEG<<" "<<MIN<<" "<<SEC << endl;
return 0;
}
Возврат результата из функции в вызывающую её функцию осуществляется оператором return выражение;
Работает оператор следующим образом. Вычисляется значение выражения, указанного после return, и преобразуется к типу возвращаемого функцией значения. Выполнение функции завершается, а вычисленное значение передаётся в вызывающую функцию. Любые операторы, следующие в функции за оператором return, игнорируются. Программа продолжает свою работу с оператора, следующего за оператором вызова данной функции.
Оператор return может отсутствовать в функциях типа void, если возврат происходит перед закрывающейся фигурной скобкой, и в функции main.
Также функция может содержать несколько операторов return, если это определено потребностями алгоритма. Например, в следующей программе функция equation вычисляет корни квадратного уравнения. Если $$a = 0$$ (уравнение не является квадратным), то в программу передаётся значение равное -1, если дискриминант отрицательный (уравнение не имеет действительных корней), то 1, а если положительный, то вычисляются корни уравнения и в программу передаётся 0.
#include <iostream>
#include <math.h>
using namespace std;
int equation ( float a, float b, float c, float * x1, float * x2 )
{ float D=b*b-4*a*c;
if ( a==0) return -1;
else if (D<0) return 1;
else
{
*x1=(-b+sqrt (D) ) /2/ a;
*x2=(-b-sqrt (D) ) /2/ a;
return 0;
}
}
int main ( )
{
float A, B, C, X1, X2; int P;
cout<<" Enter the coefficients of the equation :"<<endl;
cout<<"A="; cin >>A;
cout<<"B="; cin >>B;
cout<<"C="; cin >>C;
P=equation ( A, B, C, X1, X2);
if (P==-1) cout<<" input Error "<<endl;
else if (P==1) cout<<" No real roots "<<endl;
else cout<<" X1="<<X1<<" X2="<<X2<<endl;
return 0;
}
Рассмотрим несколько задач с применением функций.
Задача 4.1. Вводится последовательность из $$N$$ целых чисел, найти среднее арифметическое совершённых чисел и среднее геометрическое простых чисел последовательности.
Напомним, что целое число называется простым, если оно делится нацело только на само себя и единицу. Подробно алгоритм определения простого числа описан в задаче 3.15 (рис. 3.29). В этой задаче кроме простых чисел фигурируют совершённые числа. Число называется совершённым, если сумма всех делителей, меньших его самого, равна этому числу. Алгоритм, с помощью которого можно определить делители числа, подробно рассмотрен в задаче 3.14 (рис. 3.28).
Для решения поставленной задачи понадобятся две функции:
prostoe — определяет, является ли число простым, аргумент функции целое число $$N$$; функция возвращает 1, если число простое и 0 — в противном случае.;soversh — определяет, является ли число совершённым; входной параметр целое число $$N$$; функция возвращает 1, если число является совершённым и 0 — в противном случае.
#include <iostream>
#include <math.h>
unsigned int prostoe ( unsigned int N) //Описание функции.
{
//Функция определяет, является ли число простым.
int i, pr;
for ( pr =1, i =2; i<=N/ 2; i ++)
if (N%i ==0) { pr =0; break; }
return pr;
}
unsigned int soversh ( unsigned int N) //Описание функции.
{
//Функция определяет, является ли число совершённым.
unsigned int i, S;
for ( S=0, i =1; i<=N/ 2; i ++)
if (N%i ==0) S+=i; //Сумма делителей.
if ( S==N) return 1;
else return 0;
}
using namespace std;
int main ( )
{
unsigned int i,N,X, S, kp, ks;
long int P;
cout <<"N="; cin >>N;
for ( kp=ks=S=0,P=1, i =1; i<=N; i ++)
{
cout <<"X="; cin >> X; //Вводится элемент последовательности.
if ( prostoe (X) ) // X — простое число.
{
kp++; //Счётчик простых чисел.
P*=X; //Произведение простых чисел.
}
if ( soversh (X) ) //X — совершённое число.
{
ks++; //Счётчик совершённых чисел.
S+=X; //Сумма совершённых чисел.
}
}
if ( kp>0) //Если счётчик простых чисел больше нуля,
//считаем среднее геометрическое и выводим его,
cout<<"Среднее геометрическое="<<pow(P, ( float ) 1/kp )<<endl;
else //в противном случае –– сообщение об отсутствии простых чисел.
cout<<"Нет простых чисел\n";
if ( ks>0) //Если счётчик совершённых чисел больше нуля,
//считаем среднее арифметическое и выводим его,
cout<<"Среднее арифметическое="<<(float ) S/ ks<<endl;
else //в противном случае — сообщение об отсутствии совершённых чисел.
cout<<"Нет совершённых чисел\n";
return 0;
}
Задача 4.2. Вводится последовательность целых чисел, 0 — конец последовательности. Найти минимальное число среди простых чисел и максимальное — среди чисел, не являющихся простыми.
Для решения данной задачи создадим функцию prostoe, которая будет проверять, является ли число $$N$$ простым. Входным параметром функции будет целое положительное число $$N$$. Функция будет возвращать значение 1, если число простое, и 0 — в противном случае. Алгоритм поиска максимума (минимума) подробно описан в задаче 3.20 (рис. 3.31).
Текст программы:
#include <iostream>
using namespace std;
unsigned int prostoe ( unsigned int N)
{
int i, pr;
for ( pr =1, i =2; i>=N/ 2; i ++)
if (N%i ==0) { pr =0; break; }
return pr;
}
int main ( )
{
int kp=0,knp=0,min, max,N;
for ( cout << "N=", cin >>N; N != 0; cout<<"N=", cin >>N)
//В цикле вводится элемент последовательности N.
if ( prostoe (N) ) //N — простое число,
{
kp++; //счётчик простых чисел.
if ( kp==1) min=N; //Предполагаем, что первое простое число минимально,
else if (N<min ) min=N; //если найдётся меньшее число, сохраняем его.
}
else //N — простым не является,
{
knp++; //счётчик чисел не являющихся простыми.
if ( knp==1) max=N; //Предполагаем, что первое не простое число максимально,
else if (N>max) max=N; //если найдётся большее число, сохраняем его.
}
if ( kp>0) //Если счётчик простых чисел больше нуля,
cout <<" min = "<<min<<"\t"; //выводим значение минимального простого числа,
else //в противном случае —
cout <<"Нет простых чисел"; //сообщение об отсутствии простых чисел.
if ( knp>0) //Если счётчик чисел не являющихся простыми больше нуля,
cout <<" max ="<<max<<endl; //выводим значение максимального числа,
else //в противном случае —
cout <<"Нет составных чисел"; //сообщение об отсутствии чисел
//не являющихся простыми.
return 0;
}
Задача 4.3. Вводится последовательность из $$N$$ целых положительных чисел. В каждом числе найти наименьшую и наибольшую
Программный код к задаче 4.3.
#include <iostream>
using namespace std;
unsigned int Cmax( unsigned long long int P)
{
unsigned int max;
if (P==0) max=0;
for ( int i =1; P!=0;P/=10)
{
if ( i ==1) {max=P%10; i ++;}
if (P%10>max) max=P%10;
}
return max;
}
unsigned int Cmin( unsigned long long int P)
{
unsigned int min;
if (P==0) min=0;
for ( int i =1; P!=0;P/=10)
{
if ( i ==1) {min=P%10; i ++;}
if (P%10<min ) min=P%10;
}
return min;
}
int main ( )
{
unsigned int N, k;
unsigned long long int X;
for ( cout<<"N=", cin >>N, k=1;k<=N; k++)
{
cout<<"X="; cin >>X;
cout<<"Максимальная цифра="<<Cmax(X);
cout<<" Минимальная цифра="<<Cmin(X)<<endl;
}
return 0;
}
Задача 4.4. Вводится последовательность целых положительных чисел, 0 — конец последовательности. Определить, сколько в последовательности
Алгоритм определения палиндрома подробно описан в задаче 3.19. Далее приведён программный код к задаче 4.4
#include <iostream>
using namespace std;
bool palindrom ( unsigned long long int N)
{ //Функция определяет, является ли число N палиндромом, возвращает true, если N —
//палиндром, и false в противном случае
unsigned long int M, R, S;
int kol, i;
for (R=1, kol =1,M=N;M/10>0; kol ++,R*=10,M/=10);
for ( S=0,M=N, i =1; i<=kol; S+=M%10*R,M/=10,R/=10, i ++);
if (N==S ) return true;
else return false;
}
int main ( )
{ unsigned long long int X;
int K;
for (K=0, cout<<"X=", cin >>X;X!=0; cout<<"X=", cin >>X)
if ( palindrom (X) ) K++;
cout<<"Количество палиндромов равно K="<<K<<endl;
return 0;
}
Задача 4.5. Заданы два числа — $$X$$ в двоичной системе счисления, $$Y$$ в системе счисления с основанием пять. Найти сумму этих чисел. Результат вывести в десятичной системе счисления.
Любое целое число $$N$$, заданное в $$b$$-ичной системе счисления, можно представить в десятичной системе счисления: $$N=P_n\cdot b^b+P_{n-1}\cdot b^{n-1}+...+P_2\cdot b^2+P_1\cdot b+P_0=\sum_{i=0}^n{P_i\cdot b^i},$$ где $$b$$ — основание системы счисления (целое положительное фиксированное число), $$P_i$$ — разряд числа: $$0\leqslant P_i\leqslant b-1,i=0,1,2,...,n.$$ Например,
$$743_{10}=7\cdot 10^2+4\cdot 10^1+3\cdot 10^0=700+40+3=743_{10};\\ 1011101_2=1\cdot 2^6+0\cdot 2^5+1\cdot 2^4+1\cdot 2^3+1\cdot 2^2+0\cdot 2^1+1\cdot 2^0=64+16+8+4+1=93_{10}.$$Создадим функцию для перевода целого числа $$N$$, заданного в $$b$$-ичной системе счисления, в десятичную систему счисления.
#include <iostream>
using namespace std;
unsigned long long int DecNC( unsigned long long int N, unsigned int b )
{
//Функция выполняет перевод числа N, заданного в b-ичной системе счисления,
//в десятичную систему счисления
unsigned long long int S, P;
for ( S=0,P=1;N!=0; S+=N%10*P, P*=b,N/=10);
return S;
}
int main ( )
{
unsigned long long int X,Y; unsigned int bX, bY;
cout<<"X="; cin >>X; //Ввод числа X.
cout<<"b="; cin >>bX; //Ввод основания с/с.
cout<<"Y="; cin >>Y; //Ввод числа X.
cout<<"b="; cin >>bY; //Ввод основания с/с.
//Вывод заданных чисел в десятичной с/с.
cout<<X<<"("<<bX<<")="<<DecNC(X, bX)<<" (10) "<<endl;
cout<<Y<<"("<<bY<<")="<<DecNC(Y, bY)<<" (10) "<<endl;
//Вычисление суммы и вывод результата.
cout<<X<<"("<<bX<<")+"<<Y<<"("<<bY<<")=";
cout<<DecNC(X, bX)+DecNC(Y, bY)<<" (10) "<<endl;
return 0;
}
Задача 4.6. Задано число $$X$$ в десятичной системе счисления. Выполнить перевод числа в системы счисления с основанием 2, 5 и 7.
Вообще, для того чтобы перевести целое число из десятичной системы счисления в другую, необходимо выполнить следующие действия:
На рис. 4.1 приведён пример "ручного" перевода числа 256, заданного в десятичной системе счисления, в восьмеричную. В результате получим $$256_{(10)}=400_{(8)}$$
(рис 4.1) Пример перевода числа в новую систему счисления
Далее приведён текст программы, реализующей решение задачи 4.6.
#include <iostream>
using namespace std;
unsigned long long int NC( unsigned long long int N, unsigned int b )
{
unsigned long long int S, P;
for ( S=0,P=1;N!=0; S+=N%b *P, P*=10,N/=b );
return S;
}
int main ( )
{
unsigned long long int X;
cout<<"X="; cin >>X; //Ввод числа X.
//Перевод числа X в заданные системы счисления.
cout<<X<<" (10) ="<<NC(X, 2 )<<" (2) "<<endl;
cout<<X<<" (10) ="<<NC(X, 5 )<<" (5) "<<endl;
cout<<X<<" (10) ="<<NC(X, 7 )<<" (7) "<<endl;
return 0;
}
Задача 4.7. Найти корни уравнения $$x^2-\cos (5\cdot x)=0$$.
Для решения задачи использовать:
Оценить степень точности предложенных численных методов, определив, за сколько итераций был найден корень уравнения. Вычисления проводить с точностью $$\varepsilon=10^{-3}$$.
Вообще говоря, аналитическое решение уравнения $$f(x) = 0$$ можно найти только для узкого класса функций. В большинстве случаев приходится решать уравнение (4.1) численными методами. Численное решение уравнения (4.1) проводят в два этапа: сначала необходимо отделить корни уравнения, т.е. найти достаточно тесные промежутки, в которых содержится только один корень, эти промежутки называют интервалами изоляции корня; на втором этапе проводят уточнение отделённых корней, т.е. находят корни с заданной точностью.
Интервал можно выделить, изобразив график функции, или каким-либо другим способом. Но все способы основаны на следующем свойстве непрерывной функции: если функция $$f (x)$$ непрерывна на интервале [$$a, b$$] и на его концах имеет различные знаки, $$f (a) \cdot f (b) < 0$$, то между точками имеется хотя бы один корень. Будем считать интервал настолько малым, что в нём находится только один корень. Рассмотрим самый простой способ уточнения корней.
Графическое решение задачи 4.7 показано на рис. 4.2. Так как функция $$f(x)=x^2-\cos (5\cdot x)$$ дважды пересекает ось абсцисс, можно сделать вывод о наличии в уравнении $$x^2-\cos (5\cdot x)=0$$ двух корней. Первый находится на интервале [-0.4; -0.2], второй принадлежит отрезку [0.2; 0.4] .
(рис 4.2) Геометрическое решение задачи 4.7
Рассмотрим предложенные в задаче численные методы решения нелинейных уравнений.
Метод половинного деления (дихотомии). Пусть был выбран интервал изоляции [$$a, b$$] (рис. 4.3). Примем за первое приближение корня точку c, которая является серединой отрезка [$$a, b$$].Далее будем действовать по следующему алгоритму:
Итак, для вычисления одного из корней уравнения $$ x^2-\cos (5\cdot x)=0$$ методом половинного деления достаточно знать интервал изоляции корня $$a=0.2;b=0.4$$ и точность вычисления $$\varepsilon=10^{-3}$$.
Блок-схема алгоритма решения уравнения методом дихотомии приведена на рис. 4.4. Понятно, что здесь $$c$$ — корень заданного уравнения.
Однако, несмотря на простоту, такое последовательное сужение интервала проводится редко, так как требует слишком большого количества вычислений.
(рис 4.3) Графическая интерпретация метода половинного деления
Кроме того, этот способ не всегда позволяет найти решение с заданной точностью. Рассмотрим другие способы уточнения корня. При применении этих способов будем требовать, чтобы функция $$f (x)$$ удовлетворяла следующим условиям на интервале [$$a, b$$] :
Метод хорд. Этот метод отличается от метода дихотомии тем, что очередное приближение берём не в середине отрезка, а в точке пересечения с осью $$X$$ (рис. 4.5) прямой, соединяющей точки ($$a, f (a)$$) и ($$b, f (b)$$).
Запишем уравнение прямой, проходящей через точки с координатами ($$a, f (a)$$) и ($$b, f (b)$$) : $$\frac{y-f(a)}{f(b)-f(a)}=\frac{x-a}{b-a},\ \ \ y=\frac{f(b)-f(a)}{b-a}\cdot (x-a)+f(a)$$
Прямая, заданная уравнением (4.2), пересекает ось $$X$$ при условии $$y = 0$$.
Найдём точку пересечения хорды с осью $$X$$: $$y=\frac{f(b)-f(a)}{b-a}\cdot (x-a)+f(a),\ \ x=a-\frac{f(a)\cdot (b-a)}{f(b)-f(a)}.$$ итак, $$c=a-\frac{f(a)}{f(b)-f(a)}(b-a)$$.
Далее необходимо вычислить значение функции в точке $$c$$. Это и будет приближённое значение корня уравнения.
(рис 4.4) Алгоритм решения уравнения методом дихотомии
Для вычисления одного из корней уравнения $$x^2-\cos (5\cdot x)=0$$ методом хорд достаточно знать интервал изоляции корня, например, $$a = 0.2; b = 0.4$$, и точность вычисления $$\varepsilon=10^{-3}$$. Блок-схема метода представлена на рис. 4.6.
Метод касательных (метод Ньютона). В одной из точек интервала [$$a; b$$], пусть это будет точка $$a$$, проведём касательную (рис. 4.7). Запишем уравнение этой прямой:$$y=k\cdot x+m$$
Так как эта прямая является касательной, и она проходит через точку $$(c,f(c))$$, то $$k=f'(c)$$.
Следовательно, $$y=f'(x)\cdot x+m,f(c)=f'(c)\cdot c+m,m=f(c)-c\cdot f'(c),\\y=f'(c)\cdot x+f(c)-c\cdot f'(c),y=f'(c)\cdot (x-c)+f(c)$$.
Найдём точку пересечения касательной с осью $$X$$: $$f'(c)\cdot (x-c)+f(c)=0,\ \ x=c-\frac{f(c)}{f'(c)}$$
(рис 4.5) Графическая интерпретация метода хорд
Если $$|f(x)|<\varepsilon$$, то точность достигнута, и точка $$x$$ — решение; иначе необходимо переменной c присвоить значение $$x$$ и провести касательную через новую точку $$c$$; так продолжать до тех пор, пока $$|f (x)|$$ не станет меньше $$\varepsilon$$. Осталось решить вопрос, что выбрать в качестве точки начального приближения $$c$$.
В этой точке должны совпадать знаки функции и её второй производной. А так как нами было сделано допущение, что вторая и первая производные не меняют знак, то можно проверить условие $$f(x)\cdot f''(x)>0$$ на обоих концах интервала, и в качестве начального приближения взять ту точку, где это условие выполняется.
Здесь, как и в предыдущих методах, для вычисления одного из корней уравнения $$x^2-\cos (5\cdot x)=0$$ достаточно знать интервал изоляции корня, например, $$a = 0.2; b = 0.4$$, и точность вычисления $$\varepsilon=10^{-3}$$. Блок-схема метода Ньютона представлена на рис. 4.8. Понятно, что для реализации этого алгоритма нужно найти первую и вторую производные функции $$f(x)=x^2-\cos (5\cdot x): f'(x)=2\cdot x+5\cdot \sin (5\cdot x), f(x) =2 +25 \cdot cos(5 \cdot x)$$.
Метод простой итерации. Для решения уравнения этим методом необходимо записать уравнение (4.1) в виде $$x=\phi(x)$$, задать начальное приближение $$x_0\in [a;b]$$ и организовать следующий итерационный вычислительный процесс: $$x_{k+1}=\phi(x_k),k=0,1,2,...$$
Вычисление прекратить, если $$|x_{k+1}-x_k|<\varepsilon$$ ($$\varepsilon$$ — точность).
Если неравенство $$|\phi'(x)|<1$$ выполняется на всём интервале [$$a; b$$], то последовательность $$x_0, x_1, x_2,...,x_n,... , ...$$ сходится к решению $$x^*$$ (т.е. l$$\lim\limits_{k\rightarrow \infty}x_k=x^*$$).
Значение функции $$\phi(x)$$ должно удовлетворять условию $$|\phi'(x)|<1$$ для того, чтобы можно было применить метод простых итераций. Условие $$|\phi'(x)|<1$$ является достаточным условием сходимости метода простой итерации.
(рис 4.6) Алгоритм метода хорд
Уравнение (4.1) можно привести к виду $$x=\phi(x)$$ следующим образом. Умножить обе части уравнения $$f (x) = 0$$ на число $$\lambda$$. К обеим частям уравнения $$\lambda\cdot f(x)=0$$ добавить число $$x$$. Получим $$x=x+\lambda\cdot f(x)$$. Это и есть уравнение вида $$x=\phi(x)$$, где
$$\phi(x)=x+\lambda\cdot f(x)$$Необходимо чтобы неравенство $$|\phi"(x)|<1$$ выполнялось на интервале [$$a; b$$], следовательно, $$|\phi"(x)|=|1+\lambda\cdot f'(x)|$$ и $$|1+\lambda\cdot f'(x)|<1 (|1+\lambda\cdot f'(a)|<1, |1+\lambda\cdotf'(b)|<1)$$, а значит, с помощью подбора параметра $$\lambda$$ можно добиться выполнения условия сходимости.
Для вычисления корней уравнения $$x^2-\cos (5\cdot x)=0$$ воспользуемся графическим решением (рис. 4.2) и определим интервал изоляции одного из корней, например, $$a = 0.2; b = 0.4$$. Подберём значение $$\lambda$$, решив неравенство $$|1+\lambda\cdot f'(x)|<1:|1+\lambda\cdot f'(a)|<1$$ и $$|1+\lambda\cdot f'(b)|<1,\\f(x)=x^{2}-\cos (5\cdot x),f'(x)=2\cdot x+5\cdot \sin (5\cdot x),\\f'(a)=2\cdot 0.2+5\cdot \sin (5\cdot 0.2)\approx 4.6, f'(b)=2\cdot 0.4+5\cdot \sin (5\cdot 0.4)\approx 5.35,\\|1+\lambda\cdot 4.6|<1$$ и $$|1+\lambda\cdot 5.35|<1$$. $$\left\{\begin{array}{l} \left\{\begin{array}{l} 1+4.6\cdot \lambda<1 \cr 1+4.6\cdot \lambda>-1 \end{array}\right.\cr \left\{\begin{array}{l} \lambda<0 \cr \lambda>-0.37 \end{array}\right. \end{array}\right. \Rightarrow \left\{\begin{array}{l} \left\{\begin{array}{l} \lambda<0 \cr \lambda>-0.4 \end{array}\right.\cr \left\{\begin{array}{l} \lambda<0 \cr \lambda>-0.37 \end{array}\right. \end{array}\right. \Rightarrow \left\{\begin{array}{l} \lambda\in (-0.4;0) \cr \lambda\in (-0.37;0) \end{array}\right.$$
и, следовательно, $$\lambda\in (-0.37;0)$$.
(рис 4.7) Графическая интерпретация метода касательных
Таким образом, исходными данными для программы будут начальное значение корня уравнения $$x_0=0.2$$, значение параметра $$\lambda$$ (пусть $$\lambda= -0.2$$), и точность вычислений $$\varepsilon=0.001$$.
Для вычисления второго корня заданного уравнения параметр $$\lambda$$ подбирают аналогично.
Блок-схема метода простой итерации приведена на рис. 4.9.
Далее представлен текст программы, реализующий решение задачи 4.7.
#include <iostream>
#include <math.h>
using namespace std;
//Функция, определяющая левую часть уравнения f (x) = 0.
double f ( double x )
{
return ( x *x-cos (5 * x ) );
}
//Функция, реализующая метод половинного деления.
int Dichotomy ( double a, double b, double * c, double eps )
{ int k=0;
do
{
* c=(a+b ) / 2;
if ( f ( * c ) * f ( a ) <0) b=*c;
else a=*c;
k++;
}
while ( fabs ( a-b )>=eps );
return k;
}
//Функция, реализующая метод хорд.
int Chord ( double a, double b, double * c, double eps )
{ int k=0;
do
{
* c=a-f ( a ) /( f ( b )-f ( a ) ) * ( b -a );
if ( f ( * c ) * f ( a ) >0) a=*c;
else b=*c;
k++;
}
while ( fabs ( f ( * c ) )>=eps );
return k;
}
double f1 ( double x ) //Первая производная функции f (x).
{
return (2 * x+5* sin (5 * x ) );
}
double f2 ( double x ) //Вторая производная функции f (x).
{
return (2+25* cos (5 * x ) );
}
//Функция, реализующая метод касательных.
int Tangent ( double a, double b, double * c, double eps )
{ int k=0;
if ( f ( a ) * f2 ( a ) >0) * c=a;
else * c=b;
do
{
* c=*c-f (* c ) / f1 ( * c );
k++;
}
while ( fabs ( f ( * c ) )>=eps );
return k;
}
double fi ( double x, double L) //Функция, заданная выражением 4.4 .
{
return ( x+L* f ( x ) );
}
//Функция, реализующая метод простой итерации.
int Iteration ( double *x, double L, double eps )
{ int k=0; double x0;
do
{
x0=*x;
*x= fi(x0, L);
k++;
}
while ( fabs ( x0-*x )>=eps );
return k;
}
int main ( )
{
double A, B, X, P;
double ep =0.001; //Точность вычислений.
int K;
cout<<"a="; cin >>A; //Интервал изоляции корня.
cout<<"b="; cin >>B;
cout<<"Решение уравнения x^2 - cos (5* x) =0. "<<endl;
cout<<"Метод дихотомии:"<<endl;
K=Dichotomy (A, B,X, ep );
cout<<"Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
cout<<"Метод хорд:"<<endl;
K=Chord (A, B,X, ep );
cout<<" Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
cout<<"Метод касательных:"<<endl;
K=Tangent (A, B,X, ep );
cout<<" Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
cout<<"Метод простой итерации:"<<endl;
X=A;
cout<<"L="; cin >>P;
K=Iteration (X, P, ep );
cout<<" Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
return 0;
}
(рис 4.8) Алгоритм метода Ньютона
(рис 4.9) Алгоритм метода простой итерации
Результаты работы программы:
a=0.2 b=0.4 Решение уравнения x^2-cos(5*x)=0. Метод дихотомии: Найденное решение x=0.296094, количество итераций k=8 Метод хорд: Найденное решение x=0.296546, количество итераций k=2 Метод касательных: Найденное решение x=0.296556, количество итераций k=2 Метод простой итерации: L=-0.2 Найденное решение x=0.296595, количество итераций k=3
Под рекурсией в программировании понимают функцию, которая вызывает сама себя. Рекурсивные функции чаще всего используют для компактной реализации рекурсивных алгоритмов. Классическими рекурсивными алгоритмами могут быть возведение числа в целую положительную степень, вычисление факториала. С другой стороны, любой рекурсивный алгоритм можно реализовать без применения рекурсий. Достоинством рекурсии является компактная запись, а недостатком — расход памяти на повторные вызовы функций и передачу параметров, существует опасность переполнения памяти.
Рассмотрим применение рекурсии на примерах [7, 8].
Задача 4.8. Вычислить факториал числа $$n$$.
Вычисление факториала подробно рассмотрено в задаче 3.12 (рис. 3.25). Для решения этой задачи с применением рекурсии создадим функцию factoial, алгоритм которой представлен на рис. 4.10.
(рис 4.10) Рекурсивный алгоритм вычисления факториала
Текст программы с применением рекурсии:
#include <iostream>
using namespace std;
long int factorial ( int n )
{
if ( n<=1)
return n;
else
return n*factorial ( n-1);
}
int main ( )
{
int i; long int f;
cout<<"i="; cin >>i;
f=factorial ( i );
cout<<i <<" !="<<f<<"\n";
return 0;
}
Задача 4.9. Вычислить $$n$$-ю степень числа $$a$$ ($$n$$ — целое число).
Результатом возведения числа $$a$$ в целую степень $$n$$ является умножение этого числа на себя $$n$$ раз. Но это утверждение верно только для положительных значений $$n$$. Если $$n$$ принимает отрицательные значения, то $$a^{-n}=\frac{1}{a^n}$$.В случае $$n=0,\ \ a^0=1$$.
Для решения задачи создадим рекурсивную функцию stepen, алгоритм которой представлен на рис. 4.11.
(рис 4.11) Рекурсивный алгоритм вычисления степени числа
Текст программы с применением рекурсии:
#include <iostream>
using namespace std;
float stepen ( float a, int n )
{
if ( n==0)
return 1;
else if ( n<0)
return 1/ stepen ( a,-n );
else
return a * stepen ( a, n-1);
}
int main ( )
{
int i; float s, b;
cout<<"b="; cin >>b;
cout<<"i="; cin >>i;
s=stepen ( b, i );
cout<<"s="<<s<<"\n";
return 0;
}
Задача 4.10. Вычислить $$n$$-е число Фибоначчи.
Если нулевой элемент последовательности равен нулю, первый — единице, а каждый последующий представляет собой сумму двух предыдущих, то это последовательность чисел Фибоначчи (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... ).
Алгоритм рекурсивной функции fibonachi изображён на рис. 4.12.
(рис 4.12) Рекурсивный алгоритм вычисления числа Фибоначчи
Текст программы:
#include <iostream>
using namespace std;
long int fibonachi ( unsigned int n )
{
if ( ( n==0) | | ( n==1))
return n;
else
return fibonachi ( n -1)+fibonachi ( n-2);
}
int main ( )
{
int i; long int f;
cout<<"i="; cin >>i;
f=fibonachi ( i );
cout<<"f="<<f<<"\n";
return 0;
}
Язык C++ позволяет связать с одним и тем же именем функции различные определения, то есть возможно существование нескольких функций с одинаковым именем. У этих функций может быть разное количество параметров или разные типы параметров. Создание двух или более функций с одним и тем же именем называется перегрузкой имени функции. Перегруженные функции создают, когда одно и то же действие следует выполнить над разными типами входных данных.
В приведённом далее тексте программы три функции с именем Pow. Первая выполняет операцию возведения вещественного числа $$a$$ в дробную степень $$n=\frac{k}{m}$$, где $$k$$ и $$m$$ — целые числа. Вторая возводит вещественное число $$a$$ в целую степень $$n$$, а третья — целое число $$a$$ в целую Pow(a,k) вызовет вторую функцию, так как она имеет заголовок float Pow(float a, int n). Команда Pow((int)a,k) приведёт к вызову третьей функции float Pow(int a, int n), так как вещественная переменная $$a$$ преобразована к целому типу. Первая функция float Pow(float a, int k, int m) имеет три параметра, значит, обращение к ней осуществляется командой Pow(a,k,m).
#include <iostream>
using namespace std;
#include <math.h>
float Pow( float a, int k, int m) //Первая функция
{
cout<<"Функция 1 \t";
if ( a==0)
return 0;
else if ( k==0)
return 1;
else if ( a>0)
return exp ( ( float ) k/m *log ( a ) );
else if (m%2!=0)
return -(exp ( ( float ) k/m *log (-a ) ) );
}
float Pow( float a, int n ) //Вторая функция
{
float p; int i;
cout<<"Функция 2 \t";
if ( a==0)
return 0;
else if ( n==0)
return 1;
else if ( n<0)
{
n= -n;
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return ( float ) 1/p;
}
else
{
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return p;
}
}
float Pow( int a, int n ) //Третья функция
{
int i, p;
cout<<"Функция 3 \t";
if ( a==0)
return 0;
else if ( n==0)
return 1;
else if ( n<0)
{
n= -n;
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return ( float ) 1/p;
}
else
{
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return p;
}
}
int main ( )
{
float a; int k, n,m;
cout<<"a="; cin >>a;
cout<<"k="; cin >>k;
cout<<"s="<<Pow( a, k )<<"\n"; //Вызов 2-й функции.
cout<<"s="<<Pow ( ( int ) a, k )<<"\n"; //Вызов 3-й функции.
cout<<"a="; cin >>a;
cout<<"k="; cin >>k;
cout<<"m="; cin >>m;
cout<<"s="<<Pow( a, k,m)<<endl; //Вызов 1-й функции.
return 0;
}
Результаты работы программы:
a=5.2 k=3 Функция 2 s=140.608 Функция 3 s=125 a=-8 k=1 m=1 Функция 1 s=-8 a=5.2 k=-3 Функция 2 s=0.00711197 Функция 3 s=0.008 a=-8 k=1 m=3 Функция 1 s=-2
Шаблон — это особый вид функции. С помощью шаблона функции можно определить алгоритм, который будет применяться к данным различных типов. Механизм работы шаблона заключается в том, что на этапе компиляции конкретный тип данных передаётся в функцию в виде параметра.
Простейшую функцию–шаблон в общем виде можно записать так [6]:
template <class Type> заголовок
{
тело функции
}
Обычно в угловых скобках указывают список используемых в функции типов данных. Каждый тип предваряется служебным словом class. В общем случае в списке могут быть не только типы данных, но и имена переменных.
Рассмотрим пример шаблона поиска наименьшего из четырёх чисел.
#include <iostream>
using namespace std;
//Определяем абстрактный тип данных с помощью служебного слова Type .
template <class Type>
Type minimum( Type a, Type b, Type c, Type d )
{ //Определяем функцию с использованием типа данных Type .
Type min=a;
if ( b<min ) min=b;
if ( c<min ) min=c;
if ( d<min ) min=d;
return min;
}
int main ( )
{
int ia, ib, ic, id, mini; float ra, rb, rc, rd, minr;
cout<<" Vvod 4 thelih chisla \t";
cin >>ia >>ib>>ic >>id;
mini=minimum( ia, ib, ic, id ); //Вызов функции minimum, в которую передаём
//4 целых значения.
cout<<"\n"<<mini<<"\n";
cout<<" Vvod 4 vecshestvenih chisla \t"; cin >>ra>>rb>>rc>>rd;
minr=minimum( ra, rb, rc, rd ); //Вызов функции minimum, в которую передаём
//4 вещественных значения.
cout<<"\n"<<minr<<"\n";
return 0;
}
Как известно (п. 2.8), по месту объявления переменные в языке C++ делятся на три класса: локальные, глобальные и переменные, описанные в списке формальных параметров функций. Все эти переменные имеют разную область видимости.
Локальные переменные объявляются внутри функции и доступны только в ней. О таких переменных говорят, что они имеют локальную видимость, то есть, видимы только внутри функции.
Глобальные переменные описывают вне всех функций. Поскольку они доступны из любой точки программы, то их область видимости охватывает весь файл.
Одно и тоже имя может использоваться при определении глобальной и локальной переменной. В этом случае в теле функции локальная переменная имеет преимущество и "закрывает" собой глобальную. Вне этой функции "работает" глобальное описание переменной.
Из функции, где действует локальное описание переменной, можно обратиться к глобальной переменной с таким же именем, используя оператор расширения области видимости
::переменная;
Рассмотрим пример:
#include <iostream>
using namespace std;
float pr =100.678; //Переменная pr определена глобально.
int prostoe ( int n )
{
int pr =1, i; //Переменная pr определена локально.
if ( n<0) pr =0;
else
for ( i =2; i<=n / 2; i ++)
if ( n%i ==0){ pr =0; break; }
cout<<" local pr="<<pr<<"\n"; //Вывод локальной переменной.
cout<<" global pr=" <<::pr<<"\n"; //Вывод глобальной переменной.
return pr;
}
int main ( )
{
int g;
cout<<"g="; cin >>g;
if ( prostoe ( g ) ) cout<<"g - prostoe \n";
else cout<<"g - ne prostoe \n";
return 0;
}
Результаты работы программы:
g=7 local pr=1 //Локальная переменная. global pr=100.678 //Глобальная переменная. g - prostoe
Итак, любая программа на C++ состоит из одной или нескольких функций, причём одна из них должна обязательно носить имя main (основной, главный ). Именно этой функции передаётся управление после запуска программы. Как любая функция, main может принимать параметры и возвращать значения. У функции main две формы записи:
тип main ( ) {тело функции },
тип main ( int argc, char *argv [ ] ) {тело функции }
.Первый параметр argc определяет количество параметров, передаваемых в функцию main из командной строки. Второй параметр argv — указатель на массив указателей типа char (массив строк). Каждый элемент массива ссылается на отдельный параметр командной строки. При стандартном запуске программы argc равно 1, argv — массив из одного элемента, этим элементом является имя запускаемого файла.
Рассмотрим следующую программу.
#include <iostream>
#include <stdlib.h>
using namespace std;
int main ( int argc, char * argv [ ] )
{
int i;
cout<<"В командной строке "<<argc<<" аргументов\n";
for ( i =0; i <argc; i ++)
cout<<"Аргумент № "<<i <<" "<<argv [ i ]<<endl;
return 0;
}
Текст программы хранится в файле 1.cpp. При стандартном запуске программа выведет следующую информацию:
В командной строке 1 аргументов Аргумент № 0 ./1
Программа выводит количество параметров командной строки и последовательно все параметры. При стандартном запуске – количество аргументов командной строки — 1, этим параметром является имя запускаемого файла (в нашем случае, имя запускаемого файла — ./1).
Запустим программу следующим образом:
./1 abc 34 6 + 90 Вася Маша
Результаты работы программы представлены ниже.
В командной строке 8 аргументов Аргумент № 0 ./1 Аргумент № 1 abc Аргумент № 2 34 Аргумент № 3 6 Аргумент № 4 + Аргумент № 5 90 Аргумент № 6 Вася Аргумент № 7 Маша
Рассмотрим приложение, в которое в качестве параметров командной строки передаётся число1, операция, число2. Функция выводит
число1 операция число2.
Текст программы приведён на atof преобразовывает строку символов в вещественное число, а если преобразование невозможно, то результатом функции atof будет число 0.0. Функция strcmp сравнивает две строки и возвращает 0 в случае совпадения строк. Подробнее об этих функциях можно прочесть в главе, посвящённой строкам.
#include <iostream>
#include <stdlib.h>
#include <cstring>
using namespace std;
int main ( int argc, char **argv )
{
//Если количество параметров больше или равно 4, то были введены два числа и знак операции.
if ( argc >=4)
//Если операция *, то выводим число1*число2.
{
if ( ! strcmp ( argv [ 2 ], "*" ) ) cout<<atof ( argv [ 1 ] ) * atof ( argv [ 3 ] )<<endl;
else
//Если операция +, то выводим число1+число2.
if ( ! strcmp ( argv [ 2 ], "+" ) ) cout<<atof ( argv [ 1 ] )+atof ( argv [ 3 ] )<<endl;
else
//Если операция -, то выводим число1-число2.
if ( ! strcmp ( argv [ 2 ], " -" ) ) cout<<atof ( argv [ 1 ] ) -atof ( argv [ 3 ] )<<endl;
else
//Если операция /, то выводим число1/число2.
if ( ! strcmp ( argv [ 2 ], "/" ) ) cout<<atof ( argv [ 1 ] ) / atof ( argv [ 3 ] )<<endl;
else cout<<"неправильный знак операции"<<endl;
}
else
cout<<"недостаточное количество операндов"<<endl;
return 0;
}
Ниже приведены варианты запуска программы и результаты её 4.cpp. Имя исполняемого файла ./4 (ОС Lnux)
./4 1.3 + 7.8 9.1 ./4 1.3 - 7.8 -6.5 ./4 1.3 / 7.8 0.166667 ./4 1.3 \* 7.8 10.14 ./4 1.3 % 7.8 неправильный знак операции ./4 1.3+ 7.8 недостаточное количество операндов
Разработать программу на языке C++ для следующих заданий:
Разработать программу на языке C++ для решения следующей задачи. Заданы два числа — $$A$$ и $$B$$, первое в системе счисления с основанием $$p$$, второе в системе счисления с основанием $$q$$. Вычислить значение $$C$$ по указанной формуле и вывести его на экран в десятичной системе счисления и системе счисления с основанием $$r$$. Исходные данные для решения задачи представлены в табл. 4.1.
| Вариант | p | q | C | r |
|---|---|---|---|---|
| 1 | 2 | 8 | $$A^{2}\cdot (A+B)$$ | 3 |
| 2 | 3 | 4 | $$2\cdot (A^{2}+B^{2})$$ | 4 |
| 3 | 4 | 6 | $$2\cdot B^{2}\cdot (A+B)$$ | 5 |
| 4 | 5 | 2 | $$(A-B)^{2}+3\cdot A$$ | 6 |
| 5 | 6 | 4 | $$A^{2}+A\cdot B$$ | 7 |
| 6 | 7 | 3 | $$(5\cdot B-2\cdot A)^{2}$$ | 8 |
| 7 | 8 | 2 | $$(2\cdot A-3\cdot B)^{2}$$ | 5 |
| 8 | 3 | 8 | $$(B-A)^{2}+2\cdot A$$ | 6 |
| 9 | 4 | 7 | $$B^{3}-B^{2}+2\cdot A$$ | 2 |
| 10 | 5 | 6 | $$A^{3}-A^{2}+3\cdot B$$ | 8 |
| 11 | 6 | 5 | $$(2\cdot A-3\cdot B)^{2}$$ | 3 |
| 12 | 7 | 4 | $$A^{2}+2\cdot A+B^{2}$$ | 5 |
| 13 | 8 | 3 | $$A^{2}+3\cdot B+B^{2}$$ | 7 |
| 14 | 4 | 2 | $$A^{2}-2\cdot A+B$$ | 6 |
| 15 | 5 | 8 | $$3\cdot B^{2}-2\cdot B+A$$ | 3 |
| 16 | 6 | 7 | $$A^{2}+(B-A)^{2}$$ | 2 |
| 17 | 7 | 6 | $$3\cdot B^{2}+2\cdot A\cdot B$$ | 8 |
| 18 | 8 | 5 | $$2\cdot A^{2}+3\cdot A\cdot B$$ | 7 |
| 19 | 2 | 4 | $$B^{3}-2\cdot B+A$$ | 3 |
| 20 | 3 | 8 | $$A^{3}-2\cdot A+B$$ | 4 |
| 21 | 4 | 7 | $$(5\cdot A-2\cdot B)^{2}$$ | 5 |
| 22 | 5 | 6 | $$(B^{2}-3\cdot A)^{2}$$ | 7 |
| 23 | 6 | 5 | $$(A^{2}-2\cdot B)^{2}$$ | 8 |
| 24 | 7 | 4 | $$A^{2}\cdot B^{2}-A\cdot B$$ | 6 |
| 25 | 8 | 3 | $$A\cdot B+A^{2}-B$$ | 2 |
Разработать программу на языке C++ для вычисления одного из корней уравнения $$f (x) = 0$$ методами, указанными в задании. Для решения задачи предварительно определить интервал изоляции корня графическим методом. Вычисления проводить с точностью $$\varepsilon=10^{-4}$$. Оценить степень точности путём подсчёта количества итераций, выполненных для достижения заданной точности. Исходные данные для решения задачи представлены в табл. 4.2.
| № | Уравнение f (x) = 0 | Методы решения |
|---|---|---|
| 1 | $$x-0.2\cdot \sin (x+0.5)=0$$ | метод половинного деления, метод хорд |
| 2 | $$x^2-\lg(x+2)=0$$ | метод касательных, метод простой итерации |
| 3 | $$x^2-20\cdot \sin (x)=0$$ | метод хорд, метод касательных |
| 4 | $$\ln (x)+(x+1)^3=0$$ | метод дихотомии, метод простой итерации |
| 5 | $$x^2-\sin(5x)=0$$ | метод половинного деления, метод касательных |
| 6 | $$e^x+x^2-2=0$$ | метод хорд, метод простой итерации |
| 7 | $$0.8\cdot x^{2}-\sin (10\cdot x)=0$$ | метод половинного деления, метод хорд |
| 8 | $$\sin (7\cdot x)+2\cdot x-6=0$$ | метод касательных, метод простой итерации |
| 9 | $$x\cdot \ln (x)-1=0$$ | метод хорд, метод касательных |
| 10 | $$2\cdot \lg(x)+0.5\cdot x=0$$ | метод дихотомии, метод простой итерации |
| 11 | $$e^{-x}-x^2=0$$ | метод половинного деления, метод касательных |
| 12 | $$x^2-3\cdot \cos (x^2)=0$$ | метод хорд, метод простой итерации |
| 13 | $$\sin (7\cdot x)-x^2+15=0$$ | метод половинного деления, метод хорд |
| 14 | $$(x-1)^2-0.5\cdot e^x=0$$ | метод касательных, метод простой итерации |
| 15 | $$2\cdot \ln (x)-0.2\cdot x+1=0$$ | метод хорд, метод касательных |
| 16 | $$2-x\cdot e^x=0$$ | метод дихотомии, метод простой итерации |
| 17 | $$0.1\cdot x^3+3\cdot x^2-10\cdot x-7=0$$ | метод половинного деления, метод касательных |
| 18 | $$0.1\cdot x^2-e^x=0$$ | метод хорд, метод простой итерации |
| 19 | $$e^{-2\cdot x}-2\cdot x+1=0$$ | метод половинного деления, метод хорд |
| 20 | $$x^2-3+0.5^x=0$$ | метод касательных, метод простой итерации |
| 21 | $$\lg(4\cdot x)-\cos (x)=0$$ | метод хорд, метод касательных |
| 22 | $$\ln(x)-\cos^2(x)=0$$ | метод дихотомии, метод простой итерации |
| 23 | $$\frac{4}{x}-0.2\cdot e^x=0$$ | метод половинного деления, метод касательных |
| 24 | $$\sqrt{x+6.5}-e^x=0$$ | метод хорд, метод простой итерации |
| 25 | $$0.5^x+1-(x-2)^2=0$$ | метод половинного деления, метод хорд |
В практике программирования часто складываются ситуации, когда одну и ту же группу операторов, реализующих определённую цель, требуется повторить без изменений в нескольких местах программы. Для избавления от столь нерациональной траты времени была предложена концепция подпрограммы.
Подпрограмма — именованная, логически законченная группа операторов языка, которую можно вызвать для выполнения любое количество раз из различных мест программы. В языке C++ подпрограммы реализованы в виде функций [4 ]
Функция — это поименованный набор описаний и операторов, выполняющих определённую задачу. Функция может принимать параметры и возвращать значение. Информация, передаваемая в функцию для обработки, называется параметром, а результат вычисления функции её значением. Обращение к функции называют вызовом. Как известно (п. 2.8), любая программа на C++ состоит из одной или нескольких функций. При запуске программы первой выполняется функция main. Если среди операторов функции main встречается вызов функции, то управление передаётся операторам функции. Когда все операторы функции будут выполнены, управление возвращается оператору, следующему за вызовом функции.
Перед вызовом функция должна быть обязательно описана. Описание функции состоит из заголовка и тела функции:
тип имя_функции(список_переменных)
{
тело_функции
}
Заголовок функции содержит:
void;имя_функции;список_переменных — перечень передаваемых в функцию величин (аргументов), которые отделяются друг от друга запятыми; для каждой переменной из списка указывается тип и имя; если функция не имеет аргументов, то в скобках указывают либо тип void, либо ничего.Тело функции представляет собой последовательность описаний и операторов, заключённых в фигурные скобки.
В общем виде структура программы на C++ может иметь вид:
директивы компилятора
тип имя_1(список_переменных)
{
тело_функции_1;
}
тип имя_2(список_переменных)
{
тело_функции_2;
}
...
тип имя_n(список_переменных)
{
тело_функции_n;
}
int main (список_переменных)
{
//Тело функции может содержать операторы вызова функций имя_1, имя_2, ..., имя_n
тело_основной_функции;
}
Однако допустима и другая форма записи программного кода :
директивы компилятора
тип имя_1(список_переменных);
тип имя_2(список_переменных);
...
тип имя_n(список_переменных);
int main (список_переменных)
{
//Тело функции может содержать операторы вызова функций имя_1, имя_2, ..., имя_n
тело_основной_функции;
}
тип имя_1(список_переменных)
{
тело_функции_1;
}
тип имя_2(список_переменных)
{
тело_функции_2;
}
...
тип имя_n(список_переменных)
{
тело_функции_n;
}
Здесь функции описаны после функции main(), однако до неё перечислены заголовки всех функций. Такого рода опережающие заголовки называют прототипами функций. Прототип указывает компилятору тип данных, возвращаемых функцией, тип переменных, выступающих в роли аргументов, и порядок их следования. Прототипы используются для проверки правильности вызова функций в основной программе. Имена переменных, указанные в прототипе функции, компилятор игнорирует:
//Записи равносильны. int func ( int a, int b ); int func ( int, int );
Вызвать функцию можно в любом месте программы. Для вызова функции необходимо указать её имя и в круглых скобках, через запятую перечислить имена или значения аргументов, если таковые имеются:
имя_функции(список_переменных);
Рассмотрим пример. Создадим функцию f(), которая не имеет входных значений и не формирует результат. При вызове этой функции на экран выводится строка символов "С Новым Годом, ".
#include <iostream>
using namespace std;
void f ( ) //Описание функции.
{
cout << "С Новым Годом, ";
}
int main ( )
{
f ( ); //Вызов функции.
cout <<"Студент!" << endl;
f ( ); //Вызов функции.
cout <<"Преподаватель!" << endl;
f ( ); //Вызов функции.
cout <<"Народ!" << endl;
}
Результатом работы программы будут три строки:
С Новым Годом, Студент! С Новым Годом, Преподаватель! С Новым Годом, Народ!
Далее приведён пример программы, которая пять раз выводит на экран фразу "Здравствуй, мир!". Операция вывода строки символов оформлена в виде функции fun(). Эта функция также не имеет входных значений и не формирует результат. Вызов функции осуществляется в цикле:
#include <iostream>
using namespace std;
void fun ( )
{
cout << "Здравствуй, мир!" << endl;
}
int main ( )
{
for ( int i =1; i <=5; fun ( ), i ++);
}
Если тип возвращаемого значения не void, то функция может входить в состав выражений. Типы и порядок следования переменных в определении и при вызове функции должны совпадать. Для того чтобы функция вернула какое-либо значение, в ней должен быть оператор:
return (выражение);
Далее приведён пример программы, которая вычисляет значение выражения $$\sin^2(\alpha)+\cos^2(\alpha)$$ при заданном значении α. Здесь функция radian выполняет перевод градусной меры угла в
#include <iostream>
#include <math.h>
#define PI 3.14159
using namespace std;
double radian ( int deg, int min, int sec )
{
return ( deg * PI/180+min* PI /180/60+ sec * PI /180/60/60);
}
int main ( )
{
int DEG, MIN, SEC; double RAD;
//Ввод данных.
cout<<" Input :"<<endl; //Величина угла:
cout<<" DEG ="; cin >>DEG; //градусы,
cout<<" MIN ="; cin >>MIN; //минуты,
cout<<" SEC ="; cin >>SEC; //секунды.
//Величина угла в радианах.
RAD=radian (DEG, MIN, SEC); //Вызов функции.
cout << " Value in radian A="<<RAD << endl;
//Вычисление значения выражения и его вывод.
cout << " sin (A) ^2+ cos (A) ^2= ";
cout << pow( sin (RAD), 2 )+pow( cos (RAD), 2 ) << endl;
return 0;
}
Переменные, описанные внутри функции, а также переменные из списка аргументов, являются локальными. Например, если программа содержит пять разных функций, в каждой из которых описана переменная $$N$$, то для C++ это пять различных переменных. Область действия локальной переменной не выходит за рамки функции. Значения локальных переменных между вызовами одной и той же функции не сохраняются.
Переменные, определённые до объявления всех функций и доступные всем функциям, называют глобальными. В функции глобальную переменную можно отличить, если не описана локальная переменная с теми же именем. Глобальные переменные применяют для передачи данных между функциями, но это затрудняет отладку программы. Для обмена данными между функциями используют параметры функций и значения, возвращаемые функциями.
Обмен информацией между вызываемой и вызывающей функциями осуществляется с помощью механизма передачи параметров.Список_переменных, указанный в заголовке функции, называется формальными параметрами или просто параметрами функции. Список_переменных в операторе вызова функции — это фактические параметры или аргументы.
Механизм передачи параметров обеспечивает замену формальных параметров фактическими параметрами и позволяет выполнять функцию с различными данными. Между фактическими параметрами в операторе вызова функции и формальными параметрами в заголовке функции устанавливается взаимно однозначное соответствие. Количество, типы и порядок следования формальных и фактических параметров должны совпадать.
Передача параметров выполняется следующим образом. Вычисляются выражения, стоящие на месте фактических параметров. В памяти выделяется место под формальные параметры в соответствии с их типами. Затем формальным параметрам присваиваются значения фактических. Выполняется проверка типов и при необходимости выполняется их преобразование.
Передача параметров в функцию может осуществляться по значению и по адресу.
При передаче данных по значению функция работает с копиями фактических параметров, и доступа к исходным значениям аргументов у неё нет. При передаче данных по адресу в функцию передаётся не переменная, а её адрес, и, следовательно, функция имеет доступ к ячейкам памяти, в которых хранятся значения аргументов. Таким образом, данные, переданные по значению, функция изменить не может, в отличие от данных, переданных по адресу.
Если требуется запретить изменение параметра внутри функции, используют модификатор const. Заголовок функции в общем виде будет выглядеть так:
тип имя_функции ( const тип_переменной* имя_переменной, ...)
Например:
#include <iostream>
using namespace std;
int f1 ( int i ) //Данные передаются по значению
{
return ( i ++);
}
int f2 ( int * j ) //Данные передаются по адресу. При подстановке фактического параметра,
//для получения его значения, применяется операция разадресации * .
{
return ( ( * j )++);
}
int f3 ( const int * k ) //Изменение параметра не предусмотрено .
{
return ( ( * k )++);
}
int main ( )
{
int a;
cout<<"a="; cin >>a;
f1( a );
cout<<"a="<<a<<"\n";
f2(a ); //Для передачи фактического параметра используется операция взятия адреса .
cout<<"a="<<a<<"\n";
f3(a );
cout<<"a="<<a<<"\n";
return 0;
}
Результат работы программы:
Введено значение переменной $$a$$.
a=5
Значение переменной a после вызова функции $$f1$$ не изменилось.
a=5
Значение переменной a после вызова функции $$f2$$ изменилось.
a=6
Значение переменной a после вызова функции $$f3$$ не изменилось.
a=6
Удобно использовать передачу данных по адресу, если нужно чтобы функция изменяла значения переменных в вызывающей программе.
Далее приведён пример программы, в которой исходя из радианной меры некоторого угла вычисляется величина смежного с ним угла. На экран выводятся значения углов в градусной мере. Функция degree выполняет перевод из радианной меры в rad, определяющее величину угла в радианах, и адреса переменных deg, min, sec, в которых будут храниться вычисленные результаты — градусная мера угла.
#include <iostream>
#include <math.h>
#define PI 3.14159
using namespace std;
void degree ( double rad, int * deg, int * min, int * sec )
{
* deg= floor ( rad * 180/ PI );
* min=floor ( ( rad * 180/ PI -(* deg ) ) * 60);
* sec=floor ( ( ( rad * 180/ PI -(* deg ) ) *60 -(* min ) ) * 60);
}
int main ( )
{
int DEG, MIN, SEC; double RAD;
cout<<" Inpout :"<<endl;
cout << " Value in radian A="; cin >>RAD;
degree (RAD,DEG,MIN,SEC);
cout << DEG<<" "<<MIN<<" "<<SEC << endl;
degree ( PI- RAD,DEG,MIN,SEC);
cout << DEG<<" "<<MIN<<" "<<SEC << endl;
return 0;
}
Возврат результата из функции в вызывающую её функцию осуществляется оператором return выражение;
Работает оператор следующим образом. Вычисляется значение выражения, указанного после return, и преобразуется к типу возвращаемого функцией значения. Выполнение функции завершается, а вычисленное значение передаётся в вызывающую функцию. Любые операторы, следующие в функции за оператором return, игнорируются. Программа продолжает свою работу с оператора, следующего за оператором вызова данной функции.
Оператор return может отсутствовать в функциях типа void, если возврат происходит перед закрывающейся фигурной скобкой, и в функции main.
Также функция может содержать несколько операторов return, если это определено потребностями алгоритма. Например, в следующей программе функция equation вычисляет корни квадратного уравнения. Если $$a = 0$$ (уравнение не является квадратным), то в программу передаётся значение равное -1, если дискриминант отрицательный (уравнение не имеет действительных корней), то 1, а если положительный, то вычисляются корни уравнения и в программу передаётся 0.
#include <iostream>
#include <math.h>
using namespace std;
int equation ( float a, float b, float c, float * x1, float * x2 )
{ float D=b*b-4*a*c;
if ( a==0) return -1;
else if (D<0) return 1;
else
{
*x1=(-b+sqrt (D) ) /2/ a;
*x2=(-b-sqrt (D) ) /2/ a;
return 0;
}
}
int main ( )
{
float A, B, C, X1, X2; int P;
cout<<" Enter the coefficients of the equation :"<<endl;
cout<<"A="; cin >>A;
cout<<"B="; cin >>B;
cout<<"C="; cin >>C;
P=equation ( A, B, C, X1, X2);
if (P==-1) cout<<" input Error "<<endl;
else if (P==1) cout<<" No real roots "<<endl;
else cout<<" X1="<<X1<<" X2="<<X2<<endl;
return 0;
}
Рассмотрим несколько задач с применением функций.
Задача 4.1. Вводится последовательность из $$N$$ целых чисел, найти среднее арифметическое совершённых чисел и среднее геометрическое простых чисел последовательности.
Напомним, что целое число называется простым, если оно делится нацело только на само себя и единицу. Подробно алгоритм определения простого числа описан в задаче 3.15 (рис. 3.29). В этой задаче кроме простых чисел фигурируют совершённые числа. Число называется совершённым, если сумма всех делителей, меньших его самого, равна этому числу. Алгоритм, с помощью которого можно определить делители числа, подробно рассмотрен в задаче 3.14 (рис. 3.28).
Для решения поставленной задачи понадобятся две функции:
prostoe — определяет, является ли число простым, аргумент функции целое число $$N$$; функция возвращает 1, если число простое и 0 — в противном случае.;soversh — определяет, является ли число совершённым; входной параметр целое число $$N$$; функция возвращает 1, если число является совершённым и 0 — в противном случае.
#include <iostream>
#include <math.h>
unsigned int prostoe ( unsigned int N) //Описание функции.
{
//Функция определяет, является ли число простым.
int i, pr;
for ( pr =1, i =2; i<=N/ 2; i ++)
if (N%i ==0) { pr =0; break; }
return pr;
}
unsigned int soversh ( unsigned int N) //Описание функции.
{
//Функция определяет, является ли число совершённым.
unsigned int i, S;
for ( S=0, i =1; i<=N/ 2; i ++)
if (N%i ==0) S+=i; //Сумма делителей.
if ( S==N) return 1;
else return 0;
}
using namespace std;
int main ( )
{
unsigned int i,N,X, S, kp, ks;
long int P;
cout <<"N="; cin >>N;
for ( kp=ks=S=0,P=1, i =1; i<=N; i ++)
{
cout <<"X="; cin >> X; //Вводится элемент последовательности.
if ( prostoe (X) ) // X — простое число.
{
kp++; //Счётчик простых чисел.
P*=X; //Произведение простых чисел.
}
if ( soversh (X) ) //X — совершённое число.
{
ks++; //Счётчик совершённых чисел.
S+=X; //Сумма совершённых чисел.
}
}
if ( kp>0) //Если счётчик простых чисел больше нуля,
//считаем среднее геометрическое и выводим его,
cout<<"Среднее геометрическое="<<pow(P, ( float ) 1/kp )<<endl;
else //в противном случае –– сообщение об отсутствии простых чисел.
cout<<"Нет простых чисел\n";
if ( ks>0) //Если счётчик совершённых чисел больше нуля,
//считаем среднее арифметическое и выводим его,
cout<<"Среднее арифметическое="<<(float ) S/ ks<<endl;
else //в противном случае — сообщение об отсутствии совершённых чисел.
cout<<"Нет совершённых чисел\n";
return 0;
}
Задача 4.2. Вводится последовательность целых чисел, 0 — конец последовательности. Найти минимальное число среди простых чисел и максимальное — среди чисел, не являющихся простыми.
Для решения данной задачи создадим функцию prostoe, которая будет проверять, является ли число $$N$$ простым. Входным параметром функции будет целое положительное число $$N$$. Функция будет возвращать значение 1, если число простое, и 0 — в противном случае. Алгоритм поиска максимума (минимума) подробно описан в задаче 3.20 (рис. 3.31).
Текст программы:
#include <iostream>
using namespace std;
unsigned int prostoe ( unsigned int N)
{
int i, pr;
for ( pr =1, i =2; i>=N/ 2; i ++)
if (N%i ==0) { pr =0; break; }
return pr;
}
int main ( )
{
int kp=0,knp=0,min, max,N;
for ( cout << "N=", cin >>N; N != 0; cout<<"N=", cin >>N)
//В цикле вводится элемент последовательности N.
if ( prostoe (N) ) //N — простое число,
{
kp++; //счётчик простых чисел.
if ( kp==1) min=N; //Предполагаем, что первое простое число минимально,
else if (N<min ) min=N; //если найдётся меньшее число, сохраняем его.
}
else //N — простым не является,
{
knp++; //счётчик чисел не являющихся простыми.
if ( knp==1) max=N; //Предполагаем, что первое не простое число максимально,
else if (N>max) max=N; //если найдётся большее число, сохраняем его.
}
if ( kp>0) //Если счётчик простых чисел больше нуля,
cout <<" min = "<<min<<"\t"; //выводим значение минимального простого числа,
else //в противном случае —
cout <<"Нет простых чисел"; //сообщение об отсутствии простых чисел.
if ( knp>0) //Если счётчик чисел не являющихся простыми больше нуля,
cout <<" max ="<<max<<endl; //выводим значение максимального числа,
else //в противном случае —
cout <<"Нет составных чисел"; //сообщение об отсутствии чисел
//не являющихся простыми.
return 0;
}
Задача 4.3. Вводится последовательность из $$N$$ целых положительных чисел. В каждом числе найти наименьшую и наибольшую
Программный код к задаче 4.3.
#include <iostream>
using namespace std;
unsigned int Cmax( unsigned long long int P)
{
unsigned int max;
if (P==0) max=0;
for ( int i =1; P!=0;P/=10)
{
if ( i ==1) {max=P%10; i ++;}
if (P%10>max) max=P%10;
}
return max;
}
unsigned int Cmin( unsigned long long int P)
{
unsigned int min;
if (P==0) min=0;
for ( int i =1; P!=0;P/=10)
{
if ( i ==1) {min=P%10; i ++;}
if (P%10<min ) min=P%10;
}
return min;
}
int main ( )
{
unsigned int N, k;
unsigned long long int X;
for ( cout<<"N=", cin >>N, k=1;k<=N; k++)
{
cout<<"X="; cin >>X;
cout<<"Максимальная цифра="<<Cmax(X);
cout<<" Минимальная цифра="<<Cmin(X)<<endl;
}
return 0;
}
Задача 4.4. Вводится последовательность целых положительных чисел, 0 — конец последовательности. Определить, сколько в последовательности
Алгоритм определения палиндрома подробно описан в задаче 3.19. Далее приведён программный код к задаче 4.4
#include <iostream>
using namespace std;
bool palindrom ( unsigned long long int N)
{ //Функция определяет, является ли число N палиндромом, возвращает true, если N —
//палиндром, и false в противном случае
unsigned long int M, R, S;
int kol, i;
for (R=1, kol =1,M=N;M/10>0; kol ++,R*=10,M/=10);
for ( S=0,M=N, i =1; i<=kol; S+=M%10*R,M/=10,R/=10, i ++);
if (N==S ) return true;
else return false;
}
int main ( )
{ unsigned long long int X;
int K;
for (K=0, cout<<"X=", cin >>X;X!=0; cout<<"X=", cin >>X)
if ( palindrom (X) ) K++;
cout<<"Количество палиндромов равно K="<<K<<endl;
return 0;
}
Задача 4.5. Заданы два числа — $$X$$ в двоичной системе счисления, $$Y$$ в системе счисления с основанием пять. Найти сумму этих чисел. Результат вывести в десятичной системе счисления.
Любое целое число $$N$$, заданное в $$b$$-ичной системе счисления, можно представить в десятичной системе счисления: $$N=P_n\cdot b^b+P_{n-1}\cdot b^{n-1}+...+P_2\cdot b^2+P_1\cdot b+P_0=\sum_{i=0}^n{P_i\cdot b^i},$$ где $$b$$ — основание системы счисления (целое положительное фиксированное число), $$P_i$$ — разряд числа: $$0\leqslant P_i\leqslant b-1,i=0,1,2,...,n.$$ Например,
$$743_{10}=7\cdot 10^2+4\cdot 10^1+3\cdot 10^0=700+40+3=743_{10};\\ 1011101_2=1\cdot 2^6+0\cdot 2^5+1\cdot 2^4+1\cdot 2^3+1\cdot 2^2+0\cdot 2^1+1\cdot 2^0=64+16+8+4+1=93_{10}.$$Создадим функцию для перевода целого числа $$N$$, заданного в $$b$$-ичной системе счисления, в десятичную систему счисления.
#include <iostream>
using namespace std;
unsigned long long int DecNC( unsigned long long int N, unsigned int b )
{
//Функция выполняет перевод числа N, заданного в b-ичной системе счисления,
//в десятичную систему счисления
unsigned long long int S, P;
for ( S=0,P=1;N!=0; S+=N%10*P, P*=b,N/=10);
return S;
}
int main ( )
{
unsigned long long int X,Y; unsigned int bX, bY;
cout<<"X="; cin >>X; //Ввод числа X.
cout<<"b="; cin >>bX; //Ввод основания с/с.
cout<<"Y="; cin >>Y; //Ввод числа X.
cout<<"b="; cin >>bY; //Ввод основания с/с.
//Вывод заданных чисел в десятичной с/с.
cout<<X<<"("<<bX<<")="<<DecNC(X, bX)<<" (10) "<<endl;
cout<<Y<<"("<<bY<<")="<<DecNC(Y, bY)<<" (10) "<<endl;
//Вычисление суммы и вывод результата.
cout<<X<<"("<<bX<<")+"<<Y<<"("<<bY<<")=";
cout<<DecNC(X, bX)+DecNC(Y, bY)<<" (10) "<<endl;
return 0;
}
Задача 4.6. Задано число $$X$$ в десятичной системе счисления. Выполнить перевод числа в системы счисления с основанием 2, 5 и 7.
Вообще, для того чтобы перевести целое число из десятичной системы счисления в другую, необходимо выполнить следующие действия:
На рис. 4.1 приведён пример "ручного" перевода числа 256, заданного в десятичной системе счисления, в восьмеричную. В результате получим $$256_{(10)}=400_{(8)}$$
(рис 4.1) Пример перевода числа в новую систему счисления
Далее приведён текст программы, реализующей решение задачи 4.6.
#include <iostream>
using namespace std;
unsigned long long int NC( unsigned long long int N, unsigned int b )
{
unsigned long long int S, P;
for ( S=0,P=1;N!=0; S+=N%b *P, P*=10,N/=b );
return S;
}
int main ( )
{
unsigned long long int X;
cout<<"X="; cin >>X; //Ввод числа X.
//Перевод числа X в заданные системы счисления.
cout<<X<<" (10) ="<<NC(X, 2 )<<" (2) "<<endl;
cout<<X<<" (10) ="<<NC(X, 5 )<<" (5) "<<endl;
cout<<X<<" (10) ="<<NC(X, 7 )<<" (7) "<<endl;
return 0;
}
Задача 4.7. Найти корни уравнения $$x^2-\cos (5\cdot x)=0$$.
Для решения задачи использовать:
Оценить степень точности предложенных численных методов, определив, за сколько итераций был найден корень уравнения. Вычисления проводить с точностью $$\varepsilon=10^{-3}$$.
Вообще говоря, аналитическое решение уравнения $$f(x) = 0$$ можно найти только для узкого класса функций. В большинстве случаев приходится решать уравнение (4.1) численными методами. Численное решение уравнения (4.1) проводят в два этапа: сначала необходимо отделить корни уравнения, т.е. найти достаточно тесные промежутки, в которых содержится только один корень, эти промежутки называют интервалами изоляции корня; на втором этапе проводят уточнение отделённых корней, т.е. находят корни с заданной точностью.
Интервал можно выделить, изобразив график функции, или каким-либо другим способом. Но все способы основаны на следующем свойстве непрерывной функции: если функция $$f (x)$$ непрерывна на интервале [$$a, b$$] и на его концах имеет различные знаки, $$f (a) \cdot f (b) < 0$$, то между точками имеется хотя бы один корень. Будем считать интервал настолько малым, что в нём находится только один корень. Рассмотрим самый простой способ уточнения корней.
Графическое решение задачи 4.7 показано на рис. 4.2. Так как функция $$f(x)=x^2-\cos (5\cdot x)$$ дважды пересекает ось абсцисс, можно сделать вывод о наличии в уравнении $$x^2-\cos (5\cdot x)=0$$ двух корней. Первый находится на интервале [-0.4; -0.2], второй принадлежит отрезку [0.2; 0.4] .
(рис 4.2) Геометрическое решение задачи 4.7
Рассмотрим предложенные в задаче численные методы решения нелинейных уравнений.
Метод половинного деления (дихотомии). Пусть был выбран интервал изоляции [$$a, b$$] (рис. 4.3). Примем за первое приближение корня точку c, которая является серединой отрезка [$$a, b$$].Далее будем действовать по следующему алгоритму:
Итак, для вычисления одного из корней уравнения $$ x^2-\cos (5\cdot x)=0$$ методом половинного деления достаточно знать интервал изоляции корня $$a=0.2;b=0.4$$ и точность вычисления $$\varepsilon=10^{-3}$$.
Блок-схема алгоритма решения уравнения методом дихотомии приведена на рис. 4.4. Понятно, что здесь $$c$$ — корень заданного уравнения.
Однако, несмотря на простоту, такое последовательное сужение интервала проводится редко, так как требует слишком большого количества вычислений.
(рис 4.3) Графическая интерпретация метода половинного деления
Кроме того, этот способ не всегда позволяет найти решение с заданной точностью. Рассмотрим другие способы уточнения корня. При применении этих способов будем требовать, чтобы функция $$f (x)$$ удовлетворяла следующим условиям на интервале [$$a, b$$] :
Метод хорд. Этот метод отличается от метода дихотомии тем, что очередное приближение берём не в середине отрезка, а в точке пересечения с осью $$X$$ (рис. 4.5) прямой, соединяющей точки ($$a, f (a)$$) и ($$b, f (b)$$).
Запишем уравнение прямой, проходящей через точки с координатами ($$a, f (a)$$) и ($$b, f (b)$$) : $$\frac{y-f(a)}{f(b)-f(a)}=\frac{x-a}{b-a},\ \ \ y=\frac{f(b)-f(a)}{b-a}\cdot (x-a)+f(a)$$
Прямая, заданная уравнением (4.2), пересекает ось $$X$$ при условии $$y = 0$$.
Найдём точку пересечения хорды с осью $$X$$: $$y=\frac{f(b)-f(a)}{b-a}\cdot (x-a)+f(a),\ \ x=a-\frac{f(a)\cdot (b-a)}{f(b)-f(a)}.$$ итак, $$c=a-\frac{f(a)}{f(b)-f(a)}(b-a)$$.
Далее необходимо вычислить значение функции в точке $$c$$. Это и будет приближённое значение корня уравнения.
(рис 4.4) Алгоритм решения уравнения методом дихотомии
Для вычисления одного из корней уравнения $$x^2-\cos (5\cdot x)=0$$ методом хорд достаточно знать интервал изоляции корня, например, $$a = 0.2; b = 0.4$$, и точность вычисления $$\varepsilon=10^{-3}$$. Блок-схема метода представлена на рис. 4.6.
Метод касательных (метод Ньютона). В одной из точек интервала [$$a; b$$], пусть это будет точка $$a$$, проведём касательную (рис. 4.7). Запишем уравнение этой прямой:$$y=k\cdot x+m$$
Так как эта прямая является касательной, и она проходит через точку $$(c,f(c))$$, то $$k=f'(c)$$.
Следовательно, $$y=f'(x)\cdot x+m,f(c)=f'(c)\cdot c+m,m=f(c)-c\cdot f'(c),\\y=f'(c)\cdot x+f(c)-c\cdot f'(c),y=f'(c)\cdot (x-c)+f(c)$$.
Найдём точку пересечения касательной с осью $$X$$: $$f'(c)\cdot (x-c)+f(c)=0,\ \ x=c-\frac{f(c)}{f'(c)}$$
(рис 4.5) Графическая интерпретация метода хорд
Если $$|f(x)|<\varepsilon$$, то точность достигнута, и точка $$x$$ — решение; иначе необходимо переменной c присвоить значение $$x$$ и провести касательную через новую точку $$c$$; так продолжать до тех пор, пока $$|f (x)|$$ не станет меньше $$\varepsilon$$. Осталось решить вопрос, что выбрать в качестве точки начального приближения $$c$$.
В этой точке должны совпадать знаки функции и её второй производной. А так как нами было сделано допущение, что вторая и первая производные не меняют знак, то можно проверить условие $$f(x)\cdot f''(x)>0$$ на обоих концах интервала, и в качестве начального приближения взять ту точку, где это условие выполняется.
Здесь, как и в предыдущих методах, для вычисления одного из корней уравнения $$x^2-\cos (5\cdot x)=0$$ достаточно знать интервал изоляции корня, например, $$a = 0.2; b = 0.4$$, и точность вычисления $$\varepsilon=10^{-3}$$. Блок-схема метода Ньютона представлена на рис. 4.8. Понятно, что для реализации этого алгоритма нужно найти первую и вторую производные функции $$f(x)=x^2-\cos (5\cdot x): f'(x)=2\cdot x+5\cdot \sin (5\cdot x), f(x) =2 +25 \cdot cos(5 \cdot x)$$.
Метод простой итерации. Для решения уравнения этим методом необходимо записать уравнение (4.1) в виде $$x=\phi(x)$$, задать начальное приближение $$x_0\in [a;b]$$ и организовать следующий итерационный вычислительный процесс: $$x_{k+1}=\phi(x_k),k=0,1,2,...$$
Вычисление прекратить, если $$|x_{k+1}-x_k|<\varepsilon$$ ($$\varepsilon$$ — точность).
Если неравенство $$|\phi'(x)|<1$$ выполняется на всём интервале [$$a; b$$], то последовательность $$x_0, x_1, x_2,...,x_n,... , ...$$ сходится к решению $$x^*$$ (т.е. l$$\lim\limits_{k\rightarrow \infty}x_k=x^*$$).
Значение функции $$\phi(x)$$ должно удовлетворять условию $$|\phi'(x)|<1$$ для того, чтобы можно было применить метод простых итераций. Условие $$|\phi'(x)|<1$$ является достаточным условием сходимости метода простой итерации.
(рис 4.6) Алгоритм метода хорд
Уравнение (4.1) можно привести к виду $$x=\phi(x)$$ следующим образом. Умножить обе части уравнения $$f (x) = 0$$ на число $$\lambda$$. К обеим частям уравнения $$\lambda\cdot f(x)=0$$ добавить число $$x$$. Получим $$x=x+\lambda\cdot f(x)$$. Это и есть уравнение вида $$x=\phi(x)$$, где
$$\phi(x)=x+\lambda\cdot f(x)$$Необходимо чтобы неравенство $$|\phi"(x)|<1$$ выполнялось на интервале [$$a; b$$], следовательно, $$|\phi"(x)|=|1+\lambda\cdot f'(x)|$$ и $$|1+\lambda\cdot f'(x)|<1 (|1+\lambda\cdot f'(a)|<1, |1+\lambda\cdotf'(b)|<1)$$, а значит, с помощью подбора параметра $$\lambda$$ можно добиться выполнения условия сходимости.
Для вычисления корней уравнения $$x^2-\cos (5\cdot x)=0$$ воспользуемся графическим решением (рис. 4.2) и определим интервал изоляции одного из корней, например, $$a = 0.2; b = 0.4$$. Подберём значение $$\lambda$$, решив неравенство $$|1+\lambda\cdot f'(x)|<1:|1+\lambda\cdot f'(a)|<1$$ и $$|1+\lambda\cdot f'(b)|<1,\\f(x)=x^{2}-\cos (5\cdot x),f'(x)=2\cdot x+5\cdot \sin (5\cdot x),\\f'(a)=2\cdot 0.2+5\cdot \sin (5\cdot 0.2)\approx 4.6, f'(b)=2\cdot 0.4+5\cdot \sin (5\cdot 0.4)\approx 5.35,\\|1+\lambda\cdot 4.6|<1$$ и $$|1+\lambda\cdot 5.35|<1$$. $$\left\{\begin{array}{l} \left\{\begin{array}{l} 1+4.6\cdot \lambda<1 \cr 1+4.6\cdot \lambda>-1 \end{array}\right.\cr \left\{\begin{array}{l} \lambda<0 \cr \lambda>-0.37 \end{array}\right. \end{array}\right. \Rightarrow \left\{\begin{array}{l} \left\{\begin{array}{l} \lambda<0 \cr \lambda>-0.4 \end{array}\right.\cr \left\{\begin{array}{l} \lambda<0 \cr \lambda>-0.37 \end{array}\right. \end{array}\right. \Rightarrow \left\{\begin{array}{l} \lambda\in (-0.4;0) \cr \lambda\in (-0.37;0) \end{array}\right.$$
и, следовательно, $$\lambda\in (-0.37;0)$$.
(рис 4.7) Графическая интерпретация метода касательных
Таким образом, исходными данными для программы будут начальное значение корня уравнения $$x_0=0.2$$, значение параметра $$\lambda$$ (пусть $$\lambda= -0.2$$), и точность вычислений $$\varepsilon=0.001$$.
Для вычисления второго корня заданного уравнения параметр $$\lambda$$ подбирают аналогично.
Блок-схема метода простой итерации приведена на рис. 4.9.
Далее представлен текст программы, реализующий решение задачи 4.7.
#include <iostream>
#include <math.h>
using namespace std;
//Функция, определяющая левую часть уравнения f (x) = 0.
double f ( double x )
{
return ( x *x-cos (5 * x ) );
}
//Функция, реализующая метод половинного деления.
int Dichotomy ( double a, double b, double * c, double eps )
{ int k=0;
do
{
* c=(a+b ) / 2;
if ( f ( * c ) * f ( a ) <0) b=*c;
else a=*c;
k++;
}
while ( fabs ( a-b )>=eps );
return k;
}
//Функция, реализующая метод хорд.
int Chord ( double a, double b, double * c, double eps )
{ int k=0;
do
{
* c=a-f ( a ) /( f ( b )-f ( a ) ) * ( b -a );
if ( f ( * c ) * f ( a ) >0) a=*c;
else b=*c;
k++;
}
while ( fabs ( f ( * c ) )>=eps );
return k;
}
double f1 ( double x ) //Первая производная функции f (x).
{
return (2 * x+5* sin (5 * x ) );
}
double f2 ( double x ) //Вторая производная функции f (x).
{
return (2+25* cos (5 * x ) );
}
//Функция, реализующая метод касательных.
int Tangent ( double a, double b, double * c, double eps )
{ int k=0;
if ( f ( a ) * f2 ( a ) >0) * c=a;
else * c=b;
do
{
* c=*c-f (* c ) / f1 ( * c );
k++;
}
while ( fabs ( f ( * c ) )>=eps );
return k;
}
double fi ( double x, double L) //Функция, заданная выражением 4.4 .
{
return ( x+L* f ( x ) );
}
//Функция, реализующая метод простой итерации.
int Iteration ( double *x, double L, double eps )
{ int k=0; double x0;
do
{
x0=*x;
*x= fi(x0, L);
k++;
}
while ( fabs ( x0-*x )>=eps );
return k;
}
int main ( )
{
double A, B, X, P;
double ep =0.001; //Точность вычислений.
int K;
cout<<"a="; cin >>A; //Интервал изоляции корня.
cout<<"b="; cin >>B;
cout<<"Решение уравнения x^2 - cos (5* x) =0. "<<endl;
cout<<"Метод дихотомии:"<<endl;
K=Dichotomy (A, B,X, ep );
cout<<"Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
cout<<"Метод хорд:"<<endl;
K=Chord (A, B,X, ep );
cout<<" Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
cout<<"Метод касательных:"<<endl;
K=Tangent (A, B,X, ep );
cout<<" Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
cout<<"Метод простой итерации:"<<endl;
X=A;
cout<<"L="; cin >>P;
K=Iteration (X, P, ep );
cout<<" Найденное решение x="<<X;
cout<<", количество итераций k="<<K<<endl;
return 0;
}
(рис 4.8) Алгоритм метода Ньютона
(рис 4.9) Алгоритм метода простой итерации
Результаты работы программы:
a=0.2 b=0.4 Решение уравнения x^2-cos(5*x)=0. Метод дихотомии: Найденное решение x=0.296094, количество итераций k=8 Метод хорд: Найденное решение x=0.296546, количество итераций k=2 Метод касательных: Найденное решение x=0.296556, количество итераций k=2 Метод простой итерации: L=-0.2 Найденное решение x=0.296595, количество итераций k=3
Под рекурсией в программировании понимают функцию, которая вызывает сама себя. Рекурсивные функции чаще всего используют для компактной реализации рекурсивных алгоритмов. Классическими рекурсивными алгоритмами могут быть возведение числа в целую положительную степень, вычисление факториала. С другой стороны, любой рекурсивный алгоритм можно реализовать без применения рекурсий. Достоинством рекурсии является компактная запись, а недостатком — расход памяти на повторные вызовы функций и передачу параметров, существует опасность переполнения памяти.
Рассмотрим применение рекурсии на примерах [7, 8].
Задача 4.8. Вычислить факториал числа $$n$$.
Вычисление факториала подробно рассмотрено в задаче 3.12 (рис. 3.25). Для решения этой задачи с применением рекурсии создадим функцию factoial, алгоритм которой представлен на рис. 4.10.
(рис 4.10) Рекурсивный алгоритм вычисления факториала
Текст программы с применением рекурсии:
#include <iostream>
using namespace std;
long int factorial ( int n )
{
if ( n<=1)
return n;
else
return n*factorial ( n-1);
}
int main ( )
{
int i; long int f;
cout<<"i="; cin >>i;
f=factorial ( i );
cout<<i <<" !="<<f<<"\n";
return 0;
}
Задача 4.9. Вычислить $$n$$-ю степень числа $$a$$ ($$n$$ — целое число).
Результатом возведения числа $$a$$ в целую степень $$n$$ является умножение этого числа на себя $$n$$ раз. Но это утверждение верно только для положительных значений $$n$$. Если $$n$$ принимает отрицательные значения, то $$a^{-n}=\frac{1}{a^n}$$.В случае $$n=0,\ \ a^0=1$$.
Для решения задачи создадим рекурсивную функцию stepen, алгоритм которой представлен на рис. 4.11.
(рис 4.11) Рекурсивный алгоритм вычисления степени числа
Текст программы с применением рекурсии:
#include <iostream>
using namespace std;
float stepen ( float a, int n )
{
if ( n==0)
return 1;
else if ( n<0)
return 1/ stepen ( a,-n );
else
return a * stepen ( a, n-1);
}
int main ( )
{
int i; float s, b;
cout<<"b="; cin >>b;
cout<<"i="; cin >>i;
s=stepen ( b, i );
cout<<"s="<<s<<"\n";
return 0;
}
Задача 4.10. Вычислить $$n$$-е число Фибоначчи.
Если нулевой элемент последовательности равен нулю, первый — единице, а каждый последующий представляет собой сумму двух предыдущих, то это последовательность чисел Фибоначчи (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... ).
Алгоритм рекурсивной функции fibonachi изображён на рис. 4.12.
(рис 4.12) Рекурсивный алгоритм вычисления числа Фибоначчи
Текст программы:
#include <iostream>
using namespace std;
long int fibonachi ( unsigned int n )
{
if ( ( n==0) | | ( n==1))
return n;
else
return fibonachi ( n -1)+fibonachi ( n-2);
}
int main ( )
{
int i; long int f;
cout<<"i="; cin >>i;
f=fibonachi ( i );
cout<<"f="<<f<<"\n";
return 0;
}
Язык C++ позволяет связать с одним и тем же именем функции различные определения, то есть возможно существование нескольких функций с одинаковым именем. У этих функций может быть разное количество параметров или разные типы параметров. Создание двух или более функций с одним и тем же именем называется перегрузкой имени функции. Перегруженные функции создают, когда одно и то же действие следует выполнить над разными типами входных данных.
В приведённом далее тексте программы три функции с именем Pow. Первая выполняет операцию возведения вещественного числа $$a$$ в дробную степень $$n=\frac{k}{m}$$, где $$k$$ и $$m$$ — целые числа. Вторая возводит вещественное число $$a$$ в целую степень $$n$$, а третья — целое число $$a$$ в целую Pow(a,k) вызовет вторую функцию, так как она имеет заголовок float Pow(float a, int n). Команда Pow((int)a,k) приведёт к вызову третьей функции float Pow(int a, int n), так как вещественная переменная $$a$$ преобразована к целому типу. Первая функция float Pow(float a, int k, int m) имеет три параметра, значит, обращение к ней осуществляется командой Pow(a,k,m).
#include <iostream>
using namespace std;
#include <math.h>
float Pow( float a, int k, int m) //Первая функция
{
cout<<"Функция 1 \t";
if ( a==0)
return 0;
else if ( k==0)
return 1;
else if ( a>0)
return exp ( ( float ) k/m *log ( a ) );
else if (m%2!=0)
return -(exp ( ( float ) k/m *log (-a ) ) );
}
float Pow( float a, int n ) //Вторая функция
{
float p; int i;
cout<<"Функция 2 \t";
if ( a==0)
return 0;
else if ( n==0)
return 1;
else if ( n<0)
{
n= -n;
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return ( float ) 1/p;
}
else
{
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return p;
}
}
float Pow( int a, int n ) //Третья функция
{
int i, p;
cout<<"Функция 3 \t";
if ( a==0)
return 0;
else if ( n==0)
return 1;
else if ( n<0)
{
n= -n;
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return ( float ) 1/p;
}
else
{
p=1;
for ( i =1; i<=n; i ++)
p*=a;
return p;
}
}
int main ( )
{
float a; int k, n,m;
cout<<"a="; cin >>a;
cout<<"k="; cin >>k;
cout<<"s="<<Pow( a, k )<<"\n"; //Вызов 2-й функции.
cout<<"s="<<Pow ( ( int ) a, k )<<"\n"; //Вызов 3-й функции.
cout<<"a="; cin >>a;
cout<<"k="; cin >>k;
cout<<"m="; cin >>m;
cout<<"s="<<Pow( a, k,m)<<endl; //Вызов 1-й функции.
return 0;
}
Результаты работы программы:
a=5.2 k=3 Функция 2 s=140.608 Функция 3 s=125 a=-8 k=1 m=1 Функция 1 s=-8 a=5.2 k=-3 Функция 2 s=0.00711197 Функция 3 s=0.008 a=-8 k=1 m=3 Функция 1 s=-2
Шаблон — это особый вид функции. С помощью шаблона функции можно определить алгоритм, который будет применяться к данным различных типов. Механизм работы шаблона заключается в том, что на этапе компиляции конкретный тип данных передаётся в функцию в виде параметра.
Простейшую функцию–шаблон в общем виде можно записать так [6]:
template <class Type> заголовок
{
тело функции
}
Обычно в угловых скобках указывают список используемых в функции типов данных. Каждый тип предваряется служебным словом class. В общем случае в списке могут быть не только типы данных, но и имена переменных.
Рассмотрим пример шаблона поиска наименьшего из четырёх чисел.
#include <iostream>
using namespace std;
//Определяем абстрактный тип данных с помощью служебного слова Type .
template <class Type>
Type minimum( Type a, Type b, Type c, Type d )
{ //Определяем функцию с использованием типа данных Type .
Type min=a;
if ( b<min ) min=b;
if ( c<min ) min=c;
if ( d<min ) min=d;
return min;
}
int main ( )
{
int ia, ib, ic, id, mini; float ra, rb, rc, rd, minr;
cout<<" Vvod 4 thelih chisla \t";
cin >>ia >>ib>>ic >>id;
mini=minimum( ia, ib, ic, id ); //Вызов функции minimum, в которую передаём
//4 целых значения.
cout<<"\n"<<mini<<"\n";
cout<<" Vvod 4 vecshestvenih chisla \t"; cin >>ra>>rb>>rc>>rd;
minr=minimum( ra, rb, rc, rd ); //Вызов функции minimum, в которую передаём
//4 вещественных значения.
cout<<"\n"<<minr<<"\n";
return 0;
}
Как известно (п. 2.8), по месту объявления переменные в языке C++ делятся на три класса: локальные, глобальные и переменные, описанные в списке формальных параметров функций. Все эти переменные имеют разную область видимости.
Локальные переменные объявляются внутри функции и доступны только в ней. О таких переменных говорят, что они имеют локальную видимость, то есть, видимы только внутри функции.
Глобальные переменные описывают вне всех функций. Поскольку они доступны из любой точки программы, то их область видимости охватывает весь файл.
Одно и тоже имя может использоваться при определении глобальной и локальной переменной. В этом случае в теле функции локальная переменная имеет преимущество и "закрывает" собой глобальную. Вне этой функции "работает" глобальное описание переменной.
Из функции, где действует локальное описание переменной, можно обратиться к глобальной переменной с таким же именем, используя оператор расширения области видимости
::переменная;
Рассмотрим пример:
#include <iostream>
using namespace std;
float pr =100.678; //Переменная pr определена глобально.
int prostoe ( int n )
{
int pr =1, i; //Переменная pr определена локально.
if ( n<0) pr =0;
else
for ( i =2; i<=n / 2; i ++)
if ( n%i ==0){ pr =0; break; }
cout<<" local pr="<<pr<<"\n"; //Вывод локальной переменной.
cout<<" global pr=" <<::pr<<"\n"; //Вывод глобальной переменной.
return pr;
}
int main ( )
{
int g;
cout<<"g="; cin >>g;
if ( prostoe ( g ) ) cout<<"g - prostoe \n";
else cout<<"g - ne prostoe \n";
return 0;
}
Результаты работы программы:
g=7 local pr=1 //Локальная переменная. global pr=100.678 //Глобальная переменная. g - prostoe
Итак, любая программа на C++ состоит из одной или нескольких функций, причём одна из них должна обязательно носить имя main (основной, главный ). Именно этой функции передаётся управление после запуска программы. Как любая функция, main может принимать параметры и возвращать значения. У функции main две формы записи:
тип main ( ) {тело функции },
тип main ( int argc, char *argv [ ] ) {тело функции }
.Первый параметр argc определяет количество параметров, передаваемых в функцию main из командной строки. Второй параметр argv — указатель на массив указателей типа char (массив строк). Каждый элемент массива ссылается на отдельный параметр командной строки. При стандартном запуске программы argc равно 1, argv — массив из одного элемента, этим элементом является имя запускаемого файла.
Рассмотрим следующую программу.
#include <iostream>
#include <stdlib.h>
using namespace std;
int main ( int argc, char * argv [ ] )
{
int i;
cout<<"В командной строке "<<argc<<" аргументов\n";
for ( i =0; i <argc; i ++)
cout<<"Аргумент № "<<i <<" "<<argv [ i ]<<endl;
return 0;
}
Текст программы хранится в файле 1.cpp. При стандартном запуске программа выведет следующую информацию:
В командной строке 1 аргументов Аргумент № 0 ./1
Программа выводит количество параметров командной строки и последовательно все параметры. При стандартном запуске – количество аргументов командной строки — 1, этим параметром является имя запускаемого файла (в нашем случае, имя запускаемого файла — ./1).
Запустим программу следующим образом:
./1 abc 34 6 + 90 Вася Маша
Результаты работы программы представлены ниже.
В командной строке 8 аргументов Аргумент № 0 ./1 Аргумент № 1 abc Аргумент № 2 34 Аргумент № 3 6 Аргумент № 4 + Аргумент № 5 90 Аргумент № 6 Вася Аргумент № 7 Маша
Рассмотрим приложение, в которое в качестве параметров командной строки передаётся число1, операция, число2. Функция выводит
число1 операция число2.
Текст программы приведён на atof преобразовывает строку символов в вещественное число, а если преобразование невозможно, то результатом функции atof будет число 0.0. Функция strcmp сравнивает две строки и возвращает 0 в случае совпадения строк. Подробнее об этих функциях можно прочесть в главе, посвящённой строкам.
#include <iostream>
#include <stdlib.h>
#include <cstring>
using namespace std;
int main ( int argc, char **argv )
{
//Если количество параметров больше или равно 4, то были введены два числа и знак операции.
if ( argc >=4)
//Если операция *, то выводим число1*число2.
{
if ( ! strcmp ( argv [ 2 ], "*" ) ) cout<<atof ( argv [ 1 ] ) * atof ( argv [ 3 ] )<<endl;
else
//Если операция +, то выводим число1+число2.
if ( ! strcmp ( argv [ 2 ], "+" ) ) cout<<atof ( argv [ 1 ] )+atof ( argv [ 3 ] )<<endl;
else
//Если операция -, то выводим число1-число2.
if ( ! strcmp ( argv [ 2 ], " -" ) ) cout<<atof ( argv [ 1 ] ) -atof ( argv [ 3 ] )<<endl;
else
//Если операция /, то выводим число1/число2.
if ( ! strcmp ( argv [ 2 ], "/" ) ) cout<<atof ( argv [ 1 ] ) / atof ( argv [ 3 ] )<<endl;
else cout<<"неправильный знак операции"<<endl;
}
else
cout<<"недостаточное количество операндов"<<endl;
return 0;
}
Ниже приведены варианты запуска программы и результаты её 4.cpp. Имя исполняемого файла ./4 (ОС Lnux)
./4 1.3 + 7.8 9.1 ./4 1.3 - 7.8 -6.5 ./4 1.3 / 7.8 0.166667 ./4 1.3 \* 7.8 10.14 ./4 1.3 % 7.8 неправильный знак операции ./4 1.3+ 7.8 недостаточное количество операндов
Разработать программу на языке C++ для следующих заданий:
Разработать программу на языке C++ для решения следующей задачи. Заданы два числа — $$A$$ и $$B$$, первое в системе счисления с основанием $$p$$, второе в системе счисления с основанием $$q$$. Вычислить значение $$C$$ по указанной формуле и вывести его на экран в десятичной системе счисления и системе счисления с основанием $$r$$. Исходные данные для решения задачи представлены в табл. 4.1.
| Вариант | p | q | C | r |
|---|---|---|---|---|
| 1 | 2 | 8 | $$A^{2}\cdot (A+B)$$ | 3 |
| 2 | 3 | 4 | $$2\cdot (A^{2}+B^{2})$$ | 4 |
| 3 | 4 | 6 | $$2\cdot B^{2}\cdot (A+B)$$ | 5 |
| 4 | 5 | 2 | $$(A-B)^{2}+3\cdot A$$ | 6 |
| 5 | 6 | 4 | $$A^{2}+A\cdot B$$ | 7 |
| 6 | 7 | 3 | $$(5\cdot B-2\cdot A)^{2}$$ | 8 |
| 7 | 8 | 2 | $$(2\cdot A-3\cdot B)^{2}$$ | 5 |
| 8 | 3 | 8 | $$(B-A)^{2}+2\cdot A$$ | 6 |
| 9 | 4 | 7 | $$B^{3}-B^{2}+2\cdot A$$ | 2 |
| 10 | 5 | 6 | $$A^{3}-A^{2}+3\cdot B$$ | 8 |
| 11 | 6 | 5 | $$(2\cdot A-3\cdot B)^{2}$$ | 3 |
| 12 | 7 | 4 | $$A^{2}+2\cdot A+B^{2}$$ | 5 |
| 13 | 8 | 3 | $$A^{2}+3\cdot B+B^{2}$$ | 7 |
| 14 | 4 | 2 | $$A^{2}-2\cdot A+B$$ | 6 |
| 15 | 5 | 8 | $$3\cdot B^{2}-2\cdot B+A$$ | 3 |
| 16 | 6 | 7 | $$A^{2}+(B-A)^{2}$$ | 2 |
| 17 | 7 | 6 | $$3\cdot B^{2}+2\cdot A\cdot B$$ | 8 |
| 18 | 8 | 5 | $$2\cdot A^{2}+3\cdot A\cdot B$$ | 7 |
| 19 | 2 | 4 | $$B^{3}-2\cdot B+A$$ | 3 |
| 20 | 3 | 8 | $$A^{3}-2\cdot A+B$$ | 4 |
| 21 | 4 | 7 | $$(5\cdot A-2\cdot B)^{2}$$ | 5 |
| 22 | 5 | 6 | $$(B^{2}-3\cdot A)^{2}$$ | 7 |
| 23 | 6 | 5 | $$(A^{2}-2\cdot B)^{2}$$ | 8 |
| 24 | 7 | 4 | $$A^{2}\cdot B^{2}-A\cdot B$$ | 6 |
| 25 | 8 | 3 | $$A\cdot B+A^{2}-B$$ | 2 |
Разработать программу на языке C++ для вычисления одного из корней уравнения $$f (x) = 0$$ методами, указанными в задании. Для решения задачи предварительно определить интервал изоляции корня графическим методом. Вычисления проводить с точностью $$\varepsilon=10^{-4}$$. Оценить степень точности путём подсчёта количества итераций, выполненных для достижения заданной точности. Исходные данные для решения задачи представлены в табл. 4.2.
| № | Уравнение f (x) = 0 | Методы решения |
|---|---|---|
| 1 | $$x-0.2\cdot \sin (x+0.5)=0$$ | метод половинного деления, метод хорд |
| 2 | $$x^2-\lg(x+2)=0$$ | метод касательных, метод простой итерации |
| 3 | $$x^2-20\cdot \sin (x)=0$$ | метод хорд, метод касательных |
| 4 | $$\ln (x)+(x+1)^3=0$$ | метод дихотомии, метод простой итерации |
| 5 | $$x^2-\sin(5x)=0$$ | метод половинного деления, метод касательных |
| 6 | $$e^x+x^2-2=0$$ | метод хорд, метод простой итерации |
| 7 | $$0.8\cdot x^{2}-\sin (10\cdot x)=0$$ | метод половинного деления, метод хорд |
| 8 | $$\sin (7\cdot x)+2\cdot x-6=0$$ | метод касательных, метод простой итерации |
| 9 | $$x\cdot \ln (x)-1=0$$ | метод хорд, метод касательных |
| 10 | $$2\cdot \lg(x)+0.5\cdot x=0$$ | метод дихотомии, метод простой итерации |
| 11 | $$e^{-x}-x^2=0$$ | метод половинного деления, метод касательных |
| 12 | $$x^2-3\cdot \cos (x^2)=0$$ | метод хорд, метод простой итерации |
| 13 | $$\sin (7\cdot x)-x^2+15=0$$ | метод половинного деления, метод хорд |
| 14 | $$(x-1)^2-0.5\cdot e^x=0$$ | метод касательных, метод простой итерации |
| 15 | $$2\cdot \ln (x)-0.2\cdot x+1=0$$ | метод хорд, метод касательных |
| 16 | $$2-x\cdot e^x=0$$ | метод дихотомии, метод простой итерации |
| 17 | $$0.1\cdot x^3+3\cdot x^2-10\cdot x-7=0$$ | метод половинного деления, метод касательных |
| 18 | $$0.1\cdot x^2-e^x=0$$ | метод хорд, метод простой итерации |
| 19 | $$e^{-2\cdot x}-2\cdot x+1=0$$ | метод половинного деления, метод хорд |
| 20 | $$x^2-3+0.5^x=0$$ | метод касательных, метод простой итерации |
| 21 | $$\lg(4\cdot x)-\cos (x)=0$$ | метод хорд, метод касательных |
| 22 | $$\ln(x)-\cos^2(x)=0$$ | метод дихотомии, метод простой итерации |
| 23 | $$\frac{4}{x}-0.2\cdot e^x=0$$ | метод половинного деления, метод касательных |
| 24 | $$\sqrt{x+6.5}-e^x=0$$ | метод хорд, метод простой итерации |
| 25 | $$0.5^x+1-(x-2)^2=0$$ | метод половинного деления, метод хорд |
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.