测试随机值-对这种方法有何想法?

时间:2020-03-06 14:36:58  来源:igfitidea点击:

好的,我一直在研究随机图像选择器和队列系统(因此我们不会经常看到相同的图像)。

一切都进行得很顺利(就我糟糕的代码而言),直到我变得随意为止。我想测试一下,但是我们如何测试呢?没有Debug.Assert(i.IsRandom)(可悲):D

所以,我在给它加茶水后就动了脑筋,并提出了以下建议,我只是想知道我是否能想到你的想法?

  • 基本上,我知道问题是随机的,所以我将其剥离给了一个委托(然后将其传递给对象构造函数)。
  • 然后,我创建了一个类,该类几乎执行与实时代码相同的逻辑,但是记住在私有变量中选择的值。
  • 然后,我将该代表委托给实时课程并进行了测试:

IE。

Debug.Assert(myObj.RndVal == RndIntTester.ValuePassed);

但是我忍不住想,我是在浪费时间吗?我进行了多次迭代,以查看它是否在任何时候跌落等。

我们是否认为我在浪费时间?还是我可以摆脱:

GateKiller的回答使我想起了这一点:

更新以澄清

  • 我应该补充一点,我基本上从不希望从Y大小的池中看到相同的结果多于X次。
  • 测试容器的添加基本上使我可以查看是否先前"随机"选择了任何先前选择的图像。
  • 从技术上讲,我猜这里的东西不是在RNG中测试(因为我从未编写过该代码),而是我希望从有限的池中随机得到结果,并且我想跟踪它们。

解决方案

随机就是随机。即使同一张图片连续出现4次,仍可以认为是随机的。

无法测试一个值是否真正是随机的。最好的办法是多次执行测试,并测试我们是否获得了适当的分布,但是如果结果确实是随机的,即使失败的可能性很小(很小)。

如果我们正在做白盒测试,并且知道随机种子,那么我们实际上可以计算出预期的结果,但是我们可能需要单独的测试来测试RNG的随机性。

我的观点是,任何随机因素都无法得到正确测试。

当然,我们可以尝试对其进行测试,但是要尝试的组合太多,因此,仅依靠RNG并抽查大量案例,效果会更好。

好吧,问题在于,根据定义,随机数可以重复(因为它们正在……等待它:随机)。也许我们想要做的就是保存最新的随机数,然后将计算出的随机数与之比较,如果相等,则只需计算另一个...但是现在数字变得不那么随机了(我知道没有"或者多或者少"这样的东西)随机性,但这次我只使用术语),因为可以保证它们不会重复。

无论如何,我们永远不要给随机数这么多的想法。 :)

正如其他人指出的那样,不可能真正测试随机性。我们可以(并且应该)使一种特定方法具有随机性,然后为其他所有方法编写单元测试。这样,我们可以测试所有其他功能,假设我们可以从最后一部分中获得一个随机数。

如果我们有一组固定的项目,并且不想让它们重复太久,请随机调整集合。然后,我们将确保我们不会连续两次看到同一张图片,就好像我们在收听前20名广播一样。等等。在重复之前,我们将完整浏览整个图片集。

Item[] foo = …
for (int idx = foo.size(); idx > 1; --idx) {
  /* Pick random number from half-open interval [0, idx) */
  int rnd = random(idx); 
  Item tmp = foo[idx - 1];
  foo[idx - 1] = foo[rnd];
  foo[rnd] = tmp;
}

如果我们有太多项目无法一次收集和整理(一个存储库中有成千上万张图像),则可以对同一方法添加一些"分而治之"的方法。随机播放图像组,然后随机播放每组图像。

听起来可能适用于我们修改后的问题陈述的一种略有不同的方法是,让"图像选择器"实现将其最近的选择历史记录保留在最长为Y的队列中。在返回图像之前,它将进行测试以查看其是否已经在队列" X"中进行了一次检查;如果是,它会随机选择另一个,直到找到一个通过的图像为止。

如果我们真的想测试随机数生成器的质量,那么我将不得不打开统计书。

存储随机值,并在使用下一个生成的随机数之前,对照存储的值进行检查。

The generation of random numbers is
  too important to be left to chance. -- Robert R. Coveyou

解决心理问题:

防止出现明显重复的一种不错的方法是从全套中随机选择一些项目,丢弃重复项。播放这些,然后选择其他几个。 "几"是多少取决于我们玩它们的速度以及整套设备的大小,但是例如,避免在" 20"中的较大值内重复," 5分钟"可能就可以了。以程序员身份进行用户测试时,我们会厌倦幻灯片演示,因此我们不是一个很好的测试对象。

为了测试随机化代码,我会说:

步骤1:指定代码必须如何将原始随机数映射到我们域中的选项,并确保代码正确使用了随机数生成器的输出。通过模拟生成器进行测试(如果是PRNG,则使用已知的测试值作为种子)。

第2步:确保生成器足够随机以符合目的。如果我们使用了库函数,请阅读文档。如果我们自己写,为什么呢?

步骤3(仅限高级统计人员):对生成器的输出进行一些随机性统计测试。确保我们知道测试中错误失败的可能性。

任何好的伪随机数生成器都可以让我们为生成器设置种子。如果使用相同的数字为生成器提供种子,则生成的随机数流将是相同的。那么,为什么不播种随机数生成器,然后根据特定的数字流创建单元测试呢?

根据要求进行测试:"这样我们就不会经常看到相同的图像"

要求100张图片。我们是否经常看到图片?

在Wikipedia上有方便的统计随机性测试和相关研究的列表。请注意,我们将不确定其中的大多数来源是否确实是随机的,只是我们已经排除了一些可以轻松预测的来源。

要获得一系列非重复的随机数:

  • 创建一个随机数列表。
  • 向每个随机数添加序列号
  • 按原始随机数对排序列表进行排序
  • 使用序列号作为新的随机数。

不要测试随机性,而要测试看看我们获得的结果是否令人满意(或者,在接受可能会令人满意的结果之前,尝试几次获得不期望的结果)。
如果我们要测试随机输出,将不可能确保我们永远不会得到不希望的结果,但是至少可以增加发现它发生的机会。

我或者选择N个Y大小的池,检查是否出现超过X倍的结果,或者选择一个N * Y大小的池,检查每组Y大小是否出现X倍以上的结果(1到Y,2到Y + 1、3到Y + 2等)。 N是多少取决于我们希望测试的可靠性。

随机函数和类似函数给只是伪随机数,伪随机数是通过函数产生的一系列数字。通常,我们将第一个输入参数(也称为"种子")赋予该函数,该参数用于产生第一个"随机"数。之后,将每个最后一个值用作循环的下一个迭代的输入参数。我们可以查看Wikipedia上有关"伪随机数生成器"的文章,那里的解释很好。

所有这些算法都有一些共同点:该系列在多次迭代后会重复执行。请记住,这些并不是真正的随机数,而只是看似随机的一系列数字。要选择一个发电机而不是另一个,我们需要问自己:我们想要什么?

我们如何测试随机性?确实可以。有很多测试。当然,第一个也是最简单的方法是运行伪随机数生成器很多次,然后编译每个结果出现的次数。最后,每个结果应该出现很多次,非常接近(迭代次数)/(可能结果的次数)。标准偏差越大,发电机越差。

第二个是:我们当时使用多少随机数?二三将它们成对(或者三联成对)并重复之前的实验:经过很长的迭代,每个预期结果应该至少出现一次,并且每个结果出现的次数也不应距离太远预期的。有一些发电机一次只能使用一到两个,但是当我们要使用三个或者三个以上时(兰杜有人吗?),它会突然失效。

还有其他更复杂的测试:有些涉及将结果以对数标度绘制,或者绘制到中间有一个圆的平面上,然后计算其中有多少图,其他...我相信上面的那两个就足够了大多数情况下(除非我们是一位挑剔的数学家)。

随机数是从分布中生成的。在这种情况下,每个值都应具有相同的出现可能性。如果我们计算出无限数量的随机数,则将获得精确的分布。

在实践中,多次调用该函数并检查结果。如果我们希望拥有N张图像,请计算100 * N个随机数,然后计算找到的每个预期数量中的多少个。大多数应该出现70-130次。使用不同的随机种子重新运行测试,以查看结果是否不同。

如果发现我们现在使用的发电机还不够好,我们可以轻松地找到一些东西。 Google的" Merenne Twister"比我们需要的随机得多。

为了避免图像再次出现,我们需要减少随机性。一种简单的方法是检查不允许的值(如果其中之一会重新计算)。

尽管我们无法测试随机性,但是可以测试数字序列的相关性或者分布。

难以测试的目标:每次需要一张图片时,请随机从4张图片中选择1张。

易于测试的目标:对于我们选择的每100张图像,这4张图像中的每张必须至少出现20次。

我同意亚当·罗森菲尔德。对于我们正在谈论的情况,唯一可以有效测试的是跨范围分布。

我通常遇到的情况是,我使用自己喜欢的语言的PRNG生成伪随机数,然后将其操纵到所需的范围内。为了检查我的操作是否影响了分布,我生成了一堆数字,对其进行操作,然后检查结果的分布。

为了获得良好的测试,我们所产生的数字至少应比范围所容纳的数量多几个数量级。我们使用的值越多,测试效果越好。显然,如果范围非常大,则此方法将无法工作,因为我们将不得不生成太多的数字。但是在情况下,它应该可以正常工作。

这是Perl中的一个例子,说明了我的意思:

for (my $i=0; $i<=100000; $i++) {
   my $r = rand;        # Get the random number
   $r = int($r * 1000); # Move it into the desired range
   $dist{$r} ++;        # Count the occurrences of each number
}

print "Min occurrences: ", (sort { $a <=> $b } values %dist)[1], "\n";
print "Max occurrences: ", (sort { $b <=> $a } values %dist)[1], "\n";

如果最小和最大出现次数之间的分布很小,则分布良好。如果范围很广,则分配可能不正确。我们也可以使用这种方法来检查是否覆盖了范围以及是否错过了任何值。

同样,生成的数字越多,结果越有效。我倾向于从小处着手,并在合理的时间内完成我的机器可以处理的任何事情,例如5分钟。

假设我们正在测试整数范围内的随机性,一种验证方法是创建一个gajillion(大约10,000左右)的"随机"数字并将其出现在直方图上。

******    ******           ****
***********************************************
*************************************************
*************************************************
*************************************************
*************************************************
*************************************************
*************************************************
*************************************************
*************************************************
         1         2         3         4         5
12345678901234567890123456789012345678901234567890

上面显示了"相对"正态分布。

如果它看起来更偏斜,例如:

******    ******           ****
    ************  ************  ************
    ************  ************  ***************
    ************  ************  ****************
    ************  ************  *****************
    ************  ************  *****************
   ***************************  ******************
   **************************** ******************
******************************* ******************
**************************************************
         1         2         3         4         5
12345678901234567890123456789012345678901234567890

然后,我们会发现随机性降低了。正如其他人所提到的,还有重复的问题也需要解决。

如果我们要使用生成器写一个说10,000个随机数的二进制文件,使用1到1024之间的一个随机数,并尝试使用某种压缩方式(zip,gzip等)压缩该文件,则可以比较两个文件大小。如果有很多"压缩",那么它并不是特别随机。如果大小变化不大,那就是"相当随机"。

为什么这样工作

压缩算法寻找模式(重复和其他),并以某种方式减少模式。查找这些压缩算法的一种方法是衡量文件中信息的数量。高度压缩的文件具有很少的信息(例如,随机性),而高度压缩的文件具有很多的信息(随机性)

有整本书可以写关于随机性的书,并可以评估某些东西是否看起来是随机的,但我将为我们节省一些数学知识。简而言之,我们可以使用卡方检验来确定"随机"分布与我们期望的拟合程度。

如果我们使用的是Perl,则可以使用Statistics :: ChiSquare模块为我们完成艰苦的工作。

但是,如果要确保图像均匀分布,则可能不希望它们是真正随机的。取而代之,我建议我们使用整个图像列表,随机排列该列表,然后在需要"随机"图像时从其中删除一个项目。当列表为空时,我们可以重新构建,重新随机播放并重复。

这种技术意味着给定一组图像,每个单独的图像在列表中的每次迭代最多只能出现一次。图像不禁会平均分布。

一切顺利,

保罗