关于差异化的问题:
Differentiation of a buffer with Delphi
我现在正在考虑进行整合。我不能完全理解这一个。情况是我定期收到一个数据缓冲区,其中包含多个相隔时间固定距离的值。我需要区分它们。自从我在学校做微积分以来,它已经太久了......
我想出的是:
procedure IntegrateBuffer(ABuffer: TDoubleDynArray;
var AOutBuffer: TDoubleDynArray;
AVPS: integer);
const
SumSum: double = 0.0;
LastValue: double = NaN;
var
i: integer;
dt, aa, hl, hr: double;
begin
// protect from divide by zero
if (AVPS < 1) then exit;
dt := 1 / AVPS;
for i := 0 to high(ABuffer) do begin
if (i = 0) then begin
if (IsNaN(LastValue)) then begin
hl := ABuffer[0];
hr := ABuffer[0];
end else begin
hl := LastValue;
hr := ABuffer[i];
end;
end else begin
hl := ABuffer[i -1];
hr := ABuffer[i];
end;
aa := 0.5 * dt * (hl + hr);
SumSum := SumSum + aa;
AOutBuffer[i] := SumSum;
end;
// remember the last value for next time
LastValue := ABuffer[high(ABuffer)];
end;
我正在使用梯形法则,hl和hr是梯形的左右高度。 dt是基础。
AVPS是每秒的值。此值的典型值介于10和100之间。缓冲区的长度通常为500到1000个值。
我用一个与前一个数据块连续的新数据一次又一次地调用缓冲区,因此保留下一个块的最后一个值。
我做得对吗?即,它会正确整合价值吗?
谢谢。
答案 0 :(得分:7)
您似乎需要一些测试代码的帮助。在这里,正如评论中所讨论的,这是一个非常简单的测试。
{$APPTYPE CONSOLE}
uses
SysUtils, Math;
type
TDoubleDynArray = array of Double;
var
SumSum: double;
LastValue: double;
procedure Clear;
begin
SumSum := 0.0;
LastValue := NaN;
end;
procedure IntegrateBuffer(
ABuffer: TDoubleDynArray;
var AOutBuffer: TDoubleDynArray;
AVPS: integer
);
var
i: integer;
dt, aa, hl, hr: double;
begin
// protect from divide by zero
if (AVPS < 1) then exit;
dt := 1 / AVPS;
for i := 0 to high(ABuffer) do begin
if (i = 0) then begin
if (IsNaN(LastValue)) then begin
hl := ABuffer[0];
hr := ABuffer[0];
end else begin
hl := LastValue;
hr := ABuffer[i];
end;
end else begin
hl := ABuffer[i -1];
hr := ABuffer[i];
end;
aa := 0.5 * dt * (hl + hr);
SumSum := SumSum + aa;
AOutBuffer[i] := SumSum;
end;
// remember the last value for next time
LastValue := ABuffer[high(ABuffer)];
end;
var
Buffer: TDoubleDynArray;
OutBuffer: TDoubleDynArray;
begin
// test y = 1 for a single call, expected output = 1, actual output = 2
Clear;
Buffer := TDoubleDynArray.Create(1.0, 1.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Writeln(OutBuffer[high(OutBuffer)]);
Readln;
end.
我正在将函数y(x) = 1
集成到范围[0..1]上。因此,预期输出为1.但实际输出为2.
那么,怎么了?您可以在调试器中进行处理,但通过检查代码很容易看到。你在第一个样本上总结了一个三角形。如果IsNaN(LastValue)
为真,那么您不应该对积分做出贡献。此时,您没有覆盖x轴上的任何距离。
所以要修复代码,让我们试试这个:
....
if (IsNaN(LastValue)) then begin
hl := 0.0;//no contribution to sum
hr := 0.0;
end else begin
hl := LastValue;
hr := ABuffer[i];
end;
....
解决了这个问题。
现在让我们稍微扩展测试并测试y(x) = x
:
// test y = x, expected output = 12.5
Clear;
Buffer := TDoubleDynArray.Create(0.0, 1.0, 2.0, 3.0, 4.0, 5.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Writeln(OutBuffer[high(OutBuffer)]);
所以,看起来不错。
好的,多次通话怎么办:
// test y = x for multiple calls, expected output = 18
Clear;
Buffer := TDoubleDynArray.Create(0.0, 1.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Buffer := TDoubleDynArray.Create(2.0, 3.0, 4.0, 5.0, 6.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Writeln(OutBuffer[high(OutBuffer)]);
一次一个值怎么样?
// test y = x for multiple calls, one value at a time, expected 0.5
Clear;
Buffer := TDoubleDynArray.Create(0.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Buffer := TDoubleDynArray.Create(1.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Writeln(OutBuffer[high(OutBuffer)]);
传递空数组怎么样?
// test y = x for multiple calls, some empty arrays, expected 0.5
Clear;
Buffer := TDoubleDynArray.Create(0.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Buffer := nil;
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Buffer := TDoubleDynArray.Create(1.0);
SetLength(OutBuffer, Length(Buffer));
IntegrateBuffer(Buffer, OutBuffer, 1);
Writeln(OutBuffer[high(OutBuffer)]);
呃,哦,访问违规。如果缓冲区为空,只需在开始时跳过函数,就可以更好地保护它:
if (AVPS < 1) then exit;
if (Length(ABuffer) = 0) then exit;
好的,现在最后一次测试通过了
希望你现在明白了。我刚刚使用了基于Writeln
的基于const
的测试,但这不能扩展。给自己一个单元测试框架(我推荐DUnitX)并构建适当的测试用例。这也会迫使您对代码进行分解,以使其设计得很好。使代码可测试的一个常见意外的好处是它通常会导致界面设计得到改进。
对于您的下一个问题,我请求您提供带有测试代码的SSCCE! ; - )
对代码的一些评论:
var
或const
传递动态数组。在您的情况下,您希望通过{{1}}传递输入缓冲区。再次,正如我在上一个问题中所说,编写测试来证明代码,以及通过眼睛检查。编写测试的关键是从您可能想到的最简单的事情开始。这么简单,你知道100%肯定答案。然后,一旦你开始工作,将测试扩展到更复杂的情况。