Постановка задачи. Вычислить значение тригонометрической функции 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(); } } }
Результат выполнения программы:
Вариант решения 2.
Понятно, что метод Sin( ) есть некоторая подпрограмма класса Math, которая вызывается по имени. Однако может быть вам будет интересно узнать, как она устроена. Для этого мы дополним наш проект методом Sin2(x), в котором самостоятельно реализуем некоторый алгоритм вычисления sin(x). Тогда библиотечную функцию Sin(x) мы сможем использовать для задания ожидаемого результата при тестировании, а обсуждая алгоритм вычисления Sin2(x), мы освоим элементы языка C# и приобретем некоторые навыки программирования.
Заглянем в справочник по высшей математике, раздел «Ряды». Из него мы узнаем, что функция 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: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.
Что то не получаются значения из тестовой таблицы. Студия 2022 v. 17.6.4
При компиляции VS 2019 выдает ошибку
System.FormatException: «Input string was not in a correct format.»
на строке
Console.WriteLine(«sin2({ 0})={ 1}», x, y);
Антон!
Уберите в операторе вывода пробелы в фигурных скобках: