Pers.narod.ru. Алгоритмы. Некоторые математические расчёты на Паскале |
Как известно, ядро Паскаля предельно компактно, и многие математические функции в модуле System просто отсутствуют. Поэтому у людей, изучающих основы программирования именно на этом языке, типовые математические расчёты зачастую вызывают проблемы. Далее приводятся пути решения наиболее типичных из этих проблем.
Требуется вычислить значение с = ab
. В зависимости от значений
основания a
и показателя степени b
,
вычисление степени может быть реализовано по-разному.
Если a > 0
, а b
может принимать произвольные вещественные значения, используем известную формулу
ab = exp (b * ln a)
:
c:=exp(b*ln(a));
Если b
- целое число (вообще говоря, "не слишком большое" по модулю),
а a
- любое (не равное нулю при b < 0
),
возведение в степень может быть реализовано с помощью цикла:
var i:integer; {...} c:=1; for i:=1 to abs(b) do c:=c*a; {перемножение одинаковых сомножителей b раз} if b<0 then c:=1/c; {учёт знака показателя}
Для целого b
и не равного нулю a
выгоднее считать с помощью экспоненты и логарифма, не забывая о том, что не существует логарифмов от отрицательных чисел:
c:=exp(b*ln(abs(a))); {степень положительного основания} if (odd(b)=true) and (a<0) then c:=-c; {если основание отрицательно, а показатель нечетный, то меняем знак}
Стандартная функция sqrt
умеет извлекать только квадратный корень.
Извлечь корень степени n
(где n
- натуральное)
из числа a
можно всегда, кроме случая, когда a < 0
и при этом n
четно. Извлечь корень степени n
из числа a
означает возвести число a
в степень 1/n
.
При этом знак корня совпадает со знаком a
.
Ниже приводится код функции, вычисляющей корень произвольной степени
n
от своего аргумента a
:
function root(a:real;n:word):real; {Тип word здесь указывает, что n положительно} var r: real; begin r:=exp(ln(abs(a))/n); {корень из модуля} if a<0 then root:=-r else root:=r {учет знака} end;
Стандартная функция ln
вычисляет только натуральный логарифм.
Для вычисления логарифмов по другим основаниям можно применить формулу
log a b = ln b / ln a
:
c:=ln(b)/ln(a);
В частности, для вычисления десятичного логарифма lg b
можно записать:
c:=ln(b)/ln(10);
В Паскале имеется стандартная функция arctan
для вычисления арктангенса.
Другие обратные тригонометрические функции могут быть выражены через неё с помощью формул тригонометрии.
Для вычисления y = arcsin x
, где, конечно, |x| <= 1
,
можно применить один из следующих способов:
if x=1 then y:=pi/2 else if x=-1 then y:=-pi/2 else y:=arctan(x/sqrt(1-sqr(x)));
или
y:=2*arctan(x/(1+sqrt(1-sqr(x))));
на практике следует помнить о возможных погрешностях при сравнении вещественных чисел (глава учебника, п.7.2).
Для вычисления z = arccos x
, где |x| <= 1
,
можно использовать тот факт, что
сумма арксинуса и арккосинуса некоторого значения
равна прямому углу:
if x=1 then z:=0 else if x=-1 then z:=pi else z:=pi/2-arctan(x/sqrt(1-sqr(x)));
или
z:=pi/2-2*arctan(x/(1+sqrt(1-sqr(x))));
Полярным углом точки с координатами (x,y)
,
отличной от начала координат,
называют угол между положительным направлением оси Ox
и
направлением из начала координат на данную точку. При этом угол
отсчитывается против часовой стрелки.
Строго говоря, полярный угол не всегда
равен arctg (y/x)
, это верно лишь при x > 0
.
Кроме того, при делении большого значения y
на малое x
возможно переполнение. Показанная ниже функция вычисляет полярный угол fi
,
лежащий в промежутке от -pi
до +pi
,
для любой точки с координатами (x,y)
, не совпадающей с началом координат:
function fi(x:real; y:real):real; var f:real; begin if abs(x)>abs(y) then begin f:=arctan(y/x); if x>0 then fi:=f else if y>=0 then fi:=f+pi else fi:=f-pi end else begin f:=arctan(x/y); if y>0 then fi:=pi/2-f else fi:=-pi/2-f end end;
Начинающие "паскалисты" нередко не понимают строгой типизированности этого языка, из-за чего находят в нём несуществующие "баги". Вот простейший пример.
var a,b:integer; r:longint; begin a:=1000; b:=200; r:=a*b; writeln (r); end.
Эта программа выдаст отнюдь не 200000, как может показаться. Ответ будет равен 3392 (результат переполнения). Никакого бага нет. Тип выражения в Паскале определяется только типом входящих в него переменных, но не типом переменной, куда записывается результат. То есть, мы вычислили с переполнением произведение двух переменных типа Integer
, а потом "испорченный" результат переписали в переменную типа Longint
. ничего не изменит и
r:=Longint(a*b);
Здесь тоже сначала вычислен результат с переполнением, затем преобразован к типу Longint
. А вот
r:=Longint(a)*b;
рулит, получите свои 200000 :) Указанная ошибка часто встречается в программах начинающих. Чтобы её не повторять, помните - выражение в Паскале должно быть приведено к нужному типу в процессе его вычисления, а не после его окончания или при присваивании.
гостевая; E-mail |