1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
| unit RungeKutta;
interface
{.$DEFINE Cache}
uses
System.SysUtils, System.Math{$IFDEF Cache}, CacheUnit{$ENDIF};
type
TRKEquation=reference to function(x,y:extended):extended;
/// <summary>
/// Решение ОДУ методом Рунге-Кутты
/// </summary>
IRungeKutta=interface
/// <summary>
/// Собственно решение
/// </summary>
function y(x:extended):extended;
end;
/// <summary>
/// Создание интерфейса <see cref="ACod.Math.RungeKutta.IRungeKutta"/>
/// </summary>
function InitialRK(h,x0,y0:extended; RKEquation:TRKEquation
{$IFDEF Cache}; Cache:TCacheLimit=0{$ENDIF}):IRungeKutta;
/// <summary>
/// Получение решения уравнения в точке X с шагом H
/// </summary>
function GetSolutionAtPoint(x,h,x0,y0:extended; RKEquation:TRKEquation)
:extended;
implementation
type
TRungeKutta=class(TInterfacedObject,IRungeKutta)
private
FH:extended;
FX0:extended;
FY0:extended;
FRKEquation:TRKEquation;
FYN,FXN:extended;
FStepCount:UInt64;
{$IFDEF Cache}
FCache:boolean;
FCacheMass:TCache<extended,extended>;
{$ENDIF}
procedure SetH(const Value:extended);
procedure SetRKEquation(const Value:TRKEquation);
procedure SetX0(const Value:extended);
procedure SetY0(const Value:extended);
procedure Step(dx:extended);
public
constructor Create{$IFDEF Cache}(Cache:TCacheLimit){$ENDIF};
destructor Free;
property RKEquation:TRKEquation read FRKEquation write SetRKEquation;
property x0:extended read FX0 write SetX0;
property y0:extended read FY0 write SetY0;
property h:extended read FH write SetH;
function y(x:extended):extended;
end;
{ TRungeKutta }
constructor TRungeKutta.Create{$IFDEF Cache}(Cache:TCacheLimit){$ENDIF};
begin
{$IFDEF Cache}
FCache:=Cache>0;
if FCache then
FCacheMass:=TCache<extended,extended>.Create(Cache);
{$ENDIF}
end;
destructor TRungeKutta.Free;
begin
{$IFDEF Cache}
if FCache then
FCacheMass.Free;
{$ENDIF}
end;
procedure TRungeKutta.SetH(const Value:extended);
begin
FH:=Value;
end;
procedure TRungeKutta.SetRKEquation(const Value:TRKEquation);
begin
FRKEquation:=Value;
end;
procedure TRungeKutta.SetX0(const Value:extended);
begin
FX0:=Value;
end;
procedure TRungeKutta.SetY0(const Value:extended);
begin
FY0:=Value;
end;
procedure TRungeKutta.Step(dx:extended);
var
k1,k2,k3,k4:extended;
begin
k1:=dx*RKEquation(FXN,FYN);
k2:=dx*RKEquation(FXN+dx/2,FYN+k1/2);
k3:=dx*RKEquation(FXN+dx/2,FYN+k2/2);
k4:=dx*RKEquation(FXN+dx,FYN+k3);
FYN:=FYN+(k1+2*k2+2*k3+k4)/6;
FXN:=FXN+dx;
end;
function TRungeKutta.y(x:extended):extended;
var
tmp:UInt64;
pls:boolean;
begin
Result:=NaN;
if IsNaN(x)or IsInfinite(x) then
raise Exception.CreateFmt('Неподходящее для вычислений значение x=%f',[x]);
{$IFDEF Cache}
if FCache then
if FCacheMass.HasItem(x) then
Exit(FCacheMass[x]);
{$ENDIF}
FXN:=FX0;
FYN:=FY0;
FStepCount:=round(abs(x-FX0)/FH);
pls:=x<FX0;
if pls then
FH:=-FH;
tmp:=0;
while tmp<(FStepCount-1) do
begin
Step(FH);
inc(tmp);
end;
{new:23.08.2012 10:37}
Step(x-(FH*(FStepCount-1)+FX0));
{/new}
Result:=FYN;
if pls then
FH:=-FH;
{$IFDEF Cache}
if FCache then
FCacheMass[x]:=FYN;
{$ENDIF}
end;
{ Funtions }
function InitialRK(h,x0,y0:extended; RKEquation:TRKEquation{$IFDEF Cache}; Cache:TCacheLimit{$ENDIF})
:IRungeKutta;
var
tmp:TRungeKutta;
begin
if CompareValue(h,0)<=0 then
raise Exception.CreateFmt('Величина шага (%f) должна быть больше 0!',[h]);
tmp:=TRungeKutta.Create{$IFDEF Cache}(Cache){$ENDIF};
tmp.h:=h;
tmp.x0:=x0;
tmp.y0:=y0;
tmp.RKEquation:=RKEquation;
Result:=tmp;
end;
function GetSolutionAtPoint(x,h,x0,y0:extended; RKEquation:TRKEquation)
:extended;
begin
Result:=InitialRK(h,x0,y0,RKEquation{$IFDEF Cache},0{$ENDIF}).y(x);
end; |