繁体   English   中英

我应该如何对执行数组数值插值的类进行单元测试?

[英]How should I unit-test a class which performs numeric interpolation of arrays?

我在一个项目中工作,其中一些类执行数值插值,也就是说,给定一组已知位置的点,我可以要求网格节点之间的点的位置,可以这么说。

由于这些方法根据定义不返回精确值,我想知道我应该如何对它们进行单元测试。

例如,下面的代码测试如果我给它一个类似的零数组,则插值器返回一个零数组,并且它有效,但我怀疑它有效,因为我实际上并没有在这里重新采样,只是再次询问相同的位置。

[TestMethod]
public void Interpolate_ZeroIn_ZeroOut()
{
    var Xvalues = new double[] {1,2,3,4,5,6,7,8,9,10};
    var Yvalues = new double[] {0,0,0,0,0,0,0,0,0,0};

    List<Point> points = 
        Enumerable.Zip(Xvalues, Yvalues, (x,y) => new Point(x,y)).ToList();

    int interpolation_order = 5;

    FourierIterpolator target = new FourierInterpolator(points, interpolation_order);

    var output = Xvalues.Select(p => target.Interpolate(p)).ToList();

    CollectionAssert.AreEqual(output, Yvalues);
}

问题是:通过实际重采样,新点将在输入点“之间”,所以我不能使用CollectionAssert.AreEqual 此外,使用一些带平滑的插值方法,数组将不相等,只有近似值。

所以,我的问题是:

在测试涉及近似/插值的数值方法时推荐使用哪些断言?

您的插值技术是确定性的吗? 确定性意味着:如果您的方法使用相同的参数被调用两次,它会在两种情况下返回完全相同的结果吗?)

如果是,

  • 通过您的函数运行一些值,
  • 使用外部方法来验证结果是否正确(笔和纸,或一些数学工具),
  • 添加这些值作为测试用例。

这将确保测试用例检测到您的方法的任何重大更改 一个缺点是,如果您改进插值技术,您的案例将失败,从而产生不同的结果。 这不一定是坏事,因为它会提醒您您的方法现在返回不同的结果。

如果您的插值技术不是确定性的,即,如果它使用一些随机源,您可能能够断言这些值在一些合理的误差范围内。

目前很难看到测试的输入数据是什么。 我建议您使测试数据明确且易于查看。 此外,我认为您不需要 10 或 100 分来验证您的方法是否有效。 大概 2-3 点就足够了:

var points = new []{ new Point(0,0), new Point(1,0), new Point(2,0) });

接下来,您应该提供预期值:

double[] expected = { 0.33, 0.66, 1.66 };

最后 - 检查实际值是否等于或接近预期值:

[TestMethod]
public void Interpolar_ZeroIn_ZeroOut()
{
    var points = new []{ new Point(0,0), new Point(1,0), new Point(2,0) });
    // calculate expected values manually
    double[] expected = { 0.33, 0.66, 1.66 };    
    int ordem = 5;

    var interpolator = new InterpoladorFourier(points, ordem);

    for(int i = 0; i < points.Length; i++)        
       Assert.AreEqual(expected[i], interpolator.Interpolar(points[i].X));
}

如果预期数据不准确,那么您可以为断言提供增量:

Assert.AreEqual(expected[i], interpolator.Interpolar(points[i].X), 0.01);

或者甚至更好 - 您可以创建以非常易读的方式创建点的方法:

var points = CreatePoints("0,0", "1,0", "2,0"); 

使用此辅助方法:

private Point[] CreatePoints(params string[] points)
{
    List<Point> result = new List<Point>();

    foreach(var s in points)
    {
        var parts = s.Split(',');
        var x = Double.Parse(parts[0]);
        var y = Double.Parse(parts[1]);            
        var point = new Point(x,y);
        result.Add(point);
    }

    return result.ToArray();
}

我通过谷歌来到这里是因为我自己也在寻找一些答案,但我目前的方法可能比这里提供的方法更好,所以我会分享以供将来参考。

大多数(如果不是全部)插值方法都有相当数量的函数,它们可以完美地或至少达到机器精度。 例如三次样条,它的多项式达到 3 次。我只是测试合理数量的具有不同系数、不同间隔、不同点数的多项式......如果它接近机器精度,则计算 RMS 误差并“断言” (比如 rms_err < 10*eps)。 准确选择哪些多项式/函数/参数适合此处需要一些关于特定插值方法的知识,可能还有浮点错误,但这是可行的。

另一种方法是与具有相同或相似实现的可用库进行比较,或者直接导出仅使用库的值。 例如,我使用可用的 1D 实现测试了我的 2D 插值实现,这些实现使用相同的方法,当然沿着一个维度。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM