Вычислите с точностью [latex]\varepsilon[/latex] значение функции [latex]f(x) = \arctan x[/latex]. При вычислениях допустимо использовать только арифметические операции.
Тесты
Входные данные | Выходные данные | |||
№ | Точность | Аргумент | [latex]\arctan x[/latex] | Погрешность |
1 | 1 | 0.5 | 0.5 | 0.0363524 |
2 | 10 | 0.82 | 0.669293 | 0.0175249 |
3 | 100 | 0.77 | 0.652823 | 0.00335552 |
4 | 1000 | 1 | 0.666667 | 0.118731 |
Код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <iostream> #include <cmath> using namespace std; double myAtan(double x, int n) { double a = x; double sum = a; double b = a; double E = 1 / n; for(int i = 1; a > E; i++){ b *= - x * x; a *= b / (2 * i + 1); sum += a; } return sum; } int main() { double x; int n; cin >> x >> n; cout << "my arctg: " << myAtan(x, n) << endl; cout << "arctg: " << atan(x) << endl; cout << "|arctg - my arctg| = " << abs(myAtan(x, n) - atan(x)) << endl; return 0; } |
Для запроса на выполнение нажать здесь.
Решение
Для того, чтобы найти приблизительное значение [latex]\arctan x[/latex] возпользуемся формулой ряда Тейлора: [latex]\arctan x = x — \frac{x^3}{3} + \frac{x^5}{5} -[/latex] … [latex]= \sum_{n=0}^{\infty}\frac{(-1)^n}{2n+1}\cdot x^{2n+1}[/latex] для всех [latex]|x| \le 1[/latex]. Представим каждый член суммы как [latex]a_n = \frac{(-1)^n\cdot a^{2n+1}}{2n+1}[/latex], и будем рекурсивно вычислять числитель данной дроби [latex]m_n = m_{n-1} \cdot (- a^2)[/latex]. Тогда, будем находить значение очередного члена ряда по формуле [latex]a_n = \frac{m_n}{2n + 1}[/latex], пока [latex]a_n > \frac{1}{\varepsilon}[/latex].
— Нужно так \arctan x, а не arctan x.
— Сделайте правильные отступы.
— Почему double n? Это же количество повторов.
— Нужно накапливать частичные суммы ряда, пока очередное слагаемое по модулю больше чем эпсилон.
Спасибо, Игорь Евгеньевич, я исправила программу.
Хорошо. Теперь гораздо легче читать структуру программы.
Осталась проблема с реализацией. Вы правильно нашли рекурсивную формулу для вычисление очередного члена ряда по предыдущему. Но в Вашем случае рекурсивно следует вычислять только числитель (там, где степени). Знаменатель [latex]2i+1[/latex], Вы уже сосчитали рекурсивно, когда написали i++.
Что нужно сделать? Заведите переменную для знаменателя и меняйте её по своей рекурсивной формуле — b *= -x*x;. А значение очередного члена ряда находите простым делением a = b / (2 * n + 1);.
А почему нельзя так как делаете Вы? Т.е. на каждом шаге умножать на (2 * i - 1)) / (2 * i + 1). С математической точки зрения можно — числа в числителе и знаменателе будут сокращаться и получиться то, что нужно. Однако при компьютерных вычислениях возникает вычислительная погрешность связанная с хранением действительных чисел в виде конечной двоичной дроби. Это может приводить к накоплению погрешности и другим неприятным явлениям типа переполнения или исчезновения порядка.
Спасибо, Игорь Евгеньевич, я завела переменную для знаменателя, погрешность стала меньше.
Да? Не думал, что это будет прямо заметно.
Но, хорошо.
Вы учли замечание, но только в программе. Описание алгоритма теперь не соответствует коду.
Исправила.