Лекция 3 1. Математические функции языка С++ Чтобы использовать основные математические функции, необходимо подключить библиотеку math.h, а также определить константу #define _USE_MATH_DEFINES #include <cmath> . Основные базовые функции библиотеки: abs модуль числа acos арккосинус asin арксинус atan арктангенс ceil округление до ближайшего большего целого числа cos косинус random выводит случайное число от 0 до аргумента функции. exp вычисление экспоненты fabs абсолютная величина (числа с плавающей точкой) floor округление до ближайшего меньшего целого числа log натуральный логарифм log10 логарифм по основанию 10 pow(x,y) результат возведения x в степень y, xy sin синус sqrt квадратный корень tan тангенс Эта же библиотека содержит математические константы M_E – экспонента M_PI – ПИ 2. Операторы ветвления и варианта Для организации вычислений в зависимости от какого-либо условия в C++ предусмотрен условный оператор if, который в общем виде записывается следующим образом: if (условие) оператор_1; else оператор_2; Здесь условие - это логическое выражение, переменная или константа. Работает условный оператор следующим образом. Сначала вычисляется значение выражения, записанного в виде условия. Если оно имеет значение истина (true), выполняется оператор_1. В противном случае (значение ложное (false) ) оператор_2. Например, чтобы сравнить значения переменных a и b нужно написать следующую часть программного кода: int a, b; cin>>a; cin>>b; if (a==b) cout<<"a равно b"; else cout<<"a не равно b"; Если в задаче требуется, чтобы в зависимости от значения условия выполнялся не один оператор, а несколько, их необходимо заключить в фигурные скобки, как составной оператор. if (условие) { оператор_1; оператор_2; … } else { оператор_1; оператор_2; … } Альтернативная ветвь else в условном операторе может отсутствовать, если в ней нет необходимости. Оператор варианта switch необходим в тех случаях, когда в зависимости от значений переменной надо выполнить те или иные операторы: switch (выражение) { case значение_1: операторы_1; case значение_2: операторы_2; case значение_3: операторы_3; … case значение_n: операторы_n; default: операторы; break; } break; break; break; break; Оператор работает следующим образом. Вычисляется значение выражения. Затем выполняются операторы, помеченные значением, совпадающим со значением выражения. То есть если, выражение принимает значение_1, то выполняются операторы_1 и т. д. Если выражение не принимает ни одного из значений, то выполняются операторы, расположенные после слова default. Ветвь default может отсутствовать. Оператор break необходим для того, чтобы осуществить выход из операторы switch. Если он не указан, то будут выполняться следующие операторы из списка, несмотря на то, что значение, которым они помечены, не совпадает со значением выражения. Задача Необходимо вывести на название дня недели, соответствующее заданному числу D, при условии, что в месяце 31 день и 1-е число — понедельник. Для решения задачи воспользуемся операцией %, позволяющей вычислить остаток от деления двух чисел. Программа будет выводить название дня недели в зависимости от заданного нами числа. #include <iostream> using namespace std; void main () { unsigned int D, R; //описаны целые положительные числа cout<<"D="; cin>>D; R=D%7; switch (R) { case case case case case case case } } 1: 2: 3: 4: 5: 6: 0: cout<<"Monday \n"; break; cout<<"Theusday \n"; break; cout<<"Wednesday \n"; break; cout<<"Thursday \n"; break; cout<<"Friday \n"; break; cout<<"Saturday \n"; break; cout<<"Sunday \n"; break; 3. Оператор цикла с параметром Многократно повторяющийся участок программы называется циклом. Одно повторение цикла называется итерацией. Операторы, выполняющиеся в цикле, называется телом цикла. В языке С++ существует три типа циклов: 1) цикл с параметром; 2) цикл с предусловием; 3) цикл с постусловием. Если цикл с параметром представить в общем виде, то он будет иметь следующий формат: for (инициализация; выражение; модификации) оператор; Инициализация – это действие, когда мы переменной присваиваем начальное значение, т.е. значение с которого наш цикл начинает работать. В скобках, где написано слово инициализация, можно задать тип переменной, которую будем инициализировать. Можно инициализировать сразу несколько переменных. В этом случае их надо перечислить через запятую. Выражение определяет условие выполнения цикла: если его результат, приведенный к типу bool, равен true, цикл выполняется. Модификация – это действие, которое осуществляется в процессе работы цикла. У нас имеется начальное значение, называемое инициализацией, и конечное значение, называемое выражением, а правило, по которому мы задаем, чтобы из начального условия прийти в конечное, называется модификацией. В части модификаций тоже можно написать несколько операторов через запятую. Любая из частей может быть пропущена, но точки с запятой должны стоять на своих местах. Оператор является телом цикла, т.е. одно действие, но с помощью цикла оно выполняется столько, сколько указано в цикле. Соответственно, если мы хотим задать группу операторов, то они помещаются в фигурные скобки. Пример. Посчитать сумму чисел от 1 до 10. #include <stdio.h> #include <iostream> using namespace std; void main(){ int i,s=0,n; cout <<"n="; cin >>n; for (i=1;i<=n;i++) s=s+i; cout <<"s="<< s<<endl; } Пример. Вычислить сумму ряда S=1*2+2*4+3*8+4*16+…+n*2n #include <iostream> using namespace std; void main() { int i,j,n,s; cin >>n; for (i=1,j=2,s=0;i<=n;i++,j=j*2) s=s+i*j; cout <<s; } 4. Нахождение суммы ряда Числовым рядом называется бесконечная сумма S некоторой последовательности. S = a1+a2+a3+…+ai+… = a i 1 i С помощью разложения в числовой ряд можно вычислить многие из элементарных функций, например x3 x5 x7 x9 sin x x ... , 3! 5! 7! 9! x 2 x 4 x 6 x8 cos x 1 ... при 0 x ; 2! 4! 6! 8! 4 2 3 4 5 x x x x e x 1 x ... при x 1 . 2! 3! 4! 5! Основная проблема при вычислении суммы числового ряда состоит в вычислении очередного члена последовательности. Можно выделить три подхода к решению этой проблемы: 1) использование формулы общего члена ряда; 2) использование рекуррентного соотношения; 3) смешанный подход, основанный на двух предыдущих. Использование формулы общего члена ряда Формула общего члена ряда это соотношение, с помощью которого можно вычислить очередной член ряда, используя его номер. Так для ряда S = 2+4+6+8+10+… a1=2=2∙1, a2=4=2∙2, a3=6=2∙3, … , и, соответственно, формула общего члена: ai=2∙i, где i=1, 2, 3,… . Данный метод для вычисления очередного члена ряда используется лишь в том случае, если формула является легко вычислимой. Пример 2.1. Найти сумму ряда для первых N слагаемых. 1 1 1 S 1 ... ... . 2 3 n Формула общего члена ряда: 1 ai= , i=1, 2, 3,… i Поскольку эта формула легко вычислима, используем ее при составлении алгоритма. #include <iostream> using namespace std; void main() { int i,n; float s; cin >>n; for (i=1,s=0;i<=n;i++) s=s+1/float(i); cout <<s; } Использование рекуррентного соотношения Понятие рекуррентной последовательности в курсе математики вводится так: пусть известно k чисел: а1, …, ak. Эти числа являются началом числовой последовательности. Следующие элементы этой последовательности вычисляются так: ak+1 = F(a1, …, ak); ak+2 = F(a2, …, ak+1); ak+3 = F(a3, …, ak+2) … Здесь F(…) – функция от k аргументов. Формула вида ai = F(ai-k, …, ai-1) называется рекуррентной формулой. Другими словами, рекуррентная последовательность – это бесконечный ряд чисел, каждое из которых, за исключением k начальных, вычисляется через предыдущие члены ряда. Например, арифметическая и геометрическая прогрессии: 1, если i 1, . ai 1 2, если i 1 a1=1, a2=3, a3=5,… Рекуррентная формула: ai 1, если i 1, . ai 1 2, если i 1 a1=1, a2=2, a3=4, … Рекуррентная формула: ai Глубина рекурсии в обоих случаях равна 1 (такую зависимость называют одношаговой рекурсией). Числа Фибоначчи: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, … Начиная с третьего элемента, каждое число равно сумме двух предыдущих, т.е. это рекуррентная последовательность с глубиной, равной 2. Рекуррентная формула для чисел последовательности Фибоначчи: 1, если i 1, i 2, fi . f i 1 f i 2 , если i 2 Для вывода рекуррентной формулы часто можно воспользоваться соотношением вида: ai f (i ) . ai 1 Получив f(i), мы по сути найдем основную часть рекуррентного соотношения, так как ai ai1 f (i) . Правда, при этом нельзя забывать о начальном условии, без которого рекуррентная формула не будет полной. x 2 x3 ... . Определим рекуррентное соотношение для ряда: S 1 x 2! 3! x0 x1 xi x i 1 1; a1 x; ai ; ai 1 Положим a0 . Тогда 0! 1! i! (i 1)! ai x i (i 1)! x f (i ) . ai1 x i1 i! i 1, если i 0 Получаем рекуррентное соотношение: ai . x ai 1 , если i 0 i Рекуррентное соотношение используется в тех случаях, когда следующий член ряда можно выразить через предыдущий (или предыдущие), а использование формулы общего члена приведет к появлению вложенных циклов, что сделает программу неэффективной. Пример 2.2. Написать программу вычисления суммы N первых членов ряда x 2 x 4 x 6 x8 ... . Выпишем члены ряда 2! 4! 6! 8! x2 x4 x 2i x 2(i 1) a0 1, a1 , a2 , ai (1) i , ai 1 (1) i 1 . 2! 4! (2i)! (2 (i 1))! S 1 Поскольку каждый член ряда может быть получен из предыдущего домножением на определенный множитель, а вычисление «в лоб» каждого члена ряда приведет к появлению вложенных циклов, эффективнее будет использовать рекуррентное соотношение. Выведем его ai x 2i (1) i (2 (i 1))! x2 2(i 1) ai 1 (2i 1) (2i) x (1) i 1 (2i)! 1, если i 0 x2 Полученное рекуррентное соотношение ai . , если i 0 a i 1 (2i 1) (2i ) #include <iostream> #include <iomanip> using namespace std; void main() { int i,n; double x,a,s,b; cin >>x>>n; s=1; a=1; for (i=1;i<n;i++) { a=-a*x*x/(2*i-1)/2/i; s=s+a; } cout <<setprecision(5)<<fixed<<s<<endl; } Одновременное использование формулы общего члена и рекуррентного соотношения Оба рассмотренных выше способа: использование формулы общего члена и построение рекуррентного соотношения не исчерпывают всего многообразия числовых рядов. Может возникать ситуация, когда формула общего члена не является эффективно вычислимой и при этом невозможно построить рекуррентное соотношение. Часто такая ситуация находит свое разрешение, если каждый член ряда представить в виде произведения ai=bi∙ci и отдельно рассмотреть последовательности из bi и ci. При этом одна из последовательностей будет эффективно вычислима через общий член ряда, а другая через рекуррентное соотношение. i x x2 x3 S x ... . Формула общего члена аi , i 1, 2, 3, ... . Рассмотрим ряд 2 3 i 1 Представим ai=bi∙ci , bi=xi, с i . Последовательность из bi выражается рекуррентным i 1, i 0 соотношением bi , а последовательность из ci эффективно вычислима через bi 1 x, i 0 формулу общего члена. Если все три рассмотренных способа не позволят получить эффективное вычисление очередного члена ряда, тогда необходимо будет использовать механизм вложенных циклов.