You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PRNG的多输入测试:O(n)与O(nlog(n))算法验证疑问

这种用“可信慢算法”校验“优化快算法”的随机测试思路非常棒,我在实际工作中也经常用这种方式验证高性能实现的正确性。针对你的问题,我来分享一下具体的实践方案:

核心结论

当实验失败时,不需要提前手动为每个失败案例创建单元测试,但必须建立自动捕获、记录并固化失败案例的机制——这既能帮你快速定位问题,又能防止后续代码修改时出现回归。

具体操作方案

1. 先保证失败案例可复现

随机测试最大的痛点是“复现难”,所以当断言失败时,你要第一时间把导致失败的关键信息记录下来:

  • 当前的随机种子(虽然你用了固定种子,但实验运行到第几次失败也很关键,或者直接保存生成的数组本身)
  • 失败的输入数组内容
  • 慢算法给出的预期结果和快算法的实际输出

修改你的测试代码,加入异常捕获来输出这些信息:

public static int MySeed = 1000000007;
public static Random random;

[TestMethod()]
public void TestSeveralInputSizes() {
    random = new Random(MySeed);
    int numberOfExperiments = (int)1e4;
    int maxArraySize = (int)1e2;
    
    while (numberOfExperiments-- > 0) {
        var array = GenerateRandomArray(random.Next(maxArraySize));
        long expectedAnswer = SlowSolution(array);
        long myAnswer = FastSolution(array);
        
        try {
            Assert.AreEqual(expectedAnswer, myAnswer);
        } catch (AssertFailedException ex) {
            // 打印所有关键信息,方便你复现问题
            Console.WriteLine($"❌ Failed experiment!");
            Console.WriteLine($"Seed used: {MySeed}");
            Console.WriteLine($"Experiment number (remaining): {numberOfExperiments + 1}");
            Console.WriteLine($"Input array: [{string.Join(", ", array)}]");
            Console.WriteLine($"Expected result: {expectedAnswer} | Actual result: {myAnswer}");
            
            // 可选:直接生成可复用的单元测试代码片段
            Console.WriteLine("\n📋 Copy this test case to your test class:");
            string testId = Guid.NewGuid().ToString().Substring(0, 8);
            Console.WriteLine($"[TestMethod()]");
            Console.WriteLine($"public void Test_Failure_{testId}() {{");
            Console.WriteLine($"    var testArray = new long[] {{ {string.Join(", ", array)} }};");
            Console.WriteLine($"    long expected = {expectedAnswer};");
            Console.WriteLine($"    Assert.AreEqual(expected, FastSolution(testArray));");
            Console.WriteLine($"}}");
            
            // 不要吞掉异常,让测试框架标记这个测试为失败
            throw;
        }
    }
}

2. 自动固化失败案例为永久单元测试

一旦捕获到失败案例,你可以选择:

  • 手动复制上面生成的代码片段,创建一个新的单元测试方法(最简单直接)
  • 进阶方案:写一个小工具类,在捕获异常时自动将测试用例写入项目的测试文件中(比如用文件IO操作,把生成的代码追加到测试类的.cs文件末尾)

这么做的好处是:

  • 后续每次运行测试时,这个失败案例都会被自动执行,确保你修复问题后不会再出现回归
  • 其他开发者也能直接看到这个边界案例,理解算法需要处理的特殊情况

3. 保留原有的随机测试作为“模糊测试”

不要因为有了单独的失败测试就删掉原来的随机循环测试——它的作用是持续发现新的边界案例。你可以把它标记为[TestCategory("FuzzTest")],方便在日常构建中选择性运行(比如每天跑一次全量模糊测试,而每次提交只跑固定的失败案例测试)。

补充建议

  • 如果你担心随机测试运行时间太长,可以分阶段调整:开发阶段用较小的numberOfExperiments(比如100次),CI构建时用全量的1e4次
  • 可以给GenerateRandomArray加入更多参数,比如生成包含负数、重复值、空数组、单元素数组等特殊场景的输入,覆盖更多边界情况

内容的提问来源于stack exchange,提问作者Raudel Ravelo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:29:19