使用条件对概率分布进行单元测试

Unit-testing a probability distribution with conditionals

我有一个调用 rand() 的函数 choose(elems) -> elem,这使得它具有不确定性。

为了能够更好地测试这个,我想我可以把这个函数分成两部分,

generate_choices(elems, ...) -> distribution
choose(distribution) -> elem

其中 choose()rand() 的薄包装器,generate_choices() 生成一个分布,从中绘制元素。然后我可以确定性地测试这个概率分布是否符合预期。

分布均匀但是有两个条件:

  1. 如果elems不够,统一添加随机fallback元素
  2. 如果还是不够elems,统一添加随机默认元素

一些例子:

generate_choices([a, b, c, d], [], []) -> [a, b, c, d]
generate_choices([a, b, c], [fallback1], []) -> [a, b, c, fallback1]
generate_choices([a, b, c], [fb1, fb2], []) -> [a, b, c, (fb1 | fb2)]
generate_choices([a, b], [fb1, fb2], [default1]) -> [a, b, (fb1 | fb2), default1]
generate_choices([a, b], [fb1, fb2], [d1, d2]) -> [a, b, (fb1 | fb2), (d1 | d2) ]
generate_choices([a], [fb1, fb2], [d1, d2]) -> [a, (fb1|fb2), (d1|d2) ]

我的问题是:我应该如何建模 distribution

至少有两种方法可以测试使用随机数的函数。您可能希望同时进行这两种测试。

(1)一种是设置随机数生成器的初始状态,生成一些样例,通过检查验证样例是否正确。然后将这些示例作为预期输出放入您的测试脚本中,并在脚本开始时设置相同的初始状态。

(2) 另一种测试是生成大量示例,并验证这些示例平均满足预期的属性。这是一个非确定性测试,因为对于随机数生成器生成的某些序列,测试可能会失败。您将不得不接受一些小的失败概率;好消息是,您可以通过测试大量示例并使测试的公差足够大来使概率足够小。

(2a) 例如,在您给出的最简单的情况下,输入是一个序列,输出是该序列的排列。如果生成大量示例,您应该会发现所有排列都具有相同的频率,在一定的公差范围内。显然这个测试受到测试输入长度的限制,因为有 n!长度为 n 的输入的排列。

您可以通过考虑每个不同排列的比例分布来得出公差。每个排列的概率为 1/n!,每个排列的预期数量为(m 乘以 1/n!),其中 m 是生成的排列数。每个排列的数量的方差是(m 乘以 1/n!乘以(1 - 1/n!)),标准差是它的平方根。您可以将公差区间近似为(预期数加上或减去标准偏差的倍数)。您可以通过更仔细地考虑分布来获得更精确的间隔。

(2b) 测试排列的另一种方法是查看输出的第一个元素与输入的第一个元素相等的次数,输出的第一个元素等于输入的第二个元素,第一个元素输入等于输入的第三个元素,...,输入的第二个元素,...,第三个元素,...输入的最后一个元素。对于比测试 2a 更长的序列,这可能是可行的。同样,游戏是计算出每个箱子中预期数字的分布,并从中得出公差。