pascalabcnet/bin/Lib/NumLibABC.pas

2787 lines
93 KiB
ObjectPascal
Raw Normal View History

unit NumLibABC;
2017-10-05 22:55:56 +03:00
const &NumLibABCVersion = 'NumLibABC 1.0.2 от 20.03.2024';
2017-10-05 22:55:56 +03:00
type
Point=auto class
public
x,y:real;
end;
{$region ApproxCheb}
type
2017-11-26 19:35:27 +03:00
/// Аппроксимация табличной функции полиномами Чебышева
/// по методу наименьших квадратов
2017-10-05 22:55:56 +03:00
ApproxCheb=class
private
2017-11-26 19:35:27 +03:00
nt:integer; // количество точек
a:array[,] of real; // расширенная матрица системы (см. MatrCreate)
2017-10-05 22:55:56 +03:00
procedure MatrCreate;
2017-11-26 19:35:27 +03:00
// заполняет матрицу а для решения СЛАУ с целью получения
// коэффициентов интерполяционного полинома степени n
2017-10-05 22:55:56 +03:00
begin
2017-11-26 19:35:27 +03:00
a:=new real[r+1,r+2]; // инициализация нулями
2017-10-05 22:55:56 +03:00
var p,s:real;
for var i:=0 to r do begin
(p,s):=(1.0,x[i]); a[i,r+1]:=f[i];
for var j:=0 to r do begin a[i,j]:=p; p*=s end
end
end;
procedure Gauss;
2017-11-26 19:35:27 +03:00
// Базируется на программе 3.1Р
// В кн.: Мудров А.Е. Численные методы для ПЭВЫМ на языках
// Бейсик, Фортран и Паскаль. - Томск: МП "Раско", 1991.
2017-10-05 22:55:56 +03:00
begin
var n1:=r+1;
c:=new real[n1];
var p,s:real;
for var k:=0 to r do begin
var k1:=k+1; s:=a[k,k]; var jj:=k;
for var i:=k1 to r do begin
p:=a[i,k];
if Abs(r)>Abs(s) then (s,jj):=(p,i)
end;
if jj<>k then
for var i:=k to n1 do Swap(a[k,i],a[jj,i]);
for var j:=k1 to n1 do a[k,j]/=s;
for var i:=k1 to r do begin
p:=a[i,k];
for var j:=k1 to n1 do a[i,j]-=a[k,j]*p
end
end;
for var i:=r downto 0 do begin
s:=a[i,n1];
for var j:=i+1 to r do s-=a[i,j]*c[j];
c[i]:=s
end
end;
public
2017-11-26 19:35:27 +03:00
x:array of real; // значения аргументов
y:array of real; // значения функции
tol:real; // погрешносмть аппроксимации
r:integer; // степень аппроксимирующего полинома (r<nt)
f:array of real; // аппроксимированные значения функции
c:array of real; // коэффициенты аппроксимирующего полинома
2017-10-05 22:55:56 +03:00
constructor (x,y:array of real; eps:real);
begin
nt:=x.Length;
Assert(nt=y.Length,
2017-11-26 19:35:27 +03:00
'Approx: несовпадение количества точек в аргументе и функции');
2017-10-05 22:55:56 +03:00
Self.x:=x;
Self.y:=y;
f:=ArrFill(nt,0.0);
Cheb(eps)
end;
2017-11-26 19:35:27 +03:00
/// Аппроксимация табличной функции полиномами Чебышева (МНК)
2017-10-05 22:55:56 +03:00
procedure Cheb(e:real);
begin
r:=0;
var n:=nt-1;
var f1:=ArrFill(nt,0.0);
var f2:=ArrFill(nt,1.0);
var eps:=ArrFill(nt,0.0);
while true do begin
var sum1:=y.Zip(f2,(p,q)->p*q).Sum;
var sum2:=f2.Select(p->p*p).Sum;
var al:=sum1/sum2;
f:=f.Zip(f2,(p,q)->p+al*q).ToArray;
for var i:=0 to n do eps[i]:=Abs(y[i]-f[i]);
tol:=Sqrt((eps.Select(t->t*t).Sum)/n);
if (tol<e) or (r>=n+1) then break;
sum1:=x.Zip(f2,(p,q)->p*q*q).Sum;
sum2:=f2.Select(p->p*p).Sum;
var be:=-(sum1/sum2);
var g:real;
if r<>0 then begin
(sum1,sum2):=(0.0,0.0);
for var i:=0 to n do begin sum1+=x[i]*f1[i]*f2[i]; sum2+=Sqr(f1[i]) end;
g:=-(sum1/sum2);
end;
r+=1;
for var i:=0 to n do begin
var a:=f1[i]; f1[i]:=f2[i]; f2[i]:=(x[i]+be)*f1[i]+g*a
end
end
end;
2017-11-26 19:35:27 +03:00
/// Получение коэффициентов найденного полинома в его степенном представлении
2017-10-05 22:55:56 +03:00
procedure MakeCoef;
begin
MatrCreate;
Gauss
end;
end;
{$endregion}
{$region Decomp}
type
2017-11-26 19:35:27 +03:00
/// LU-разложение вещественной матрицы системы линейных уравнений
2017-10-05 22:55:56 +03:00
Decomp=class
private
2017-11-26 19:35:27 +03:00
n:integer; // число строк/столбцов исходной матрицы минус 1
2017-10-05 22:55:56 +03:00
procedure Decomp;
//
2017-11-26 19:35:27 +03:00
// *** Вычисление LU-разложения вещественной матрицы посредством
// гауссова исключения и оценка обусловленности матрицы ***
// Используется для решения систем линейных уравнений
// При точной вырожденности cond=MaxReal (примерно 10^308)
// Можно вычислить определитель матрицы, найдя произведение
// диагональных элементов матрицы "а" и умножив его на последний
// элемент массива - результата.
// Для решения системы линейных уравнений надо вызвать процедуру
// Solve, передав ей вектор правых частей уравнения.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Является результатом переработки одноименной фортран-программы,
// приведенной в кн. Дж.Форсайт, М.Малькольм, К.Моулер
// "Машинные методы математических вычислений",
// М., "Мир", 1980.
2017-10-05 22:55:56 +03:00
//
begin
2017-11-26 19:35:27 +03:00
Assert(a.RowCount=a.ColCount,'DECOMP: матрица не квадратная');
2017-10-05 22:55:56 +03:00
ipvt[n]:=1;
if n=0 then begin
if a[0,0]<>0 then cond:=1
else cond:=MaxReal;
Exit
end;
var anorm:=a.Cols.Select(r->r.Select(x->Abs(x)).Sum).Max;
for var k:=0 to n-1 do begin
var m:=k;
for var i:=k+1 to n do
if Abs(a[i,k])>Abs(a[m,k]) then m:=i;
ipvt[k]:=m;
if m<>k then ipvt[n]:=-ipvt[n];
Swap(a[m,k],a[k,k]);
if a[k,k]<>0 then begin
for var i:=k+1 to n do a[i,k]:=-a[i,k]/a[k,k];
for var j:=k+1 to n do begin
Swap(a[m,j],a[k,j]);
if a[k,j]<>0 then
for var i:=k+1 to n do a[i,j]+=a[i,k]*a[k,j]
end
end
end;
var work:=new real[n+1];
for var k:=0 to n do begin
var t:=0.0;
if k<>0 then
for var i:=0 to k-1 do t+=a[i,k]*work[i];
var ek:=1.0;
if t<0.0 then ek:=-1.0;
if a[k,k]=0.0 then begin cond:=MaxReal; Exit end;
work[k]:=-(ek+t)/a[k,k];
end;
for var kb:=0 to n-1 do begin
var k:=n-kb-1;
var t:=0.0;
for var i:=k+1 to n do t+=a[i,k]*work[k];
work[k]:=t;
var m:=ipvt[k];
if m<>k then Swap(work[m],work[k])
end;
var ynorm:=work.Select(x->Abs(x)).Sum;
Self.Solve(work);
var znorm:=work.Select(x->Abs(x)).Sum;
cond:=Max(anorm*znorm/ynorm,1.0);
det:=real(ipvt.Last);
for var i:=0 to a.RowCount-1 do det*=a[i,i];
end;
public
2017-11-26 19:35:27 +03:00
a:array[,] of real; // исходная матрица
2017-10-05 22:55:56 +03:00
cond:real;
2017-11-26 19:35:27 +03:00
ipvt:array of integer; // вспомогательный вектор
det:real; // детерминант матрицы a
2017-10-05 22:55:56 +03:00
constructor(a:array[,] of real);
begin
n:=a.RowCount-1;
2017-11-26 19:35:27 +03:00
Assert((n=a.ColCount-1) and (n>=0),'DECOMP: исходная матрица не квадратная');
2017-10-05 22:55:56 +03:00
Self.a:=new real[n+1,n+1];
for var i:=0 to n do
for var j:=0 to n do
Self.a[i,j]:=a[i,j];
Self.ipvt:=new integer[n+1];
Decomp
end;
2017-11-26 19:35:27 +03:00
/// Решение системы линейных уравнений для заданного
/// вектора правых частей уравнения.
2017-10-05 22:55:56 +03:00
procedure Solve(b:array of real);
//
2017-11-26 19:35:27 +03:00
// Массив b - вектор правых частей систеы.
// Массив ipvt является результатом вызова Decomp.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Является результатом переработки одноименной фортран-программы,
// приведенной в кн. Дж.Форсайт, М.Малькольм, К.Моулер
// "Машинные методы математических вычислений",
// М., "Мир", 1980.
2017-10-05 22:55:56 +03:00
//
begin
2017-11-26 19:35:27 +03:00
Assert(b.Length-1=n,'DECOMP/SOLVE: неверная длина вектора правых частей');
2017-10-05 22:55:56 +03:00
for var k:=0 to n-1 do begin
var m:=ipvt[k];
Swap(b[m],b[k]);
for var i:=k+1 to n do b[i]+=a[i,k]*b[k]
end;
for var k:=n downto 1 do begin
b[k]/=a[k,k];
var t:=-b[k];
for var i:=0 to k-1 do begin
b[i]+=a[i,k]*t;
end
end;
b[0]/=a[0,0]
end;
end;
{$endregion}
{$region Factors}
type
2017-11-26 19:35:27 +03:00
/// Разложение полинома с целочисленными коэффициентами
/// на рациональные линейные множители вида ux-v
2017-10-05 22:55:56 +03:00
Factors=class
private
a:array of integer;
n:integer;
public
constructor(params a:array of integer);
begin
Self.a:=a;
Self.n:=a.Length-1;
end;
2017-11-26 19:35:27 +03:00
/// Разложение полинома с целочисленными коэффициентами
/// на рациональные линейные множители вида ux-v
2017-10-05 22:55:56 +03:00
function Factorize:array[,] of integer;
//
2017-11-26 19:35:27 +03:00
// Коэффициенты полинома должны задаваться в порядке возрастания степени.
// Возвращаемый массив:
// [0,0] - количество найденных линейных множителей;
// [0,1] - максимальный НОД коэффициентов полинома;
// Каждая последующая i-я строка содержит коэффициенты множителя
2017-10-05 22:55:56 +03:00
// [i,0]=u, [i,1]=v
//
2017-11-26 19:35:27 +03:00
// Замечание: В случае, если свободный член нулевой, предварительно следует
// вынести аргумент за скобку и производить поиск в оставшемся полиноме.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Является переработкой алгоритма 75б (язык Алгол-60)
// В кн. Агеев М.И., Алик В.П., Марков Ю.И.
// Библиотека алгоритмов 51б-100б. (Справочное пособие).
// Вып.2. М., "Сов.радио", 1976
2017-10-05 22:55:56 +03:00
//
begin
var f,g,q:integer;
var (r,c):=(0,1);
Result:=new integer[n+1,2];
var b:=a.Reverse.ToArray;
2017-11-26 19:35:27 +03:00
while b[n]=1 do begin // исключим множители (1*х-0)
2017-10-05 22:55:56 +03:00
n-=1; r+=1;
(Result[r,0],Result[r,1]):=(1,0);
end;
var Fin:=false;
for var p:=1 to Abs(b[0]) do
if b[0] mod p=0 then
for var q1:=1 to Abs(b[n]) do begin
q:=q1;
if q<>1 then begin
var flag:=true;
for var j:=0 to n do
if b[j] mod q<>0 then begin flag:=false; break end;
if flag then
for var j:=0 to n do b[j]:=b[j] div q
end;
repeat
(f,g):=(b[0],1);
for var i:=1 to n do begin g*=p; f:=f*q+g*b[i] end;
if f=0 then begin
r+=1;
(Result[r,0],Result[r,1]):=(p,q);
for var i:=0 to n do begin f:=(b[i]+f) div p; b[i]:=f; f*=q end;
n-=1;
if n<>0 then continue;
Fin:=true
end;
if not Fin then q:=-q;
until (q>0) or Fin;
if Fin then break;
end;
if n=0 then c*=b[0];
(Result[0,0],Result[0,1]):=(r,c);
SetLength(Result,r+1,2)
end;
end;
{$endregion}
{$region FMin}
type
2017-11-26 19:35:27 +03:00
/// Нахождение минимума функции на заданном интервале
2017-10-05 22:55:56 +03:00
FMin=class
private
a,b,t:real;
f:real->real;
procedure FMin;
2017-11-26 19:35:27 +03:00
// Нахождение локального минимума функции f(x) на интервале [a;b]
// Метод использует комбинацию поиска золотого сечения и последовательной
// параболической интерполяции. Сходимость никода не бывает хуже, чем при
// фибоначчиевом поиске. Если функция имеет непрерывную положительную вторую
// производную в точне минимума, не совпадающем с текущими границами интервала
// поиска, сходимость сверхлинейная и обычно имеет порядок 1.324...
// Бесполезно пытаться задавать точность е меньше значения квадратного корня
// из машинной точности, принимаемого по умолчанию.
// Результат: кортеж двух переменных типа real:
// найденные значения аргумента и вычисленной для него функции.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// На основе версии Fortran90 (John Burkardt).
// Алгоритм:
2017-10-05 22:55:56 +03:00
// Richard Brent,
// Algorithms for Minimization Without Derivatives,
// Dover, 2002,
// ISBN: 0-486-41998-3,
// LC: QA402.5.B74.
begin
var d,fu,u,tol1:real;
2017-11-26 19:35:27 +03:00
var c:=0.5*(3-Sqrt(5.0)); // квадрат величины, обратной золотому сечению
2017-10-05 22:55:56 +03:00
var(sa,sb):=(a,b);
x:=sa+c*(b-a);
var w:=x;
var (v,e,fx):=(w,0.0,f(x));
var fw:=fx;
var fv:=fw;
var eps:=1.0;
repeat
eps:=0.5*eps;
tol1:=1.0+eps
until tol1<=1.0;
eps:=Sqrt(eps);
repeat
var m:=0.5*(sa+sb);
var tol:=eps*Abs(x)+t;
var t2:=2*tol;
2017-11-26 19:35:27 +03:00
// проверка критерия выхода
2017-10-05 22:55:56 +03:00
if Abs(x-m) <= t2-0.5*(sb-sa) then break;
2017-11-26 19:35:27 +03:00
// приемлема ли парабола?
2017-10-05 22:55:56 +03:00
var r:=0.0;
var q:=r;
var p:=q;
if tol<Abs(e) then begin
r:=(x-w)*(fx-fv); q:=(x-v)*(fx-fw);
p:=(x-v)*q-(x-w)*r; q:=2.0*(q-r);
if q>0.0 then p:=-p;
q:=Abs(q); r:=e; e:=d
end;
if (Abs(p)<Abs(0.5*q*r)) and (q*(sa-x)<p) and (p<q*(sb-x)) then begin
2017-11-26 19:35:27 +03:00
// шаг параболической интерполяции
2017-10-05 22:55:56 +03:00
d:=p/q; u:=x+d;
2017-11-26 19:35:27 +03:00
// f не следует вычислять слишком близко к a или b.
2017-10-05 22:55:56 +03:00
if ((u-sa)<t2) or ((sb-u)<t2) then d:=x<m?tol:-tol;
end
else begin
2017-11-26 19:35:27 +03:00
// шаг золотого сечения
2017-10-05 22:55:56 +03:00
e:=x<m?sb-x:sa-x; d:=c*e
end;
2017-11-26 19:35:27 +03:00
// f не следует вычислять слишком близко к найденному х
2017-10-05 22:55:56 +03:00
if tol<=Abs(d) then u:=x+d
else if d>0.0 then u:=x+tol
else u:=x-tol;
fu:=f(u);
2017-11-26 19:35:27 +03:00
// Обновить найденные параметры
2017-10-05 22:55:56 +03:00
if fu<=fx then begin
if u<x then sb:=x else sa:=x;
(v,fv):=(w,fw);
(w,fw):=(x,fx);
(x,fx):=(u,fu)
end
else begin
if u<x then sa:=u else sb:=u;
if (fu<=fw) or (w=x) then begin
(v,fv):=(w,fw);
(w,fw):=(u,fu)
end
else
if (fu<=fv) or (v=x) or (v=w) then (v,fv):=(u,fu)
end
until false;
Value:=fx
end;
public
2017-11-26 19:35:27 +03:00
x:real; // значение аргумента
Value:real; // значение функции
2017-10-05 22:55:56 +03:00
constructor(f:real->real; a,b:real; t:real:=1.05e-8);
begin
Self.a:=a;
Self.b:=b;
Self.t:=t;
Self.f:=f;
FMin
end;
end;
{$endregion}
{$region FMinN}
type
2017-11-26 19:35:27 +03:00
/// минимизация функции многих переменных
2017-10-05 22:55:56 +03:00
FMinN=class
private
2017-11-26 19:35:27 +03:00
delta:array of real; // инициализируется HJ
fbefore:real; // значение функции при входе
newx:array of real; // инициализируется HJ
2017-10-05 22:55:56 +03:00
2017-11-26 19:35:27 +03:00
fun:function(x:array of real):real; // целевая функция
2017-10-05 22:55:56 +03:00
function BestNearby:real;
2017-11-26 19:35:27 +03:00
// Поиск точки, лучше исходной (по одному параметру за шаг)
// За основу взят текст программы BEST_NEARBY.
// Источник:
2017-10-05 22:55:56 +03:00
// TOMS178
// Minimization by Hooke-Jeeves Direct Search
// FORTRAN-90 version by John Burkardt.
// ( Dept. of Scientific Computing, Florida State University ),
2017-11-26 19:35:27 +03:00
// на условиях GNU LGPL
2017-10-05 22:55:56 +03:00
begin
Result:=fbefore;
var z:=Copy(newx);
for var i:=0 to n-1 do begin
z[i]:=newx[i]+delta[i];
var ftmp:=fun(z);
if ftmp<Result then Result:=ftmp
else begin
delta[i]:=-delta[i];
z[i]:=newx[i]+delta[i];
ftmp:=fun(z);
if ftmp<Result then Result:=ftmp
else z[i]:=newx[i]
end
end;
newx:=Copy(z);
end;
function IsEqual(x1,x2:array of real; eps:real):Boolean;
begin
Result:=true;
for var i:=0 to x1.Length-1 do
Result:=Result and (Abs(x1[i]-x2[i])<=eps);
end;
public
n:integer;
2017-11-26 19:35:27 +03:00
iter:integer; // число итераций
x:array of real; // вектор аргументов
2017-10-05 22:55:56 +03:00
constructor (px:array of real; f:function(x:array of real):real);
begin
n:=px.Length;
x:=Copy(px);
fun:=f;
iter:=0;
Randomize;
end;
2017-11-26 19:35:27 +03:00
/// Нахождение безусловного минимума методом адаптивного случайного поиска
2017-10-05 22:55:56 +03:00
procedure ARS(R:real; var t:real; MaxIter:integer:=100);
//
2017-11-26 19:35:27 +03:00
// t - радиус области неопределенности найденных координат;
// R - минимальная величина шага по аргументам, R<t;
// MaxIter - максимальное количество итераций
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Алгоритм приведен в книге Пантелеев А.В., Летова Т.А.
// Методы оптимизации в примерах и задачах. - М., Высш.шк., 2005.
2017-10-05 22:55:56 +03:00
//
begin
var (a,b,n):=(1.618,0.618,x.Length);
var (y,z,ksi):=(new real[n],new real[n],new real[n]);
var (k,j,M):=(0,1,3*n);
Randomize;
t:=1.0;
while true do begin
var ka:=0.0;
for var i:=0 to n-1 do begin
ksi[i]:=2*Random-1.0;
ka+=Sqr(ksi[i])
end;
ka:=Sqrt(ka);
for var i:=0 to n-1 do y[i]:=x[i]+t*ksi[i]/ka;
var fx:=fun(x);
2017-11-26 19:35:27 +03:00
if fun(y)<fx then begin // шаг удачный
2017-10-05 22:55:56 +03:00
for var i:=0 to n-1 do z[i]:=x[i]+a*(y[i]-x[i]);
2017-11-26 19:35:27 +03:00
if fun(z)<fun(x) then begin // направление поиска удачное
2017-10-05 22:55:56 +03:00
x:=Copy(z); t:=a*t; k+=1;
if k<MaxIter then begin j:=1; continue end
else break
end
end;
if j<M then j+=1
else
if t>R then begin t:=b*t; j:=1 end
else break
end
end;
2017-11-26 19:35:27 +03:00
/// Поиск минимума функции методом Монте-Карло
procedure MKSearch(a,b:array of real; // векторы границ
var y:real; // лучшее значение функции fun
m:integer:=1000); // число случайных точек
// Авторская реализация. Алгоритм тривиален.
// Найденный вектор параметров сохраняется в свойстве класса x
// Значение функции для этого вектора возвращается в параметре y
2017-10-05 22:55:56 +03:00
begin
var n:=x.Length;
var tx:=new real[n];
var ty:real;
loop m do begin
for var i:=0 to n-1 do tx[i]:=Random*(b[i]-a[i])+a[i];
ty:=fun(tx);
if ty<y then begin x:=Copy(tx); y:=ty end
end
end;
2017-11-26 19:35:27 +03:00
/// Алгоритм прямого поиска Хука - Дживса
2017-10-05 22:55:56 +03:00
function HJ(eps:real:=1e-6;
rho:real:=0.5;
itermax:integer:=5000):array of real;
2017-11-26 19:35:27 +03:00
// За основу взят текст программы HOOKE.
// Источник
2017-10-05 22:55:56 +03:00
// TOMS178
// Minimization by Hooke-Jeeves Direct Search
// FORTRAN-90 version by John Burkardt.
// ( Dept. of Scientific Computing, Florida State University ),
2017-11-26 19:35:27 +03:00
// на условиях GNU LGPL
// Утверждается что aлгоритм Хука - Дживса хорошо работает
// при rho=0.5, но плохо при rho=0.6 и лучше при rho=0.8
2017-10-05 22:55:56 +03:00
begin
newx:=Copy(x);
var xbefore:=Copy(x);
delta:=x.Select(t->t=0?rho:rho*Abs(t)).ToArray;
var steplength:=rho;
iter:=0;
fbefore:=fun(newx);
var newf:=fbefore;
while (iter<itermax) and (eps<steplength) do begin
iter+=1;
2017-11-26 19:35:27 +03:00
// поиск новой "лучшей точки" по одной координате за шаг
2017-10-05 22:55:56 +03:00
newx:=Copy(xbefore);
newf:=BestNearby;
2017-11-26 19:35:27 +03:00
// если улучшение получено, идем в этом направлении
2017-10-05 22:55:56 +03:00
var keep:=1;
while (newf<fbefore) and (keep=1) do begin
for var i:=0 to n-1 do begin
if newx[i]<=xbefore[i] then delta[i]:=-Abs(delta[i])
else delta[i]:=Abs(delta[i]);
var tmp:=xbefore[i];
xbefore[i]:=newx[i];
newx[i]+=newx[i]-tmp
end;
fbefore:=newf;
newf:=BestNearby;
2017-11-26 19:35:27 +03:00
if fbefore<=newf then break; // если улучшить не получается...
2017-10-05 22:55:56 +03:00
keep:=0;
for var i:=0 to n-1 do
if 0.5*Abs(delta[i])<Abs(newx[i]-xbefore[i]) then begin
keep:=1; break
end;
end;
if (eps<=steplength) and (fbefore<=newf) then begin
steplength*=rho;
delta.Transform(x->x*rho);
end
end;
Result:=Copy(xbefore);
end;
2017-11-26 19:35:27 +03:00
/// Алгоритм наилучшей пробы с направляющим гиперквадратом
procedure BPHS(a,b:array of real; // векторы границ
var y:real; // лучшее значение
k:integer:=100; // число случайных точек
m:integer:=1000); // число случайных точек для MKSearch
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Использует MKSearch. Функция вычисляется k*m раз.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Рейзлин В.И. Численные методы оптимизации: учебное пособие
// / В.И. Рейзлин; Томский политехнический университет.
// -Томск: Изд-во Томского политехнического университета, 2011.
2017-10-05 22:55:56 +03:00
begin
var alpha:=2.0;
y:=real.MaxValue;
var ty:=y;
var ta:=Copy(a);
var tb:=Copy(b);
var brk:=false;
loop k do begin
MKSearch(ta,tb,ty,m);
if ty<y then begin
y:=ty;
for var i:=0 to n-1 do begin
a[i]:=ta[i]; b[i]:=tb[i];
var h:=(tb[i]-ta[i])/2/alpha;
brk:=brk or (h+1=1);
ta[i]:=x[i]-h; brk:=brk or (h+1=1); tb[i]:=x[i]+h;
end;
if brk then break
end
end
end;
2017-11-26 19:35:27 +03:00
/// Выбор результатов по лучшим из p проб, сделанных BPHS
2017-10-05 22:55:56 +03:00
function BestP(a,b:array of real;
eps:real:=0.01; //- версия 1.0.1
2017-10-05 22:55:56 +03:00
p:integer:=10;
k:integer:=100;
m:integer:=1000):List<(real,array of real)>;
2017-11-26 19:35:27 +03:00
// Авторская программа. Использует BPHS, MKSearch (косвенно)
2017-10-05 22:55:56 +03:00
begin
var L1:=new List<(real,array of real)>;
var L2:=new List<(real,array of real)>;
Result:=new List<(real,array of real)>;
var a1:=ArrFill(p,true);
if p>0 then begin
var i:=0;
loop p do begin
var y:=real.MaxValue;
var ta:=Copy(a);
var tb:=Copy(b);
BPHS(ta,tb,y,k,m);
L1.Add((y,x));
i+=1;
end;
end;
for var i:=0 to L1.Count-2 do begin
if a1[i] then begin
L2.Clear;
L2.Add(L1[i]);
for var j:=i+1 to L1.Count-1 do
if a1[j] then
if IsEqual(L1[i][1],L1[j][1],eps) then begin
L2.Add(L1[j]);
a1[j]:=false
end;
var q:=L2.Select(x->x[0]).ToArray.IndexMin;
2017-11-26 19:35:27 +03:00
if L2.Count>1 then Result.Add(L2[q]) // от случайностей
2017-10-05 22:55:56 +03:00
end
end
end;
end;
{$endregion}
{$region Fraction}
type
2017-11-26 19:35:27 +03:00
/// Класс обыкновенных дробей произвольной точности
2017-10-05 22:55:56 +03:00
Fraction=class
private
class function ToReal(m:BigInteger):(real,integer);
begin
var s:=m.ToString;
var r:real;
if Real.TryParse(s,r) then Result:=(r,0)
else Result:=(s.Left(16).ToReal,s.Length-16)
end;
2017-11-26 19:35:27 +03:00
/// НОД пары чисел
class function GCD(a, b: BigInteger): BigInteger;
2017-10-05 22:55:56 +03:00
begin
if a < 0 then
a := -a;
while b <> 0 do
(a, b) := (b, a mod b);
Result := a
2017-10-05 22:55:56 +03:00
end;
public
numerator:BigInteger;
denominator:BigInteger;
constructor (num:BigInteger:=0; denom:BigInteger:=1);
begin
2017-11-26 19:35:27 +03:00
Assert(denom>0,'Fraction: знаменатель дроби должен быть положительным');
2017-10-05 22:55:56 +03:00
var n:=GCD(num,denom);
numerator:=num div n;
denominator:=denom div n
end;
class function operator +(a,b:fraction):fraction;
begin
var n:=GCD(a.denominator,b.denominator);
Result:=new fraction(a.numerator*(b.denominator div n)+
b.numerator*(a.denominator div n),a.denominator div n*b.denominator);
end;
class procedure operator +=(var r:fraction; a:fraction);
begin
r:=r+a;
end;
class function operator +(a:BigInteger; b:fraction):fraction;
begin
Result:=new fraction(a*b.denominator+b.numerator,b.denominator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class function operator +(a:fraction; b:BigInteger):fraction;
begin
Result:=new fraction(b*a.denominator+a.numerator,a.denominator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class procedure operator +=(var r:fraction; a:BigInteger);
begin
r:=r+a;
end;
class function operator -(a,b:fraction):fraction;
begin
var n:=GCD(a.denominator,b.denominator);
Result:=new fraction(a.numerator*(b.denominator div n)-
b.numerator*(a.denominator div n),a.denominator div n*b.denominator);
end;
class procedure operator -=(var R:fraction; a:fraction);
begin
r:=r-a;
end;
class function operator -(a:BigInteger; b:fraction):fraction;
begin
Result:=new fraction(a*b.denominator-b.numerator,b.denominator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class function operator -(a:fraction; b:BigInteger):fraction;
begin
Result:=new fraction(a.numerator-b*a.denominator,a.denominator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class procedure operator -=(var r:fraction; a:BigInteger);
begin
r:=r-a;
end;
class function operator *(a,b:fraction):fraction;
begin
Result:=new fraction(a.numerator*b.numerator,a.denominator*b.denominator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class procedure operator *=(var r:fraction; a:fraction);
begin
r:=r*a;
end;
class function operator *(a:BigInteger; b:fraction):fraction;
begin
Result:=new fraction(a*b.numerator,b.denominator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class function operator *(a:fraction; b:BigInteger):fraction;
begin
Result:=new fraction(a.numerator*b,a.denominator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class procedure operator *=(var r:fraction; a:BigInteger);
begin
r:=r*a;
end;
class function operator /(a,b:fraction):fraction;
begin
Result:=new fraction(a.numerator*b.denominator,a.denominator*b.numerator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class procedure operator /=(var r:fraction; a:fraction);
begin
r:=r/a;
end;
class function operator /(a:BigInteger; b:fraction):fraction;
begin
Result:=new fraction(a*b.denominator,b.numerator);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class function operator /(a:fraction; b:BigInteger):fraction;
begin
Result:=new fraction(a.numerator,a.denominator*b);
var n:=GCD(Result.numerator,Result.denominator);
if n<>1 then begin
Result.numerator:=Result.numerator div n;
Result.denominator:=Result.denominator div n
end
end;
class procedure operator /=(var r:fraction; a:BigInteger);
begin
r:=r/a;
end;
class function operator -(a:fraction):=
new fraction(-a.numerator,a.denominator);
class function operator =(a:fraction; b:fraction):=(a-b).numerator=0;
class function operator =(a:BigInteger; b:fraction):=(a-b).numerator=0;
class function operator =(a:fraction; b:BigInteger):=(a-b).numerator=0;
class function operator >(a:fraction; b:fraction):=(a-b).numerator>0;
class function operator >(a:BigInteger; b:fraction):=(a-b).numerator>0;
class function operator >(a:fraction; b:BigInteger):=(a-b).numerator>0;
class function operator >=(a:fraction; b:fraction):=(a-b).numerator>=0;
class function operator >=(a:BigInteger; b:fraction):=(a-b).numerator>=0;
class function operator >=(a:fraction; b:BigInteger):=(a-b).numerator>=0;
class function operator <(a:fraction; b:fraction):=(a-b).numerator<0;
class function operator <(a:BigInteger; b:fraction):=(a-b).numerator<0;
class function operator <(a:fraction; b:BigInteger):=(a-b).numerator<0;
class function operator <=(a:fraction; b:fraction):=(a-b).numerator<=0;
class function operator <=(a:BigInteger; b:fraction):=(a-b).numerator<=0;
class function operator <=(a:fraction; b:BigInteger):=(a-b).numerator<=0;
class function operator <>(a:fraction; b:fraction):=(a-b).numerator<>0;
class function operator <>(a:BigInteger; b:fraction):=(a-b).numerator<>0;
class function operator <>(a:fraction; b:BigInteger):=(a-b).numerator<>0;
2017-11-26 19:35:27 +03:00
/// Обратная величина дроби
2017-10-05 22:55:56 +03:00
function Inv:fraction;
begin
Result:=new fraction;
Result.numerator:=denominator;
Result.denominator:=numerator
end;
2017-11-26 19:35:27 +03:00
/// Абсолютная величина дроби
2017-10-05 22:55:56 +03:00
function Abs:fraction;
begin
Result:=new fraction;
Result.numerator:=PABCSystem.Abs(numerator);
Result.denominator:=denominator
end;
function ToReal:real;
begin
var a:=Fraction.ToReal(numerator);
var b:=Fraction.ToReal(denominator);
Result:=a[0]/b[0]*Power(10,a[1]-b[1])
end;
function Print(s:string:=' '):fraction;
begin
Result:=Self;
if denominator=1 then Write(numerator,s)
else Write(numerator,'/',denominator,s)
end;
function ToString:string; override;
begin
Result:=numerator.ToString;
if denominator<>1 then Result+='/'+denominator.ToString
end;
end;
2017-11-26 19:35:27 +03:00
/// Обертка для конструктора класса
2017-10-05 22:55:56 +03:00
function Frc(a:BigInteger):fraction;
begin
Result:=new fraction(a,1);
end;
2017-11-26 19:35:27 +03:00
/// Обертка для конструктора класса
2017-10-05 22:55:56 +03:00
function Frc(a,b:BigInteger):fraction;
begin
Result:=new fraction(a,b);
end;
2017-11-26 19:35:27 +03:00
/// Обертка для конструктора класса
2017-10-05 22:55:56 +03:00
function Frc(a,b,c:BigInteger):fraction;
begin
Result:=new fraction(b,c);
Result:=a+Result
end;
{$endregion}
{$region Vector}
type
2017-11-26 19:35:27 +03:00
/// В Е К Т О Р Ы
2017-10-05 22:55:56 +03:00
Vector=class
public
Value:array of real;
Length:integer;
constructor (params px:array of real);
begin
Value:=PABCSystem.Copy(px);
Length:=Value.Length;
2017-11-26 19:35:27 +03:00
Assert(Value.Length<>0,'Vector: нулевая длина запрещена')
2017-10-05 22:55:56 +03:00
end;
constructor (pa,pb:array of real);
begin
Value:=PABCSystem.Copy(pb);
Length:=Value.Length;
for var i:=0 to Length-1 do Value[i]-=pa[i];
2017-11-26 19:35:27 +03:00
Assert(Value.Length<>0,'Vector: нулевая длина запрещена');
2017-10-05 22:55:56 +03:00
end;
constructor (pn:integer);
begin
Value:=new real[pn];
Length:=pn
end;
class function operator + (va,vb:Vector):Vector;
begin
2017-11-26 19:35:27 +03:00
Assert(va.Length=vb.Length,'Vector:длины несовместимы');
2017-10-05 22:55:56 +03:00
Result:=new Vector(va.Value.Zip(vb.Value,(p,q)->p+q).ToArray);
end;
class function operator - (va,vb:Vector):Vector;
begin
2017-11-26 19:35:27 +03:00
Assert(va.Length=vb.Length,'Vector:длины несовместимы');
2017-10-05 22:55:56 +03:00
Result:=new Vector(va.Value.Zip(vb.Value,(p,q)->p-q).ToArray);
end;
2017-11-26 19:35:27 +03:00
class function operator - (va:Vector):Vector; // унарный минус
2017-10-05 22:55:56 +03:00
begin
Result:=new Vector(va.Value);
for var i:=0 to Result.Length-1 do Result.Value[i]:=-Result.Value[i];
end;
2017-11-26 19:35:27 +03:00
class function operator * (va,vb:Vector):real; // скалярное произведение
2017-10-05 22:55:56 +03:00
begin
2017-11-26 19:35:27 +03:00
Assert(va.Length=vb.Length,'Vector:длины несовместимы');
2017-10-05 22:55:56 +03:00
Result:=va.Value.Zip(vb.Value,(p,q)->p*q).Sum;
end;
class function operator * (ra:real; vb:Vector):Vector;
begin
Result:=new Vector(vb.Value);
for var i:=0 to Result.Length-1 do Result.Value[i]*=ra
end;
class function operator * (va:Vector; rb:real):=rb*va;
class function operator = (va,vb:Vector):boolean;
begin
2017-11-26 19:35:27 +03:00
Assert(va.Length=vb.Length,'Vector:длины несовместимы');
2017-10-05 22:55:56 +03:00
Result:=(va.Value[0]/vb.Value[0]=1.0);
var i:=1;
while Result and (i<=va.Length-1) do begin
Result:=Result and (va.Value[i]/vb.Value[i]=1.0);
i+=1
end
end;
2017-11-26 19:35:27 +03:00
/// векторное произведение
2017-10-05 22:55:56 +03:00
function VP(vb:Vector):Vector;
begin
Result:=new Vector(3);
2017-11-26 19:35:27 +03:00
Assert(Length=vb.Length,'Vector.VP: длины векторов несовместимы');
2017-10-05 22:55:56 +03:00
if (Length=2) or (Length=3) then begin
var r:=new real[3,3] ((1,1,1),(0,0,0),(0,0,0));
for var j:=0 to Length-1 do begin
r[1,j]:=Value[j];
r[2,j]:=vb.Value[j]
end;
Result.Value[0]:=r[1,1]*r[2,2]-r[1,2]*r[2,1];
Result.Value[1]:=-r[1,0]*r[2,2]+r[1,2]*r[2,0];
Result.Value[2]:=r[1,0]*r[2,1]-r[1,1]*r[2,0]
end
else begin
2017-11-26 19:35:27 +03:00
Assert(false,'Vector.VP: Векторы должны быть двух или трехмерными');
2017-10-05 22:55:56 +03:00
Result.Value:=ArrFill(3,MaxReal)
end
end;
2017-11-26 19:35:27 +03:00
/// смешанное произведение
2017-10-05 22:55:56 +03:00
function MP(vb,vc:Vector):real;
begin
Assert((Length=vb.Length) or (Length=vc.Length),
2017-11-26 19:35:27 +03:00
'Vector.MP: длины векторов несовместимы');
2017-10-05 22:55:56 +03:00
if (Length=2) or (Length=3) then begin
var r:=new real[3,3];
for var j:=0 to Length-1 do begin
r[0,j]:=Value[j];
r[1,j]:=vb.Value[j];
r[2,j]:=vc.Value[j]
end;
Result:=r[0,0]*(r[1,1]*r[2,2]-r[1,2]*r[2,1])-
r[0,1]*(r[1,0]*r[2,2]-r[1,2]*r[2,0])+
r[0,2]*(r[1,0]*r[2,1]-r[1,1]*r[2,0])
end
else begin
2017-11-26 19:35:27 +03:00
Assert(false,'Vector.VP: Векторы должны быть двух или трехмерными');
2017-10-05 22:55:56 +03:00
Result:=MaxReal
end
end;
2017-11-26 19:35:27 +03:00
/// модуль вектора
2017-10-05 22:55:56 +03:00
function ModV:real;
begin
Result:=Sqrt(Value.Select(t->t*t).Sum);
end;
2017-11-26 19:35:27 +03:00
/// орт вектора
2017-10-05 22:55:56 +03:00
function Ort:Vector;
begin
Result:=new Vector(Length);
var invd:=1.0/Sqrt(Value.Select(t->t*t).Sum);
for var i:=0 to Length-1 do Result.Value[i]:=Value[i]*invd
end;
2017-11-26 19:35:27 +03:00
/// коллинеарность векторов
2017-10-05 22:55:56 +03:00
function IsCollinear(vb:Vector):boolean;
begin
2017-11-26 19:35:27 +03:00
Assert(Length=vb.Length,'Vector.IsCollinear: длины векторов несовместимы');
2017-10-05 22:55:56 +03:00
var k:=Value[0]/vb.Value[0];
Result:=true;
var i:=1;
while Result and (i<=Length-1) do begin
Result:=Result and (Value[i]/vb.Value[i]=k);
i+=1
end
end;
2017-11-26 19:35:27 +03:00
/// компланарность векторов
2017-10-05 22:55:56 +03:00
function IsCoplanar(vb,vc:Vector):boolean;
begin
Assert((Length=vb.Length) or (Length=vc.Length),
2017-11-26 19:35:27 +03:00
'Vector.IsCoplanar: длины векторов несовместимы');
2017-10-05 22:55:56 +03:00
Result:=Self.MP(vb,vc)=0.0
end;
2017-11-26 19:35:27 +03:00
/// возвращает копию вектора
2017-10-05 22:55:56 +03:00
function Copy:Vector;
begin
Result:=new Vector(Value)
end;
function Print(delim:string:=' '):Vector;
begin
Value.Print(delim);
Result:=new Vector(Value)
end;
function Println(delim:string:=' '):Vector;
begin
Value.Println(delim);
Result:=new Vector(Value)
end;
end;
{$endregion}
{$region Matrix}
type
2017-11-26 19:35:27 +03:00
/// М А Т Р И Ц Ы
2017-10-05 22:55:56 +03:00
Matrix=class
public
Value: array[,] of real;
RowCount: integer;
ColCount: integer;
constructor (pa:array[,] of real);
begin
(RowCount,ColCount):=(pa.RowCount,pa.ColCount);
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do
Value[i,j]:=pa[i,j]
end;
constructor (nRows,nCols:integer);
begin
Value:=new real[nRows,nCols];
RowCount:=nRows;
ColCount:=nCols
end;
constructor (nRows,nCols:integer; params pa:array of real);
begin
Assert(pa.Length=nRows*nCols,
2017-11-26 19:35:27 +03:00
'Создание MarixR: Неверное количество параметров');
2017-10-05 22:55:56 +03:00
(RowCount,ColCount):=(nRows,nCols);
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do
Value[i,j]:=pa[ColCount*i+j]
end;
class function operator + (pa,pb:Matrix):Matrix;
2017-11-26 19:35:27 +03:00
// сложение матриц
2017-10-05 22:55:56 +03:00
begin
Assert((pa.RowCount=pb.RowCount) and (pa.ColCount=pb.ColCount),
2017-11-26 19:35:27 +03:00
'Сложение MarixR: Несовпадение размеров');
Result:=new Matrix(pa.RowCount,pa.ColCount);
2017-10-05 22:55:56 +03:00
for var i:=0 to pa.RowCount-1 do
for var j:=0 to pa.ColCount-1 do
Result.Value[i,j]:=pa.Value[i,j]+pb.Value[i,j]
end;
class function operator - (pa:Matrix):Matrix;
2017-11-26 19:35:27 +03:00
// смена знака матрицы (унарный минус)
2017-10-05 22:55:56 +03:00
begin
Result:=new Matrix(pa.RowCount,pa.ColCount);
for var i:=0 to pa.RowCount-1 do
for var j:=0 to pa.ColCount-1 do
Result.Value[i,j]:=-pa.Value[i,j]
end;
class function operator - (pa,pb:Matrix):Matrix;
2017-11-26 19:35:27 +03:00
// вычитание матриц
2017-10-05 22:55:56 +03:00
begin
Assert((pa.RowCount=pb.RowCount) and (pa.ColCount=pb.ColCount),
2017-11-26 19:35:27 +03:00
'Вычитание MarixR: Несовпадение размеров');
2017-10-05 22:55:56 +03:00
Result:=new Matrix(pa.RowCount,pa.ColCount);
for var i:=0 to pa.RowCount-1 do
for var j:=0 to pa.ColCount-1 do
Result.Value[i,j]:=pa.Value[i,j]-pb.Value[i,j]
end;
class function operator * (r:real; pb:Matrix):Matrix;
2017-11-26 19:35:27 +03:00
// умножение скаляра на матрицу
2017-10-05 22:55:56 +03:00
begin
Result:=new Matrix(pb.RowCount,pb.ColCount);
for var i:=0 to pb.RowCount-1 do
for var j:=0 to pb.ColCount-1 do
Result.Value[i,j]:=r*pb.Value[i,j]
end;
class function operator * (pa:Matrix; r:real):=r*pa;
2017-11-26 19:35:27 +03:00
// умножение матрицы на скаляр
2017-10-05 22:55:56 +03:00
class function operator * (pa,pb:Matrix):Matrix;
2017-11-26 19:35:27 +03:00
// умножение матриц
2017-10-05 22:55:56 +03:00
begin
Assert(pa.ColCount=pb.RowCount,
2017-11-26 19:35:27 +03:00
'Matrix: умножение матриц. Несовпадение размеров');
2017-10-05 22:55:56 +03:00
Result:=new Matrix(pa.RowCount,pb.ColCount);
for var i:=0 to pa.RowCount-1 do
for var j:=0 to pb.ColCount-1 do begin
var s:=0.0;
for var k:=0 to pa.ColCount-1 do
s+=pa.Value[i,k]*pb.Value[k,j];
Result.Value[i,j]:=s
end
end;
class function operator * (pa:Matrix; pb:Vector):Vector;
2017-11-26 19:35:27 +03:00
// умножение матрицы на вектор
2017-10-05 22:55:56 +03:00
begin
Assert(pa.ColCount=pb.Length,
2017-11-26 19:35:27 +03:00
'Matrix: умножение матрицы на вектор. Несовпадение размеров');
2017-10-05 22:55:56 +03:00
Result:=new Vector(pa.RowCount);
for var i:=0 to pa.RowCount-1 do begin
var s:=0.0;
for var j:=0 to pa.ColCount-1 do
s+=pa.Value[i,j]*pb.Value[j];
Result.Value[i]:=s
end
end;
class function operator * (pa:Vector; pb:Matrix):Vector;
2017-11-26 19:35:27 +03:00
// умножение вектора на матрицу
2017-10-05 22:55:56 +03:00
begin
Assert(pa.Length=pb.RowCount,
2017-11-26 19:35:27 +03:00
'Matrix: умножение вектора на матрицу. Несовпадение размеров');
2017-10-05 22:55:56 +03:00
Result:=new Vector(pb.ColCount);
for var j:=0 to pb.ColCount-1 do begin
var s:=0.0;
for var i:=0 to pb.RowCount-1 do
s+=pa.Value[i]*pb.Value[i,j];
Result.Value[j]:=s
end
end;
2017-11-26 19:35:27 +03:00
/// возвращает диагональную матрицу n*n на базе значения r
2017-10-05 22:55:56 +03:00
class function Diag(n:integer; r:real):Matrix;
begin
Result:=new Matrix(n,n);
for var i:=0 to n-1 do
for var j:=0 to n-1 do
Result.Value[i,j]:=i=j?r:0.0
end;
2017-11-26 19:35:27 +03:00
/// выделение строки
2017-10-05 22:55:56 +03:00
function Row(k:integer; base:integer:=1):Vector;
begin
if base<>0 then base:=1;
Result:=new Vector(ColCount);
Result.Value:=Value.Row(k-base)
end;
2017-11-26 19:35:27 +03:00
/// замена строки
2017-10-05 22:55:56 +03:00
procedure SetRow(v:Vector; k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
Value.SetRow(k-base,v.Value)
end;
2017-11-26 19:35:27 +03:00
/// вставка строки послк указанной
2017-10-05 22:55:56 +03:00
procedure InsertRowAfter(v:Vector; k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
var k1:=k-base;
Assert((k1 >= 0) and (k1 < RowCount),
2017-11-26 19:35:27 +03:00
'Matrix.InsertRowAfter: Недопустимый номер строки');
2017-10-05 22:55:56 +03:00
Assert(ColCount=v.Length,
2017-11-26 19:35:27 +03:00
'Matrix.InsertRowAfter: Несовместимая длина вектора');
2017-10-05 22:55:56 +03:00
var tm:=new Matrix(RowCount+1,ColCount);
for var i:=0 to k1 do
for var j:=0 to ColCount-1 do tm.Value[i,j]:=Value[i,j];
for var j:=0 to ColCount-1 do tm.Value[k1+1,j]:=v.Value[j];
for var i:=k1+2 to RowCount do
for var j:=0 to ColCount-1 do tm.Value[i,j]:=Value[i-1,j];
RowCount+=1;
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do Value[i,j]:=tm.Value[i,j];
end;
2017-11-26 19:35:27 +03:00
/// вставка строки перед указанной
2017-10-05 22:55:56 +03:00
procedure InsertRowBefore(v:Vector; k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
var k1:=k-base;
Assert((k1 >= 0) and (k1 < RowCount),
2017-11-26 19:35:27 +03:00
'Matrix.InsertRowBefore: Недопустимый номер строки');
2017-10-05 22:55:56 +03:00
Assert(ColCount=v.Length,
2017-11-26 19:35:27 +03:00
'Matrix.InsertRowBefore: Несовместимая длина вектора');
2017-10-05 22:55:56 +03:00
var tm:=new Matrix(RowCount+1,ColCount);
for var i:=0 to k1-1 do
for var j:=0 to ColCount-1 do tm.Value[i,j]:=Value[i,j];
for var j:=0 to ColCount-1 do tm.Value[k1,j]:=v.Value[j];
for var i:=k1+1 to RowCount do
for var j:=0 to ColCount-1 do tm.Value[i,j]:=Value[i-1,j];
RowCount+=1;
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do Value[i,j]:=tm.Value[i,j];
end;
2017-11-26 19:35:27 +03:00
/// удаление строки
2017-10-05 22:55:56 +03:00
procedure DeleteRow(k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
var k1:=k-base;
Assert((k1 >= 0) and (k1 < RowCount),
2017-11-26 19:35:27 +03:00
'Matrix.DeleteRowAfter: Недопустимый номер строки');
2017-10-05 22:55:56 +03:00
var tm:=new Matrix(RowCount-1,ColCount);
for var i:=0 to k1-1 do
for var j:=0 to ColCount-1 do tm.Value[i,j]:=Value[i,j];
for var i:=k1+1 to RowCount-1 do
for var j:=0 to ColCount-1 do tm.Value[i-1,j]:=Value[i,j];
RowCount-=1;
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do Value[i,j]:=tm.Value[i,j];
end;
2017-11-26 19:35:27 +03:00
/// Суммирование j-й строки с i-й, результат в i-й строке
2017-10-05 22:55:56 +03:00
procedure AddRow(i,j:integer; base:integer:=1);
begin
if base<>0 then base:=1;
for var k:=0 to ColCount-1 do
Value[i-base,k]+=Value[j-base,k]
end;
2017-11-26 19:35:27 +03:00
/// Вычитание j-й строки из i-й, результат в i-й строке
2017-10-05 22:55:56 +03:00
procedure SubRow(i,j:integer; base:integer:=1);
begin
if base<>0 then base:=1;
for var k:=0 to ColCount-1 do
Value[i-base,k]-=Value[j-base,k]
end;
2017-11-26 19:35:27 +03:00
/// Умножение строки на скаляр
2017-10-05 22:55:56 +03:00
procedure MultRow(k:integer; r:real; base:integer:=1);
begin
if base<>0 then base:=1;
for var j:=0 to ColCount-1 do
Value[k-base,j]*=r
end;
2017-11-26 19:35:27 +03:00
/// Обмен местами строк i и j
2017-10-05 22:55:56 +03:00
procedure SwapRows(i,j:integer; base:integer:=1);
begin
if base<>0 then base:=1;
Value.SwapRows(i-base,j-base)
end;
2017-11-26 19:35:27 +03:00
/// выделение столбца
2017-10-05 22:55:56 +03:00
function Col(k:integer; base:integer:=1):Vector;
begin
if base<>0 then base:=1;
Result:=new Vector(RowCount);
Result.Value:=Value.Col(k-base)
end;
2017-11-26 19:35:27 +03:00
/// замена столбца
2017-10-05 22:55:56 +03:00
procedure SetCol(v:Vector; k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
Value.SetCol(k-base,v.Value)
end;
2017-11-26 19:35:27 +03:00
/// вставка столбца послк указанного
2017-10-05 22:55:56 +03:00
procedure InsertColAfter(v:Vector; k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
var k1:=k-base;
Assert((k1 >= 0) and (k1 < ColCount),
2017-11-26 19:35:27 +03:00
'Matrix.InsertColAfter: Недопустимый номер столбца');
2017-10-05 22:55:56 +03:00
Assert(RowCount=v.Length,
2017-11-26 19:35:27 +03:00
'Matrix.InsertColAfter: Несовместимая длина вектора');
2017-10-05 22:55:56 +03:00
var tm:=new Matrix(RowCount,ColCount+1);
for var j:=0 to k1 do
for var i:=0 to RowCount-1 do tm.Value[i,j]:=Value[i,j];
for var i:=0 to RowCount-1 do tm.Value[i,k1+1]:=v.Value[i];
for var j:=k1+2 to ColCount do
for var i:=0 to RowCount-1 do tm.Value[i,j]:=Value[i,j-1];
ColCount+=1;
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do Value[i,j]:=tm.Value[i,j];
end;
2017-11-26 19:35:27 +03:00
/// вставка столбца перед указанным
2017-10-05 22:55:56 +03:00
procedure InsertColBefore(v:Vector; k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
var k1:=k-base;
Assert((k1 >= 0) and (k1 < ColCount),
2017-11-26 19:35:27 +03:00
'Matrix.InsertColBefore: Недопустимый номер столбца');
2017-10-05 22:55:56 +03:00
Assert(RowCount=v.Length,
2017-11-26 19:35:27 +03:00
'Matrix.InsertColBefore: Несовместимая длина вектора');
2017-10-05 22:55:56 +03:00
var tm:=new Matrix(RowCount,ColCount+1);
for var j:=0 to k1-1 do
for var i:=0 to RowCount-1 do tm.Value[i,j]:=Value[i,j];
for var i:=0 to RowCount-1 do tm.Value[i,k1]:=v.Value[i];
for var j:=k1+1 to ColCount do
for var i:=0 to RowCount-1 do tm.Value[i,j]:=Value[i,j-1];
ColCount+=1;
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do Value[i,j]:=tm.Value[i,j];
end;
2017-11-26 19:35:27 +03:00
/// удаление столбца
2017-10-05 22:55:56 +03:00
procedure DeleteCol(k:integer; base:integer:=1);
begin
if base<>0 then base:=1;
var k1:=k-base;
Assert((k1 >= 0) and (k1 < ColCount),
2017-11-26 19:35:27 +03:00
'Matrix.DeleteCol: Недопустимый номер столбца');
2017-10-05 22:55:56 +03:00
var tm:=new Matrix(RowCount,ColCount-1);
for var j:=0 to k1-1 do
for var i:=0 to RowCount-1 do tm.Value[i,j]:=Value[i,j];
for var j:=k1+1 to ColCount-1 do
for var i:=0 to RowCount-1 do tm.Value[i,j-1]:=Value[i,j];
ColCount-=1;
Value:=new real[RowCount,ColCount];
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do Value[i,j]:=tm.Value[i,j];
end;
2017-11-26 19:35:27 +03:00
/// Суммирование j-го столбца с i-м, результат в i-м столбце
2017-10-05 22:55:56 +03:00
procedure AddCol(i,j:integer; base:integer:=1);
begin
if base<>0 then base:=1;
for var k:=0 to RowCount-1 do
Value[k,i-base]+=Value[k,j-base]
end;
2017-11-26 19:35:27 +03:00
/// Вычитание j-го столбца из i-го, результат в i-м столбце
2017-10-05 22:55:56 +03:00
procedure SubCol(i,j:integer; base:integer:=1);
begin
if base<>0 then base:=1;
for var k:=0 to RowCount-1 do
Value[k,i-base]-=Value[k,j-base]
end;
2017-11-26 19:35:27 +03:00
/// Умножение столбца на скаляр
2017-10-05 22:55:56 +03:00
procedure MultCol(k:integer; r:real; base:integer:=1);
begin
if base<>0 then base:=1;
for var i:=0 to RowCount-1 do
Value[i,k-base]*=r
end;
2017-11-26 19:35:27 +03:00
/// Обмен местами столбцов i и j
2017-10-05 22:55:56 +03:00
procedure SwapCols(i,j:integer; base:integer:=1);
begin
if base<>0 then base:=1;
Value.SwapCols(i-base,j-base)
end;
2017-11-26 19:35:27 +03:00
/// Транспонирование матрицы
2017-10-05 22:55:56 +03:00
function Transpose:Matrix;
begin
Result:=new Matrix(ColCount,RowCount);
Result.Value:=PABCSystem.Transpose(Value);
end;
2017-11-26 19:35:27 +03:00
/// вывод матрицы на экран
2017-10-05 22:55:56 +03:00
function Println(w:integer:=6; d:integer:=2):Matrix;
begin
Result:=new Matrix(RowCount,ColCount);
for var i:=0 to RowCount-1 do begin
for var j:=0 to ColCount-1 do begin
Result.Value[i,j]:=Value[i,j];
Write(Result.Value[i,j]:w:d)
end;
Writeln
end
end;
2017-11-26 19:35:27 +03:00
/// детерминант матрицы
2017-10-05 22:55:56 +03:00
function Det:real;
begin
var r:=new Decomp(Value);
Result:=r.det
end;
2017-11-26 19:35:27 +03:00
/// обращение квадратной матрицы
2017-10-05 22:55:56 +03:00
function Inv:Matrix;
begin
2017-11-26 19:35:27 +03:00
Assert(RowCount=ColCount,'Matrix.Inv: матрица не квадратная');
2017-10-05 22:55:56 +03:00
Result:=new Matrix(RowCount,ColCount);
var r:=new Decomp(Value);
if r.cond<MaxReal then begin
for var j:=0 to ColCount-1 do begin
var v:=ArrGen(RowCount,i->i=j?1.0:0.0);
r.Solve(v);
for var i:=0 to RowCount-1 do Result.Value[i,j]:=v[i]
end
end
else Result.Value:=MatrFill(RowCount,ColCount,MaxReal);
end;
/// Решение системы линейных алгебраических уравнения (СЛАУ)
2017-10-05 22:55:56 +03:00
function SLAU(vb:Vector; var cond:real):Vector;
2017-11-26 19:35:27 +03:00
// Возвращает вектор, содержащий решение СЛАУ.
// Заносит в cond число обусловленности исходной матрицы.
// Если cond велико, исходная матрица вырождена или близка к этому и
// вектором результата пользоваться нельзя.
// Является оболочкой к самостоятельному классу Decomp.
2017-10-05 22:55:56 +03:00
begin
Assert(vb.Length=ColCount,
2017-11-26 19:35:27 +03:00
'Matrix.SLAU: длина вектора vb несовместима с размером матрицы');
2017-10-05 22:55:56 +03:00
var r:=new Decomp(Value);
cond:=r.cond;
Result:=new Vector(vb.Length);
Result.Value:=PABCSystem.Copy(vb.Value);
r.Solve(Result.Value);
end;
2017-11-26 19:35:27 +03:00
/// возвращает копию матрицы
2017-10-05 22:55:56 +03:00
function Copy:Matrix;
begin
Result:=new Matrix(RowCount,ColCount);
for var i:=0 to RowCount-1 do
for var j:=0 to ColCount-1 do
Result.Value[i,j]:=Value[i,j]
end;
end;
{$endregion}
{$region Polynom}
type
2017-11-26 19:35:27 +03:00
/// Полиномиальная арифметика
2017-10-05 22:55:56 +03:00
Polynom=class
public
2017-11-26 19:35:27 +03:00
a:array of real; // коэффициенты по возрастанию степени
n:integer; // кол-во элементов в полиноме
eps:real; // оценка точности при экономизации полинома
2017-10-05 22:55:56 +03:00
constructor(params p:array of real);
begin
a:=PABCSystem.Copy(p);
n:=a.Length
end;
constructor Create(n:integer);
begin
a:=ArrFill(n,0.0);
Self.n:=n
end;
2017-11-26 19:35:27 +03:00
/// Значение полинома P(x)
2017-10-05 22:55:56 +03:00
function Value(x:real):real;
begin
Result:=a[0];
var k:=1.0;
for var i:=1 to n-1 do begin
k*=x;
Result+=a[i]*k
end;
end;
2017-11-26 19:35:27 +03:00
/// коэффициенты производной полинома
2017-10-05 22:55:56 +03:00
function PDif:Polynom;
begin
Result:=new Polynom(n-1);
for var i:=0 to n-2 do Result.a[i]:=a[i+1]*(i+1)
end;
2017-11-26 19:35:27 +03:00
/// коэффициенты неопределенного интеграла от полинома
2017-10-05 22:55:56 +03:00
function PInt:Polynom;
begin
Result:=new Polynom(Arr(0.0)+a);
for var i:=1 to Result.n-1 do Result.a[i]/=i
end;
class function operator + (p,q:Polynom):Polynom;
begin
var m:=Max(p.n,q.n);
Result:=new Polynom(m);
if p.n>q.n then begin
for var i:=0 to q.n-1 do Result.a[i]:=p.a[i]+q.a[i];
for var i:=q.n to m-1 do Result.a[i]:=p.a[i]
end
else begin
for var i:=0 to p.n-1 do Result.a[i]:=p.a[i]+q.a[i];
for var i:=p.n to m-1 do Result.a[i]:=q.a[i]
end
end;
class function operator + (r:real; p:Polynom):Polynom;
begin
Result:=new Polynom(p.a);
Result.a[0]+=r
end;
class function operator + (p:Polynom; r:real):=r+p;
class function operator - (p:Polynom):=
new Polynom(p.a.Select(x->-x).ToArray);
class function operator - (p,q:Polynom):Polynom;
begin
var m:=Max(p.n,q.n);
Result:=new Polynom(m);
if p.n>q.n then begin
for var i:=0 to q.n-1 do Result.a[i]:=p.a[i]-q.a[i];
for var i:=q.n to m-1 do Result.a[i]:=p.a[i]
end
else begin
for var i:=0 to p.n-1 do Result.a[i]:=p.a[i]-q.a[i];
for var i:=p.n to m-1 do Result.a[i]:=-q.a[i]
end
end;
class function operator - (r:real; p:Polynom):Polynom;
begin
Result:=-p;
Result.a[0]+=r
end;
class function operator - (p:Polynom; r:real):Polynom;
begin
Result:=new Polynom(p.a);
Result.a[0]-=r
end;
class function operator * (p,q:Polynom):Polynom;
begin
var m:=p.n+q.n-1;
Result:=new Polynom(m);
for var i:=0 to p.n-1 do
for var j:=0 to q.n-1 do
Result.a[i+j]+=p.a[i]*q.a[j]
end;
class function operator * (r:real; p:Polynom):Polynom;
begin
Result:=new Polynom(p.a);
for var i:=0 to p.n-1 do Result.a[i]*=r
end;
class function operator * (p:Polynom; r:real):=r*p;
class function operator = (p,q:Polynom):boolean;
begin
if p.n<>q.n then Result:=false
else begin
Result:=true;
for var i:=0 to p.n-1 do
if Abs(p.a[i]-q.a[i])>0 then begin
Result:=false; Exit
end
end
end;
2017-11-26 19:35:27 +03:00
/// Копия полинома
2017-10-05 22:55:56 +03:00
function Copy:=new Polynom(a);
class function operator / (p,q:Polynom):(Polynom,Polynom);
begin
var np:=p.n-q.n+1;
if np<=0 then begin
Result:=(new Polynom(1),p.Copy);
Exit
end;
var pp:=new Polynom(np);
var s:=p.Copy;
var ns:=q.n-1;
var i:=np;
repeat
pp.a[i-1]:=s.a[i+ns-1]/q.a.Last;
for var k:=0 to ns-1 do begin
var j:=k-1+i;
s.a[j]-=pp.a[i-1]*q.a[k]
end;
i-=1
until i<=0;
SetLength(s.a,ns);
s.n:=ns;
Result:=(pp,s)
end;
class function operator / (p:Polynom; q:real):Polynom;
begin
Result:=p.Copy;
for var i:=0 to Result.n-1 do Result.a[i]/=q
end;
class function operator / (p:real; q:Polynom):=(p,q.Copy);
2017-11-26 19:35:27 +03:00
/// Вывод коэффициентов полинома с разделителем
2017-10-05 22:55:56 +03:00
function Print(delim:char:=' '):Polynom;
begin
a.Print(delim);
Result:=Self.Copy
end;
2017-11-26 19:35:27 +03:00
/// Замена аргумента х на at+b
2017-10-05 22:55:56 +03:00
function Polyx(a,b:real):Polynom;
begin
Result:=new Polynom(n);
var z:=new real[n];
var w:=ArrFill(n,1.0);
Result.a[0]:=Self.a[0];
z[0]:=1;
for var i:=1 to n-1 do begin
z[i]:=b*z[i-1];
Result.a[0]+=Self.a[i]*z[i]
end;
for var j:=1 to n-1 do begin
w[0]*=a; Result.a[j]:=Self.a[j]*w[0];
for var i:=j+1 to n-1 do begin
var k:=i-j;
w[k]:=a*w[k]+w[k-1];
Result.a[j]+=Self.a[i]*w[k]*z[k]
end
end
end;
2017-11-26 19:35:27 +03:00
/// Экономизация полинома на [-1;1]
2017-10-05 22:55:56 +03:00
function EconomSym(h,limit:real):Polynom;
2017-11-26 19:35:27 +03:00
// Переработка алгоритма 38б
// В кн.: Агеев М.И., Алик В.П., Марков Ю.И.
// Библиотека алгоритмов 1б-50б. (Справочное пособие).
// М., "Сов.радио", 1975
2017-10-05 22:55:56 +03:00
begin
var n1:=n-1;
var t:=new real[n];
Result:=Self.Copy;
var s:real;
eps:=0;
while n1>=2 do begin
t[n1]:=-Result.a[n1];
var k:=n1;
while k>=2 do begin
t[k-2]:=-t[k]*Sqr(h)*k*(k-1)/((n1+k-2)*(n1-k+2));
k-=2
end;
s:=n1.IsEven?a[0]:t[1]/n1;
if eps+Abs(s)<limit then begin
eps+=Abs(s);
k:=n1;
while k>=0 do begin Result.a[k]+=t[k]; k-=2 end;
n1-=1;
end
else break
end;
SetLength(Result.a,n1+1);
Result.n:=n1+1;
Result.eps:=eps
end;
2017-11-26 19:35:27 +03:00
/// Экономизация полинома на [0,1]
2017-10-05 22:55:56 +03:00
function EconomUnsym(h,limit:real):Polynom;
2017-11-26 19:35:27 +03:00
// Переработка алгоритма 37б
// В кн.: Агеев М.И., Алик В.П., Марков Ю.И.
// Библиотека алгоритмов 1б-50б. (Справочное пособие).
// М., "Сов.радио", 1975
2017-10-05 22:55:56 +03:00
begin
var n1:=n-1;
var t:=new real[n];
Result:=Self.Copy;
eps:=0.0;
while n1>=1 do begin
t[n1]:=-Result.a[n1];
for var k:=n1 downto 1 do
t[k-1]:=-t[k]*h*k*(k-0.5)/((n1+k-1)*(n1-k+1));
if eps+Abs(t[0])<limit then begin
eps+=Abs(t[0]);
for var k:=n1 downto 0 do Result.a[k]+=t[k];
n1-=1;
end
else break
end;
SetLength(Result.a,n1+1);
Result.n:=n1+1;
Result.eps:=eps
end;
2017-11-26 19:35:27 +03:00
/// Вывод коэффициентов полинома с разделителем и сменой строки
2017-10-05 22:55:56 +03:00
function Println(delim:char:=' '):Polynom;
begin
a.Println(delim);
Result:=Self.Copy
end;
2017-11-26 19:35:27 +03:00
/// Вывод полинома в "математическом" виде со сменой строки
2017-10-05 22:55:56 +03:00
procedure PrintlnBeauty(p:char:='x');
begin
var s:string:='';
for var i:=Self.n-1 downto 0 do begin
var t:=Self.a[i];
case i of
0: if t<0 then s+=t.ToString
else if t>0 then s+='+'+t.ToString
else if s.Length=0 then s:='0';
1: if t<0 then
if t<>-1 then s+=t.ToString+p
else s+='-'+p
else if t>0 then
if t<>1 then s+='+'+t.ToString+p
else s+='+'+p;
else
if t<0 then
if t<>-1 then s+=t.ToString+p+'^'+i.ToString
else s+='-'+p+'^'+i.ToString
else if t>0 then
if t<>1 then s+='+'+t.ToString+p+'^'+i.ToString
else s+='+'+p+'^'+i.ToString
end
end;
if s[1]='+' then s:=s[2:];
Writeln(s)
end;
end;
{$endregion}
{$region PolRt}
type
2017-11-26 19:35:27 +03:00
/// Нахождение всех корней полинома с действительными коэффициентами
/// методом Ньютона-Рафсона (не выше 36-й степени)
2017-10-05 22:55:56 +03:00
PolRt=class
private
p:Polynom;
public
ier:integer;
constructor(p:Polynom);
begin
Self.p:=p;
end;
2017-11-26 19:35:27 +03:00
/// Вывод корней полинома в массив
2017-10-05 22:55:56 +03:00
function Value:array of complex;
//
2017-11-26 19:35:27 +03:00
// Существенно переработанная для языка PascalABC.NET 3.2
// подпрограмма DPOLRT из библиотеки SSP
// (она же - "Пакет научных программ на языке Фортран-4").
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Коэффициенты полинома задаются в порядке возрастания степени.
// ier=0 - ошибок не обнаружено
// ier=1 - мало элементов для построения полинома
// ier=2 - степень полинома превышает 36
// ier=3 - не удалось достигнуть приемлемой точности решения за 500 шагов
// ier=4 - коэффициент при старшей степени полинома нулевой
2017-10-05 22:55:56 +03:00
//
begin
var m:=p.n-2;
if m>35 then begin ier:=2; Exit end;
if p.a[m+1]=0 then begin ier:=4; Exit end;
if m<=0 then begin ier:=1; Exit end;
var SSq:complex->real:=x->Sqr(x.Real)+Sqr(x.Imaginary);
ier:=0;
Result:=new complex[m+1];
var sumsq,temp,alpha:real;
var x:complex;
var L50, L60, L100, L135: boolean;
var (ifit,n2,n,nx,nxx):=(0,0,m,m,m+1);
var kj1:=nxx;
var cof:=p.a.Reverse.ToArray;
while n>=0 do begin
var xo:=cplx(0.00500101,0.01000101);
var ini:=0;
(L50,L60,L135):=(false,false,false);
while true do begin
if not l60 then begin
xo:=cplx(-10*xo.Imaginary,-10*xo.Real); x:=xo; ini+=1
end;
L100:=false;
var xpr:=cplx(0.0,0.0);
for var ict:=1 to 500 do begin
var u:=cplx(cof[n+1],0.0);
var xt:=cplx(1.0,0.0);
var ux:=cplx(0.0,0.0);
if u.Real=0 then begin
(x,L50,L135):=(0.0,true,true); nx-=1; nxx-=1;
break
end;
for var i:=1 to n+1 do begin
temp:=cof[n-i+1];
var xt2:=x*xt;
u+=temp*xt2; ux+=Conjugate(i*temp*xt);
xt:=xt2
end;
sumsq:=SSq(ux);
if sumsq=0.0 then
if ifit=0 then begin L60:=false; continue end
else begin x:=xpr; L50:=true; break end;
var dx:=-u*ux/sumsq; x+=dx;
if Abs(dx.Imaginary)+Abs(dx.Real)<1e-12 then begin
L100:=true; break
end
end;
if not L50 then begin
L100:=L100 or (ifit<>0);
if not L100 then
if ini>=5 then begin ier:=3; Exit end
else begin L60:=false; continue end;
for var l:=0 to nxx do Swap(p.a[kj1-l],cof[l]);
Swap(n,nx);
if ifit=0 then begin
(ifit,xpr,L60):=(1,x,true);
continue
end
else break
end
end;
L50:=false;
if L135 then begin
x:=cplx(x.Real,0.0); (sumsq,alpha,L135):=(0.0,x.Real,false); n-=1;
end
else begin
ifit:=0;
if Abs(x.Imaginary)-1e-10*Abs(x.Real)>=0 then begin
alpha:=2*x.Real; sumsq:=SSq(x); n-=2
end
else begin
x:=cplx(x.Real,0.0); (sumsq,alpha):=(0.0,x.Real); n-=1
end
end;
cof[1]:=cof[1]+alpha*cof[0];
for var l:=1 to n do cof[l+1]+=alpha*cof[l]-sumsq*cof[l-1];
Result[n2]:=x; n2+=1;
if sumsq<>0.0 then begin
Result[n2]:=Conjugate(x); n2+=1
end
end
end;
end;
{$endregion}
{$region Quanc8}
type
2017-11-26 19:35:27 +03:00
/// Адаптивная квадратурная программа численного интегрирования
2017-10-05 22:55:56 +03:00
Quanc8=class
private
fun:real->real;
a,b,abserr,relerr:real;
public
constructor(f:real->real; a,b,abserr,relerr:real);
begin
fun:=f;
Self.a:=a;
Self.b:=b;
Self.abserr:=abserr;
Self.relerr:=relerr;
end;
2017-11-26 19:35:27 +03:00
/// Вычисление и возврат значения интеграла в виде кортежа
/// Непосредственно результат первый в кортеже
2017-10-05 22:55:56 +03:00
function Value:(real,real,real,integer);
//
2017-11-26 19:35:27 +03:00
// a, b - границы интервала интегрирования
// abserr - заданная предельная абсолютная ошибка
// relerr - заданная предельная относительная ошибка
// * Возвращаемый кортеж *
// [0] - результат интегрирования (res)
// [1] - оценка абсолютной ошибки результата (errest)
// [2] - индикатор надежности (flag)
// [3] - число точек, в которых вычислялась функция (nofun)
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Если flag ненулевой, но мал, результат еще можно принять,
// если велик - программа непригодна для такой функции.
// flag=XXX.YYY, где ХХХ - количество подинтервалов
// длины (b-a)/2^30 с недопустимо большой ошибкой вычисления,
// YYY - часть необработанного основного интервала.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Является результатом переработки одноименной фортран-программы,
// приведенной в кн. Дж.Форсайт, М.Малькольм, К.Моулер
// "Машинные методы математических вычислений",
// М., "Мир", 1980.
2017-10-05 22:55:56 +03:00
//
begin
2017-11-26 19:35:27 +03:00
// этап 1
2017-10-05 22:55:56 +03:00
var (res,errest,flag):=(0.0,0.0,0.0);
var nofun:=0;
var (levmin,levmax,levout,nomax):=(1,30,6,5000);
var nofin:=nomax-8*(levmax-levout+128);
var k:=14175.0;
var (w0,w1,w2,w3,w4):=(3956.0/k,23552.0/k,-3712.0/k,41984.0/k,-18160.0/k);
var (area,cor11):=(0.0,0.0);
if(a=b) then begin Result:=(0.0,0.0,0.0,0); Exit end;
2017-11-26 19:35:27 +03:00
// этап 2
2017-10-05 22:55:56 +03:00
var (lev,nim):=(0,1);
var x:=new real[17];
(x[0],x[16]):=(a,b);
var qprev:=0.0;
var f:=new real[17];
f[0]:=fun(x[0]);
var stone:=(b-a)/16;
x[8]:=(x[0]+x[16])/2;
x[4]:=(x[0]+x[8])/2;
x[12]:=(x[8]+x[16])/2;
x[2]:=(x[0]+x[4])/2;
x[6]:=(x[4]+x[8])/2;
x[10]:=(x[8]+x[12])/2;
x[14]:=(x[12]+x[16])/2;
var j:=2;
while j<=16 do begin f[j]:=fun(x[j]); j+=2 end;
nofun:=9;
2017-11-26 19:35:27 +03:00
// этап 3
2017-10-05 22:55:56 +03:00
var qright:=new real[32];
var fsave:=new real[9,31];
var xsave:=new real[9,31];
while True do begin
x[1]:=(x[0]+x[2])/2; f[1]:=fun(x[1]);
j:=3;
while j<=15 do begin
x[j]:=(x[j-1]+x[j+1])/2; f[j]:=fun(x[j]); j:=j+2
end;
nofun+=8;
var step:=(x[16]-x[0])/16;
var qleft:=(w0*(f[0]+f[8])+w1*(f[1]+f[7])+w2*(f[2]+f[6])+w3*(f[3]+f[5])+
w4*f[4])*step;
qright[lev+1]:=(w0*(f[8]+f[16])+w1*(f[9]+f[15])+w2*(f[10]+f[14])+
w3*(f[11]+f[13])+w4*f[12])*step;
var qnow:=qleft+qright[lev+1];
var qdiff:=qnow-qprev; area+=qdiff;
2017-11-26 19:35:27 +03:00
// Этап 4
2017-10-05 22:55:56 +03:00
var esterr:=Abs(qdiff)/1023;
var tolerr:=Max(abserr,relerr*Abs(area))*(step/stone);
var L70:=false;
if lev>=levmin then begin;
if lev>=levmax then begin
flag+=1; L70:=true
end
else if nofun>nofin then begin
nofin*=2; levmax:=levout; flag+=(b-x[0])/(b-a); L70:=true
end
else if esterr<=tolerr then L70:=true;
end;
2017-11-26 19:35:27 +03:00
// этап 5
2017-10-05 22:55:56 +03:00
if not L70 then begin
nim*=2; lev+=1;
for var i:=1 to 8 do begin
fsave[i,lev]:=f[i+8];
xsave[i,lev]:=x[i+8]
end;
qprev:=qleft;
for var i:=8 downto 1 do begin
f[2*i]:=f[i];
x[2*i]:=x[i]
end;
continue
end;
2017-11-26 19:35:27 +03:00
// этап 7
2017-10-05 22:55:56 +03:00
res+=qnow; errest+=esterr; cor11+=qdiff/1023;
while nim.IsOdd do begin
nim:=nim div 2;
lev-=1
end;
nim+=1;
if lev<=0 then break;
qprev:=qright[lev]; x[0]:=x[16]; f[0]:=f[16];
for var i:=1 to 8 do begin
f[2*i]:=fsave[i,lev];
x[2*i]:=xsave[i,lev]
end;
end;
2017-11-26 19:35:27 +03:00
// этап 8
2017-10-05 22:55:56 +03:00
res+=cor11;
if errest=0 then begin
Result:=(res,errest,flag,nofun); Exit
end;
repeat
var temp:=Abs(res)+errest;
if temp<>Abs(res) then begin
Result:=(res,errest,flag,nofun); Exit
end;
errest*=2;
until false;
end;
end;
{$endregion}
{$region RKF45}
type
2017-11-26 19:35:27 +03:00
/// Метод Рунге-Кутты-Фельберга 4-5 порядка
2017-10-05 22:55:56 +03:00
RKF45=class
2017-11-26 19:35:27 +03:00
// За основу взят текст программы RKF45 на языке Fortran-90,
// находящейся на сайте https://www.sc.fsu.edu
2017-10-05 22:55:56 +03:00
// ( Dept. of Scientific Computing, Florida State University ),
2017-11-26 19:35:27 +03:00
// распространяемой на условиях GNU LGPL
2017-10-05 22:55:56 +03:00
private
2017-11-26 19:35:27 +03:00
neqn:integer; // количество уравнений системы
2017-10-05 22:55:56 +03:00
abserr_save:real;
relerr_save:real;
h:real;
epsilon:=1.2e-15;
flag_save:integer;
init:integer;
kflag:integer;
kop:integer;
nfe:integer;
f1:array of real;
f2:array of real;
f3:array of real;
f4:array of real;
f5:array of real;
p:procedure(x:real; y,yp:array of real);
function Sign(a,b:real):=b<0?-a:a;
procedure Fehl(s:array of real; t,h:real);
//
2017-11-26 19:35:27 +03:00
// Метод Рунге-Кутты-Фельберга 4-5 порядка.
// Интегрируется система обыкновенных дифференциальных уравнений 1-го порядка
// вида dy(i)/dt = f(t, y(1), y(2) ... ,
// где значения функции y(i) и значения её производных yp(i) заданы
// в начальной точке t.
// Метод продолжает решение на фиксированный шаг h и помещает в массив s
// приближение к решению в точке t+h, имеющее пятый порядок точности.
// В формулах произведена группировка с целью уменьшить потерю
// верных знаков.
// Чтобы можно было различать значения независимых аргументов, при вызове
// Fehl не следует задавать для h значение, меньшее ошибки округления t,
// умноженной на 13.
2017-10-05 22:55:56 +03:00
//
begin
var ch:=h/4.0;
for var i:=0 to neqn-1 do f5[i]:=y[i]+ch*yp[i];
p(t+ch,f5,f1); ch:=3.0*h/32.0;
for var i:=0 to neqn-1 do f5[i]:=y[i]+ch*(yp[i]+3.0*f1[i]);
p(t+3.0*h/8.0,f5,f2); ch:=h/2197.0;
for var i:=0 to neqn-1 do
f5[i]:=y[i]+ch*(1932.0*yp[i]+7296.0*f2[i]-7200.0*f1[i]);
p(t+12.0*h/13.0,f5,f3); ch:=h/4104.0;
for var i:=0 to neqn-1 do
f5[i]:=y[i]+ch*(8341.0*yp[i]-845.0*f3[i]+29440.0*f2[i]-32832.0*f1[i]);
p(t+h,f5,f4); ch:=h/20520.0;
for var i:=0 to neqn-1 do
f1[i]:=y[i]+ch*(-6080.0*yp[i]+9295.0*f3[i]-5643.0*f4[i]+41040.0*f1[i]-
28352.0*f2[i]);
p(t+h/2.0,f1,f5); ch:=h/7618050.0;
for var i:=0 to neqn-1 do
s[i]:=y[i]+ch*(902880.0*yp[i]+3855735.0*f3[i]-1371249.0*f4[i]+
3953664.0*f2[i]+277020.0*f5[i])
end;
procedure FlagDev;
2017-11-26 19:35:27 +03:00
// обработчик состояния флага завершенеия шага flag
// Выводит сообщения при MsgOn=true
2017-10-05 22:55:56 +03:00
begin
if MsgOn then
case flag of
2017-11-26 19:35:27 +03:00
-5:Writeln('RKF45: Критическая ошибка',NewLine,'kFlag=5 и AbsErr=0');
-4:Writeln('RKF45: Критическая ошибка',NewLine,
'kFlag=6 и RelErr<=RelErr_save и AbsErr<=AbsErr_save');
-3:Writeln('RKF45: Критическая ошибка',NewLine,
'Невозможно продолжить интегрирование',NewLine,
'Пользователь не отреагировал на flag=5..8');
1: Writeln('Ненадлежащий вызов');
3: Writeln('Установлено: relerr,abserr = ',relerr,',',abserr);
4: Writeln('Требуется слишком много шагов');
2017-10-05 22:55:56 +03:00
5: begin
abserr:=0.2e-5;
2017-11-26 19:35:27 +03:00
Writeln('Установлено: relerr,abserr = ',relerr,',',abserr);
2017-10-05 22:55:56 +03:00
flag:=0
end;
6: begin
relerr*=10.0;
2017-11-26 19:35:27 +03:00
Writeln('Установлено: relerr,abserr = ',relerr,',',abserr);
2017-10-05 22:55:56 +03:00
flag:=0
end;
7: begin
2017-11-26 19:35:27 +03:00
Writeln('Сложно получить требуемую точность');
2017-10-05 22:55:56 +03:00
flag:=0
end;
2017-11-26 19:35:27 +03:00
8: Writeln('Ненадлежащий вызов')
2017-10-05 22:55:56 +03:00
end;
end;
public
flag:integer;
relerr:real;
abserr:real;
2017-11-26 19:35:27 +03:00
y:array of real; // функция
yp:array of real; // первая производная
2017-10-05 22:55:56 +03:00
MsgOn:boolean;
constructor (f:procedure(x:real; y,yp:array of real);
y:array of real; abserr,relerr:real; MsgOn:boolean:=true);
begin
neqn:=y.Length;
Self.y:=Copy(y);
yp:=new real[neqn];
f1:=new real[neqn];
f2:=new real[neqn];
f3:=new real[neqn];
f4:=new real[neqn];
f5:=new real[neqn];
Self.epsilon:=epsilon;
flag:=1;
Self.abserr:=abserr;
Self.relerr:=relerr;
Self.MsgOn:=MsgOn;
(abserr_save,relerr_save,h):=(-1.0,-1.0,-1.0);
(flag_save,init,kflag,kop,nfe):=(-1000,-1000,-1000,-1,-1);
p:=f;
end;
procedure Solve(var t:real; tout:real);
2017-11-26 19:35:27 +03:00
// Реализует метод Runge-Kutta-Fehlberg
// Обычно данный класс выполняет интегрирование на отрезке
// от t до t_out, но если требуется одношаговвй интегратор,
// можно с его помощью сделать небольшой шажок в направлении t_out.
// После каждого шага значения t м t_out устанавливаются так,
// чтобы для следующего шага достаточно только еще раз
// обратиться к методу Solve.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Перед первым обращением пользователь должен
// - обеспечить наличие процедуры с интерфейсом вида F(t,y,yp),
// вычисляющей при заданном аргументе t значения функции y и её первой
// производной yp на правом конце интервала;
// - задать начальные значения параметоров y, t, tout, relerr, abserr и flag.
// Обычно t задает начальную точку для решения,
// y определяет начальные условия, а flag полагают равным +1.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Cтандартно значение flag перед первым вызовом устанавливыается в 1,
// а дальнейшее управление берет на себя программа. Flag=-1 задается,
// если нужен одношаговый режим.
// После завершения метод Solve возвращает значение Flag=2 в обычном режиме
// или -2 в одношаговом, что позволяет нормально продолжить работу.
// (Значения +1 и -1 указывают на необходимость некоторой внутренней
// инициализации, а 2 и -2 вызывают её обход)
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// Если возвращенный результат равен заданному tout, пользователь
// должен определить новое значение для следующего шага и продолжить работу.
// В одношаговом режиме пользователь, получив Flag=-2, должен понимать,
// что решение - это лишь очередная итерация в направлении tout и следует
// повторить вызов метода Solve.
// Значение Flag=2 независимо от режима указывает, что tout достигнуто.
// Если нужно продолжить работу в одношаговом режиме, следует задать Flag=-2.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// В некоторых случаях могут возникать ошибки. Понять их причину и
// принять соответствующее решение помогает значение Flag.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// 3: интегрирование не завершено, поскольку заданное значение границы
// льносительной ошибки relerr слишкои мало. Для продолжения необходимо
// увеличить значение relerr. Если результат устраивает установите
// Flag=2 и продолжайтк работу.
2017-10-05 22:55:56 +03:00
//
2017-11-26 19:35:27 +03:00
// 4: интегрирование не завершено, поскольку потребовалось более 3000
// вычислений значения производной, что соответствует примерно 500 шагам.
// Пользователь может повторить обращение, при этом счетчик
// количества вычислений функции будет сброшен в ноль и, возможно, это
// даст результат.
// 5: интегрирование не завершено, поскольку решение обратилось в ноль,
// вследствие чего тест одной только относительной ошибки не проходит.
// Пользователь должен задать ненулевое значение abserr и продолжить работу.
// Возможно, использование одношагового интегратора будет хорошим выходом.
// 6: интегрирование не завершено, поскольку требуемая точность не может
// быть достигнута даже при наименьшем допустимом шаге. Пользователь должен
// увеличить границу допустимой погрешности abserr и/или reller прежде чем
// пытаться продолжить решение. Также, потребуется установить flag=2 (или -2
// для вызова одношагового интегратора). flag=6 возникает в случае
// обнаружения проблемной точки, в которой или решение меняется очень резко,
// или присутствует сингулярность.
// 7: по всей видимости RKF45 неэффективна для для данной задачию. Слишком
// большое значение требуемых для вычисления точек препятствует выбору шага.
// Можно попытаться использовать режим одношагового интегратора.
// Если пользователь попытается продолжить решение, задав flag=2, работа
// будет завершена.
// 8: Неверные входные параметры. Возникает, если:
// - нулевое или отрицательне количество уравнений;
// - t=tout и при этом flag не равен 1;
// - relerr или abserr отрицательны;
// - flag имеет значение 0 или не лежит в интервале [-4;8]
2017-10-05 22:55:56 +03:00
begin
var maxnfe:=3000;
var remin:=1.0E-12;
2017-11-26 19:35:27 +03:00
// remin - это минимальное доа\пустимое знвчение relerr.
// Попытки получить с помощью RKF45 более высокую точность обычно
// безуспешны и обходятся очень дорого.
2017-10-05 22:55:56 +03:00
var ae,dt,ee,eeoet,eps,esttol,et,hmin,relerr_min,s,scale,tol,toln,ypk:real;
var hfaild,output:boolean;
2017-11-26 19:35:27 +03:00
eps:=Self.epsilon; // машинная точность
// контроль входных значений
2017-10-05 22:55:56 +03:00
if (neqn<1) then begin
flag:=8; FlagDev; Exit
end;
if (relerr<0.0) or (abserr<0.0) then begin
flag:=8; FlagDev; Exit
end;
if (flag=0) or (flag<-2) or (flag>8) then begin
flag:=8; FlagDev; Exit
end;
var mflag:=Abs(flag);
2017-11-26 19:35:27 +03:00
if mflag<>1 then begin // обход части кода при первом обращении
2017-10-05 22:55:56 +03:00
if (t=tout) and (kflag<>3) then begin
flag:=8; FlagDev; Exit
end;
if mflag=2 then begin
if kflag=3 then begin
flag:=flag_save; mflag:=Abs(flag)
end
else if init=0 then flag:=flag_save
else if kflag=4 then nfe:=0
else if (kflag=5) and (abserr=0.0) then begin
flag:=-5; FlagDev; Exit
end
else if (kflag=6) and (relerr<=relerr_save) and
(abserr<=abserr_save) then begin
flag:=-4; FlagDev; Exit
end
end
else
if flag=3 then begin
flag:=flag_save;
if kflag=3 then mflag:=Abs(flag);
end
else if flag=4 then begin
nfe:=0; flag:=flag_save;
if kflag=3 then mflag:=Abs(flag)
end
else if (flag=5) and (abserr>0.0) then begin
flag:=flag_save;
if kflag=3 then mflag:=Abs(flag)
end
else begin
flag:=-3; FlagDev; Exit
end
end;
flag_save:=flag; kflag:=0;
2017-11-26 19:35:27 +03:00
relerr_save:=relerr; abserr_save:=abserr; // для будущих проверок
relerr_min:=2.0*epsilon+remin; // ограниечение по точности
2017-10-05 22:55:56 +03:00
if relerr<relerr_min then begin
relerr:=relerr_min; flag:=3; kflag:=3;
FlagDev; Exit
end;
dt:=tout-t;
if mflag=1 then begin
init:=0; kop:=0;
p(t,y,yp);
nfe:=1;
if t=tout then begin
flag:=2; FlagDev; Exit
end
end;
if init=0 then begin
init:=1; h:=Abs(dt); toln:=0.0;
for var k:=0 to neqn-1 do begin
tol:=relerr*Abs(y[k])+abserr;
if tol>0.0 then begin
toln:=tol; ypk:=Abs(yp[k]);
if tol<ypk*Power(h,5) then h:=Power(tol/ypk,0.2)
end
end;
if toln<=0.0 then h:=0.0;
h:=Max(h,26.0*eps*Max(Abs(t),Abs(dt)));
flag_save:=flag<0?-2:2
end;
if dt<0 then h:=-h;
if 2.0*Abs(dt)<=Abs(h) then kop+=1;
if kop=10000 then begin kop:=0; flag:=7; FlagDev; Exit end;
if Abs(dt)<=26.0*eps*Abs(t) then begin
t:=tout;
for var i:=0 to neqn-1 do y[i]+=dt*yp[i];
p(t,y,yp);
nfe+=1; flag:=2;
FlagDev; Exit
end;
2017-11-26 19:35:27 +03:00
// интегрирование шаг за шагом
2017-10-05 22:55:56 +03:00
output:=false; scale:=2.0/relerr; ae:=scale*abserr;
repeat
hfaild:=false; hmin:=26.0*eps*Abs(t); dt:=tout-t;
if 2.0*Abs(h)>Abs(dt) then begin
if Abs(dt)<=Abs(h) then begin output:=true; h:=dt end
else h:=0.5*dt
end;
2017-11-26 19:35:27 +03:00
// внутренний одношаговый интегратор
2017-10-05 22:55:56 +03:00
while true do begin
if maxnfe<nfe then begin (flag,kflag):=(4,4); FlagDev; Exit end;
Fehl(f1,t,h);
nfe+=5; eeoet:=0.0;
for var k:=0 to neqn-1 do begin
et:=Abs(y[k])+Abs(f1[k])+ae;
if et<=0.0 then begin flag:=5; FlagDev; Exit end;
ee:=Abs((-2090.0*yp[k]+(21970.0*f3[k]-15048.0*f4[k]))+
(22528.0*f2[k]-27360.0*f5[k]));
eeoet:=Max(eeoet,ee/et)
end;
esttol:=Abs(h)*eeoet*scale/752400.0;
if esttol<=1.0 then Break;
hfaild:=true; output:=false;
if esttol<59049.0 then s:=0.9/Power(esttol,0.2)
else s:=0.1;
h*=s;
if Abs(h)<hmin then begin (flag,kflag):=(6,6); FlagDev; Exit end;
end;
t+=h; y:=Copy(f1);
p(t,y,yp);
nfe+=1;
if esttol>0.0001889568 then s:=0.9/Power(esttol,0.2)
else s:=5.0;
if hfaild then s:=Min(s,1.0);
h:=Sign(Max(s*Abs(h),hmin),h);
2017-11-26 19:35:27 +03:00
// окончание кода внутреннего одношагового интегратора
2017-10-05 22:55:56 +03:00
if output then begin t:=tout; flag:=2; FlagDev; Exit end;
until flag<=0;
flag:=-2;
FlagDev
end;
end;
{$endregion}
{$region RootsIsolation}
type
2017-11-26 19:35:27 +03:00
/// Изоляция корней уравнения y(x)=0 на заданном интервале
2017-10-05 22:55:56 +03:00
RootsIsolation=class
private
f:real->real;
a,b,h:real;
public
constructor(f:real->real; a,b,h:real);
begin
Self.f:=f;
Self.a:=a;
Self.b:=b;
Self.h:=h
end;
2017-11-26 19:35:27 +03:00
/// Полученные результаты
2017-10-05 22:55:56 +03:00
function Value:array of (real,real);
//
2017-11-26 19:35:27 +03:00
// f - функция, для которой ищутся интервалы изоляции;
// a,b - границы интервала просмотра [a;b]
// h - шаг просмотра
// Функция возвращает массив кортежей (x1,x2), таких что f(x1) и f(x2) имеют
// разные знаки.
2017-10-05 22:55:56 +03:00
//
begin
var r:=new List<(real,real)>;
var h2:=h/2;
var eps:=1e-7;
var (xa,ya,bk):=(a,f(a),b+h2);
if Abs(ya)<eps then begin
r.Add((xa-h2,xa+h2));
xa+=h2; ya:=f(xa)
end;
var xb:=xa+h;
while xb<=bk do begin
var yb:=f(xb);
if Abs(yb)<eps then begin
r.Add((xb-h2,xb+h2));
xa:=xb+h2; ya:=f(xa);
xb:=xa+h
end
else if ya*yb<0 then begin
r.Add((xa,xb));
(xa,ya):=(xb,yb);
xb:=xa+h
end
else begin
(xa,ya):=(xb,yb);
xb:=xa+h
end
end;
Result:=r.ToArray
end;
end;
{$endregion}
{$region Spline}
type
2017-11-26 19:35:27 +03:00
/// Интерполяция табличной функции кубическим сплайном
2017-10-05 22:55:56 +03:00
Spline=class
private
2017-11-26 19:35:27 +03:00
n:integer; // количество узлов интерполяции
aK:array[,] of real; // коэффициенты сплайна
P:array of Point; // узлы интерполяции
2017-10-05 22:55:56 +03:00
2017-11-26 19:35:27 +03:00
///- Построение сплайна (вызывается конструктором)
2017-10-05 22:55:56 +03:00
procedure Spline;
//
2017-11-26 19:35:27 +03:00
// Функция Spline возвращает коэффициенты сплайна в массиве.
// Является результатом переработки фортран-программы,
// приведенной в кн. Дж.Форсайт, М.Малькольм, К.Моулер
// "Машинные методы математических вычислений",
// М., "Мир", 1980.
2017-10-05 22:55:56 +03:00
//
begin
if n<2 then Exit;
if n=2 then begin
aK[0,0]:=(P[1].y-P[0].y)/(P[1].x-P[0].x); aK[0,1]:=aK[0,0];
(aK[1,0],aK[1,1],aK[2,0],aK[2,1]):=(0,0,0,0);
Exit
end;
aK[2,0]:=P[1].x-P[0].x; aK[1,1]:=(P[1].y-P[0].y)/aK[2,0];
for var i:=1 to n-2 do begin
aK[2,i]:=P[i+1].x-P[i].x;
aK[0,i]:=2*(aK[2,i-1]+aK[2,i]);
aK[1,i+1]:=(P[i+1].y-P[i].y)/aK[2,i];
aK[1,i]:=aK[1,i+1]-aK[1,i]
end;
aK[0,0]:=-aK[2,0]; aK[0,n-1]:=-aK[2,n-2];
aK[1,0]:=0; aK[1,n-1]:=0;
if n<>3 then begin
aK[1,0]:=aK[1,2]/(P[3].x-P[1].x)-aK[1,1]/(P[2].x-P[0].x);
aK[1,n-1]:=aK[1,n-2]/(P[n-1].x-P[n-3].x)-aK[1,n-3]/(P[n-2].x-P[n-4].x);
aK[1,0]:=aK[1,0]*Sqr(aK[2,0])/(P[3].x-P[0].x);
aK[1,n-1]:=-aK[1,n-1]*Sqr(aK[2,n-2])/(P[n-1].x-P[n-4].x)
end;
for var i:=1 to n-1 do begin
var t:=aK[2,i-1]/aK[0,i-1];
aK[0,i]:=aK[0,i]-t*aK[2,i-1];
aK[1,i]:=aK[1,i]-t*aK[1,i-1]
end;
aK[1,n-1]:=aK[1,n-1]/aK[0,n-1];
for var i:=n-2 downto 0 do
aK[1,i]:=(aK[1,i]-aK[2,i]*aK[1,i+1])/aK[0,i];
aK[0,n-1]:=(P[n-1].y-P[n-2].y)/aK[2,n-2]+
aK[2,n-2]*(aK[1,n-2]+2*aK[1,n-1]);
for var i:=0 to n-2 do begin
aK[0,i]:=(P[i+1].y-P[i].y)/aK[2,i]-
aK[2,i]*(aK[1,i+1]+2*aK[1,i]);
aK[2,i]:=(aK[1,i+1]-aK[1,i])/aK[2,i];
aK[1,i]:=3*aK[1,i]
end;
aK[1,n-1]:=3*aK[1,n-1]; aK[2,n-1]:=aK[2,n-2]
end;
public
constructor (pts:array of Point);
begin
n:=pts.Length;
P:=pts;
SetLength(aK,3,n);
Spline;
end;
2017-11-26 19:35:27 +03:00
/// Вычисление значения построенного
/// конструктором сплайна в заданной точке
2017-10-05 22:55:56 +03:00
function Value(u:real):real;
//
2017-11-26 19:35:27 +03:00
// портированная с фортрана функция Seval)
2017-10-05 22:55:56 +03:00
begin
var i,j:integer;
if u<P[0].x then i:=0
else if u>P[n-1].x then i:=n-2
else begin
j:=n-1;
repeat
var k:=(i+j) div 2;
if u<P[k].x then j:=k else i:=k
until j-i<=1
end;
var dx:=u-P[i].x;
Result:=P[i].y+dx*(aK[0,i]+dx*(aK[1,i]+dx*aK[2,i]))
end;
2017-11-26 19:35:27 +03:00
/// Вычисляет значение первой и второй производной в заданной точке
2017-10-05 22:55:56 +03:00
function Diff(u:real):(real,real);
//
2017-11-26 19:35:27 +03:00
// (модифицированный метод Value)
2017-10-05 22:55:56 +03:00
begin
var i,j:integer;
if u<P[0].x then i:=0
else if u>P[n-1].x then i:=n-2
else begin
j:=n-1;
repeat
var k:=(i+j) div 2;
if u<P[k].x then j:=k else i:=k
until j-i<=1
end;
var dx:=u-P[i].x;
Result:=(aK[0,i]+dx*(2*aK[1,i]+3*dx*aK[2,i]),2*ak[1,i]+6*dx*aK[2,i])
end;
end;
{$endregion}
{$region Svenn}
type
2017-11-26 19:35:27 +03:00
/// Поиск начального интервала неопределенности для локализации
/// минимума унимодальной функции методом Свенна.
2017-10-05 22:55:56 +03:00
Svenn=class
private
f:real->real;
xn,t:real;
public
constructor(f:real->real; xn,t:real);
begin
Self.f:=f;
Self.xn:=xn;
Self.t:=t
end;
2017-11-26 19:35:27 +03:00
/// Cобственно, само решение и результат
2017-10-05 22:55:56 +03:00
function Value:(real,real,integer);
//
2017-11-26 19:35:27 +03:00
// xn - начальное приближение
// t - шаг
// Результат: кортеж из трех элементов, где первые два представляют
// найденный интервал, если третий элемент нулевой и не представляют
// ничего если начальная точка/шаг выбраны неудачно, либо если функция
// не унимодальна а окрестности заданной точки.
// Примечание: если записать функцию по модулю, можно отыскать интервал
// изоляции корня уравнения f(x)=0
2017-10-05 22:55:56 +03:00
//
begin
var k:=0;
var (x1,x2,x3):=(xn-t,xn,xn+t);
var (y1,y2,y3):=(f(x1),f(x2),f(x3));
var d,a,b:real;
if (y1>=y2) and (y2<=y3) then begin
Result:=(x1,x3,0);
Exit
end
else if (y1<=y2) and (y2>=y3) then begin
Result:=(x1,x3,1);
Exit
end
else if (y1>=y2) and (y2>=y3) then (d,a,x2,k):=(t,x2,x2+t,1)
else (d,b,x2,k):=(-t,x2,x2-t,1);
repeat
x1:=x2+Power(2,k)*d;
(y1,y2):=(f(x1),f(x2));
if (y1<y2) then begin
if d=t then a:=x2 else b:=x2;
k+=1; x2:=x1
end
until y1>=y2;
if d=t then Result:=(a,x1,0)
else Result:=(x1,b,0)
end;
end;
{$endregion}
{$region Zeroin}
type
2017-11-26 19:35:27 +03:00
/// Нахождение действительного нуля функции на интервале изоляции
2017-10-05 22:55:56 +03:00
Zeroin=class
private
tol:real;
f:real->real;
public
constructor(f:real->real; tol:real);
begin
Self.tol:=tol;
Self.f:=f;
end;
2017-11-26 19:35:27 +03:00
/// Собственно, само решение и результат
2017-10-05 22:55:56 +03:00
function Value(ax,bx:real):real;
//
2017-11-26 19:35:27 +03:00
// *** Нахождение действительного нуля функции на интервале изоляции ***
// Считается без проверки, что на интервале [ax;bx] ноль есть,
// причем единственный.
// Является результатом переработки одноименной фортран-программы,
// приведенной в кн. Дж.Форсайт, М.Малькольм, К.Моулер
// "Машинные методы математических вычислений",
// М., "Мир", 1980.
2017-10-05 22:55:56 +03:00
//
begin
var Sign:real->real:=x->(x=0?0.0:x<0?-1.0:1.0);
var eps:=1.0;
var tol1:real;
repeat
eps:=eps/2; tol1:=1.0+eps
until tol1<=1.0;
var (fa,fb,a,b):=(f(ax),f(bx),ax,bx);
repeat
var (c,d,fc):=(a,b-a,fa);
var e:=d;
repeat
if Abs(fc)<Abs(fb) then
begin (a,b):=(b,c); c:=a; (fa,fb):=(fb,fc); fc:=fa end;
tol1:=2*eps*Abs(b)+0.5*tol;
var xm:=0.5*(c-b);
if (Abs(xm)<tol1) or (fb=0) then begin Result:=b; Exit end;
if (Abs(e)<tol1) or (Abs(fa)<=Abs(fb)) then (d,e):=(xm,d)
else begin
var p,q,s:real;
if a=c then begin s:=fb/fa; p:=2*xm*s; q:=1-s end
else begin
var r:=fb/fc; q:=fa/fc; s:=fb/fa; p:=s*(2*xm*q*(q-r)-(b-a)*(r-1));
q:=(q-1)*(r-1)*(s-1)
end;
if p>0 then q:=-q;
p:=Abs(p);
if (2*p >= 3*xm*q-Abs(tol1*q)) or (p>=Abs(0.5*e*q)) then (d,e):=(xm,d)
else (e,d):=(d,p/q)
end;
(a,fa):=(b,fb);
if abs(d)>tol1 then b:=b+d
else b+=Abs(tol1)*Sign(xm);
fb:=f(b);
until fb*Sign(fc)>0;
until false
end;
end;
{$endregion}
//----------------------------------------
2020-11-01 12:42:10 +03:00
///--
procedure __InitModule__;
begin
end;
2017-10-05 22:55:56 +03:00
2020-11-01 12:42:10 +03:00
///--
procedure __FinalizeModule__;
begin
end;
2017-10-05 22:55:56 +03:00
end.