我们有几种不同的优化算法,可以为每次运行产生不同的结果.例如,优化的目标可以是找到函数的最小值,其中0是全局最小值.优化运行返回如下数据:
[0.1, 0.1321, 0.0921, 0.012, 0.4]
这与全球最小值非常接近,所以这没关系.我们的第一种方法是选择一个阈值,如果结果发生得太高,让单元测试失败.不幸的是,这根本不起作用:结果似乎有一个高斯分布,因此,虽然不太可能,但即使算法仍然很好而且我们运气不好,测试也会不时发生.
那么,我该如何正确测试呢?我想这里需要相当多的统计数据.同样重要的是测试仍然很快,只需让测试运行几百次,然后取平均值就太慢了.
以下是一些进一步的说明:
例如,我有一个算法将Circle拟合成一组点.它非常快,但并不总能产生相同的结果.我想写一个单元测试,以保证在大多数情况下它足够好.
不幸的是我无法为随机数生成器选择固定种子,因为我不想测试算法是否产生与以前完全相同的结果,但我想测试类似"有90%确定性我得到0.1或者0.1的结果"更好".
John D. Cook.. 15
听起来你的优化器需要两种测试:
测试算法的整体有效性
测试算法实现的完整性
由于算法涉及随机化,(1)难以进行单元测试.任何随机过程的测试都会在某些时间内失败.您需要了解一些统计信息才能了解它应该失败的频率.有很多方法可以在测试的严格程度和失败的频率之间进行权衡.
但是有一些方法可以为(2)编写单元测试.例如,您可以在运行单元测试之前将种子重置为特定值.然后输出是确定性的.这不会让你评估算法的平均有效性,但那是(1).这样的测试可以作为一个旅程线:如果有人在维护期间将错误引入代码中,确定性单元测试可能会捕获该错误.
可能还有其他东西可以进行单元测试.例如,无论随机部分发生什么,也许您的算法可以保证在一定范围内返回值.也许某些价值应该总是积极的等等.
更新:我在Beautiful Testing中写了一篇关于这个问题的章节.请参阅第10章:测试随机数生成器.
听起来你的优化器需要两种测试:
测试算法的整体有效性
测试算法实现的完整性
由于算法涉及随机化,(1)难以进行单元测试.任何随机过程的测试都会在某些时间内失败.您需要了解一些统计信息才能了解它应该失败的频率.有很多方法可以在测试的严格程度和失败的频率之间进行权衡.
但是有一些方法可以为(2)编写单元测试.例如,您可以在运行单元测试之前将种子重置为特定值.然后输出是确定性的.这不会让你评估算法的平均有效性,但那是(1).这样的测试可以作为一个旅程线:如果有人在维护期间将错误引入代码中,确定性单元测试可能会捕获该错误.
可能还有其他东西可以进行单元测试.例如,无论随机部分发生什么,也许您的算法可以保证在一定范围内返回值.也许某些价值应该总是积极的等等.
更新:我在Beautiful Testing中写了一篇关于这个问题的章节.请参阅第10章:测试随机数生成器.
单元测试不应该具有未知的通过/未通过状态.如果您的算法在多次使用相同输入运行时返回不同的值,那么您可能在算法中做了一些棘手的事情.
我将采用5种优化算法中的每一种并测试它们以确保给定一组输入x,每次都得到y的优化值.
编辑:要解决系统的随机组件,您可以引入传递种子的功能,以便使用随机数生成器,或者您可以使用模拟库(ala RhinoMocks)强制它使用特定的数字要求RNG提供随机数.
您的算法可能有一个随机组件.把它控制住.
你也可以
允许调用者为随机数生成器选择种子.然后在测试中使用硬编码的种子.
让呼叫者提供随机数发生器.然后在测试中使用伪随机数生成器.
第二个选项可能是最好的,因为这样可以更容易地推断出算法的正确结果.
在单元测试算法时,您要验证的是您已正确实现了算法.不是算法是否按预期执行.单元测试不应将待测代码视为黑盒子.
您可能需要有一个单独的"性能"测试至比较不同的算法如何运行(以及他们是否实际工作),但你的单元测试是用于测试你的实现算法.
例如,在实现Foo-Bar-Baz优化算法(TM)时,您可能不小心写了x:= x/2而不是x:= x/3.这可能意味着算法工作得更慢,但仍然找到相同的算法.您需要进行白盒测试才能找到这样的错误.
编辑:
不幸的是我无法为随机数生成器选择固定种子,因为我不想测试算法是否产生与以前完全相同的结果,但我想测试类似"有90%确定性我得到0.1或者0.1的结果"更好".
我看不出任何方法可以进行自动验证和随机测试.特别是如果你想有机会将真实误差与统计噪声区分开来.
如果你想测试"有90%的确定性我得到0.1或更好的结果",我建议如下:
double expectedResult = ...; double resultMargin = 0.1; int successes = 0; for(int i=0;i<100;i++){ int randomSeed = i; double result = optimizer.Optimize(randomSeed); if(Math.Abs(result, expectedResult)(请注意,此测试是确定性的).
4> Jon Skeet..:让测试运行,如果其中任何一个失败,重新运行这些测试 50次,看看他们失败的时间比例.(当然是以自动方式.)