Задача «Вычисление sin(x)». Пример решения

Постановка задачи. Вычислить значение тригонометрической функции sin(x) от произвольного значения аргумента x.

Вариант решения 1. Поищем готовый вариант решения. Тригонометрия – раздел математики. Предположим, что в библиотеке System реализован класс, связанный с математическими функциями (в библиотеках прежних языков всегда была функция извлечения квадратного корня из числа – sqrt()). Как же узнать название класса?

Воспользуемся интеллектуальной подсказкой. Внутри метода Main() консольного приложения наберем одну букву M в английской раскладке. В выпадающем меню выберем класс Math (по-английски математика – Mathematics или maths). Наведя мышкой на это слово, прочитаем:
«class System.Math. Предоставляет константы и статические методы для тригонометрических, логарифмических и иных общих математических функций».
Мы почти у цели.

После слова Math ставим точку и снова выбираем что-либо, например метод Sin. Прочтем подсказку:
« double Math.Sin(double a).  Возвращает синус указанного угла».
Выбрав Sin, откроем скобку « ( ». Снова  появится подсказка:
«double Math.Sin(double a) Возвращает синус указанного угла. a: —  Угол, измеряемый в радианах)».
Итак, если нам необходимо вычислить y=sin(x), где переменные  x  и  y должны быть объявлены типом double, то это можно сделать одним оператором:
 y = Math.Sin(x);

Если вы обратили внимание на третью подсказку, то вы знаете, или хотите узнать, что за единица измерения угла 1 радиан. В школе чаще всего вас приучили к измерению углов в градусах (прямой угол — 90°, развернутый — 180°, и т.д.), а при необходимости еще и в минутах и секундах.
Разработчики библиотеки не стали вводить еще один новый тип данных («градусный»), но обеспечили возможность для перевода градусов в радианы, добавив константу – число π (double Math.PI).
По определению, 1 радиан — центральный угол, длина дуги которого равна радиусу окружности, 2π радиан = 360°. Вспомните, что длина окружности радиусом R равна 2πR.
Составив пропорцию, вы сможете переводить угол a, заданный в градусах, в угол x, заданный в радианах:   x = a· π /180.
Проверка:     a=360°,  x=2π;     a=30°,  x=0,523598775598299.

Программа, реализующая вычисление sin(a), угол а задан в градусах, представлена ниже:

using System;
namespace sin_x_ЧерезРяд
{
   class Program
   {
      static void Main(string[] args)
      {
         Console.Write("Вычисление sin(x)\nУгол в градусах = ");
         double a = Convert.ToDouble(Console.ReadLine());
         double x, y;
         x = a * Math.PI / 180;
         Console.WriteLine("Угол в радианах = {0}",x);
         y = Math.Sin(x);
         Console.WriteLine("sin({0})={1}",x,y);
         Console.ReadKey();
      }
   }
}

Результат выполнения программы:
2193

Вариант решения 2.

Понятно, что метод Sin( ) есть некоторая подпрограмма класса Math, которая вызывается по имени. Однако может быть вам будет интересно узнать, как она устроена. Для этого мы дополним наш проект методом Sin2(x), в котором самостоятельно реализуем некоторый алгоритм вычисления sin(x). Тогда библиотечную функцию Sin(x) мы сможем использовать для задания ожидаемого результата при тестировании, а обсуждая алгоритм вычисления Sin2(x), мы освоим элементы языка C# и приобретем некоторые навыки программирования.

Заглянем в справочник по высшей математике, раздел «Ряды». Из него мы узнаем, что функция sin(x) может быть представлена бесконечным рядом:
sin(x)_ряд
В теории, вы скажете все красиво, но бесконечный ряд – это бесконечное время вычислений, кроме того, возможно переполнение при возведении в степень и вычислении знаменателя.

Справка. Через n! обозначается функция ФАКТОРИАЛ — произведение целых чисел от 1 до n. По определению 0!=1. Например, 3!=1·2·3;  7!=1·2·…·6·7.

Отметим, что ряд знакопеременный: + , — , + , — , …
Предположим, что |x|<=1. Тогда отношение двух соседних членов ряда rn+2 и  rn будет равно:
|rn+2 /  rn | = x2 / ((n+2)(n+1)) <= 1/((n+2)(n+1).

То есть каждый следующий член ряда будет вносить в сумму все меньший и меньший вклад, поэтому можно использовать столько членов ряда, сколько нужно для достижения заданной точности вычислений. Например, если n=9, то | r11 / r9 | <=1/110 (более чем в 100 раз).

Что следует предпринять, если  |x| >> 1  (много больше 1), ведь в этом случае наш критерий  работать не будет? Тут пригодится знание такого свойства функции sin(x) как периодичность. Для угла x, заданного в радианах:
sin(x+2πk) = sin(x), где k – любое целое число, 0, ±1, ±2, ±3, … .
Для угла а, заданного в градусах:
sin(a+360°·k) = sin(a), где k – любое целое число.
Таким образом, если угол х по модулю не будет больше 2π, |x|<=2π, то тогда оценка отношения двух соседних членов ряда не превысит:
|rn+2 /  rn | = 4π2 / ((n+2)(n+1))
т.е., начиная с n=7 убывание гарантировано.

Перейдем к программированию функции Sin2(x). Используем принцип 7 – проектирование сверху-вниз. Объявим в классе Program метод:
static double Sin2(double x)  {    }
а перед оператором  Console.ReadKey(); в методе Main() вставим:
y = Sin2(x);
Console.WriteLine(«sin({0})={1}», x, y);

Наша функция, также как и библиотечная, получает на вход значение угла в радианах и возвращает значение sin(x). Тип аргумента и самой функции задан как double. Приступим к разработке начинки метода Sin2() – блока в фигурных скобках.

Реализация 2.
Очевидный, но не лучший алгоритм состоит в непосредственной реализации формулы ряда: начальное значение суммы ряда s = x, затем в цикле for добавлять каждый вычисленный член ряда с чередованием знака rn=xn/n!, для чего будет необходимо написать еще две функции: возведение в степень и вычисление факториала.
Не совсем понятно, сколько раз выполнять цикл. Хотелось бы увязать необходимое число членов ряда с требуемой точностью вычислений eps.
Добавим в метод Sin2() локальную константу double eps = 1E-15.
Вместо цикла for применим цикл while (условие) {  }.
Введем две переменные: double r – активный член ряда, int n – степень.
Для учета смены знака используем функцию модуля числа – Math.Abs( ).

Тогда метод Sin2() может быть записан так:

static double Sin2(double x)
{
   const double eps = 1e-15;  // погрешность вычислений
   double s = 0;  // начальная сумма
   double r = x;   // первый член ряда
   int n = 1;         // показатель степени
   while (Math.Abs(r) > eps) // условие выполнения цикла
   {
      s=s+r;      // добавление члена ряда
      n = n + 2;  // наращивание  n:  1,3,5,7,...
      r=-r*x*x/(n*(n-1)); // новый член ряда
   }
   return s;       // возврат результата
}

Выполним тестирование программы:

Угол a, градусы Угол x, радианы Sin(x) Sin2(x)
0 0 0 0
30 0,523598775598299 0,5 0,5
60 1,0471975511966 0,8660254037844439 0,8660254037844438
90 1,5707963267949 1 1
270 4,71238898038469 -1 -1
360 6,28318530717959 -2,45E-16 -4,16E-15
10000 174,532925199433 -0,984807753012209 -3,07952784012437E+58

При углах, не превышающих 360°, оба метода дают практически одинаковый результат, погрешность почти не превышает eps=1E-15. При больших значениях углов Sin2() иногда выдает неверный результат (|sin(x)|<=1).
Учтем замечание о периодичности функции sin(x). Для этого вставим сразу после { один оператор, позволяющий учесть периодичность функции:
x = x — 2 * Math.PI * Math.Truncate(x/(2*Math.PI));
Примечание. Функция Truncate( ) класса Math вычисляет целую часть числа двойной точности с плавающей запятой.
Протестируйте результирующую программу sin_x_ЧерезРяд для произвольных углов.  В частности, Sin2(10000°)=-0,984807753012207.

Следующая учебная задача «Бином Ньютона».


NEW: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.


Понравилась статья? Поделиться с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о

3 комментариев
Новые
Старые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Что то не получаются значения из тестовой таблицы. Студия 2022 v. 17.6.4

При компиляции VS 2019 выдает ошибку
System.FormatException: «Input string was not in a correct format.»
на строке
Console.WriteLine(«sin2({ 0})={ 1}», x, y);

3
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x