考虑一种随机调整数组元素的方法。

我想出了两个想法,两个都有明显的缺陷:


随机排列数组,然后确保其顺序与以前不同。这听起来不错,但是如果随机播放以相同顺序随机播放,则失败。 (不可能的,但是可能的。)
用恒定的种子对数组进行混洗,并根据预定的输出进行检查。这依赖于随机函数始终在给定相同种子的情况下始终返回相同的值。但是,有时这是一个无效的假设。

考虑第二个函数,该函数模拟掷骰子并返回随机数。您将如何测试此功能?您将如何测试函数...


从不返回超出给定范围的数字?
以有效分布形式返回数字? (对于一个骰子来说是统一的,对于大量骰子来说是正常的。)这里的单元测试甚至是正确的解决方案吗?如果不是,那是什么类型的测试?


只是为了让大家放心,我不是在编写自己的随机数生成器。

评论

紧的联轴器显示其头部。传递生成随机数的对象。然后,在测试过程中,您可以传递一个对象,该对象生成一组指定的数字,您知道这些数字后,洗牌后的面板是什么样的。您可以分别测试随机数生成器的随机性。

我会强烈考虑对shuffle使用现有的库例程(java Collections.shuffle()或类似的)。在developer.com/tech/article.php/616221/…上有一个关于编写有缺陷的随机播放算法的警告性故事。对于编写d6()函数,将对其进行足够的测试,以确保它不会生成超出范围的数字,然后对分布进行卡方检验(卡方对伪随机序列相当敏感)。还要看一下序列相关系数。

“这依赖于随机函数总是在给定相同种子的情况下返回相同值。但是,有时这是一个无效的假设。”我点击了链接,但没有看到无效的假设。它说得很清楚:“如果重复使用相同的种子,则会生成相同的数字序列。”

@Kyralessa“不能保证在.NET Framework的主要版本中,Random类中随机数生成器的实现不会保持相同。”因此,这不是一个大问题,但仍然需要考虑。

@Kyralessa我错过了那句话的重要部分:“结果,您的应用程序代码不应假定相同的种子将在不同版本的.NET Framework中产生相同的伪随机序列。”

#1 楼

我认为单元测试不是测试随机性的正确工具。单元测试应该调用一个方法,并针对期望值测试返回的值(或对象状态)。测试随机性的问题在于,您想要测试的大多数东西都没有期望值。您可以使用给定的种子进行测试,但这仅测试可重复性。它没有给您任何方法来衡量分布的随机性,甚至根本不是随机的。

幸运的是,您可以运行许多统计测试,例如Diehard Battery随机性测试。另请参见:



如何对伪随机数生成器进行单元测试?




Steve Jessop建议您找到与您所使用的RNG算法相同的经过测试的实现,并将其输出与选定的种子进行比较,并与您自己的实现进行比较。 />约翰·库克(John D. Cook)向读者介绍他的CodeProject文章“简单随机数生成”,其中包括Donald Knuth的第2卷“半数值算法”中提到的Kolmogorov-Smirnov测试的实现。产生的数字是统一的,卡方检验,并且检验均值和标准差在预期范围内。 (请注意,仅测试分布是不够的。[1,2,3,4,5,6,7,8]是均匀分布,但肯定不是随机的。)



具有返回随机结果的函数的单元测试



Brian Genisio指出,模拟RNG是使测试可重复的一种选择,并提供C#示例代码。再次,更多的人指出使用固定的种子值以确保可重复性,并进行简单测试以实现均匀分布,卡方等。



单元测试随机性是一篇Wiki文章,讨论了在尝试测试本质上不可重复的测试时已经涉及的许多挑战。我从中收集到了一个有趣的信息:压缩文件的随机性越小)。




评论


另一个用于统计随机性的良好测试套件是在fourmilab.ch/random中找到的“ ent”。

–user40980
2012年5月3日20:44

您可以总结一下您发布的一些链接,以确保答案的完整性吗?

– dlras2
2012年5月4日下午3:48

@DanRasmussen当然,我周末有时间这样做。

–比尔蜥蜴
2012年5月4日上午10:33

“……随机性的问题是没有期望值……” –具有讽刺意味的是,鉴于“期望值”是统计中定义明确的术语。尽管这不是您的意思,但它暗示了正确的解决方案:使用统计分布的已知属性,结合随机抽样和统计测试,确定算法是否具有很高的概率。是的,这不是经典的单元测试,但我想提一下它,因为在最简单的情况下,它只是查看……期望值的分布。

–康拉德·鲁道夫(Konrad Rudolph)
2012年5月4日17:43



Dieharder上著名的Diehard随机性测试电池有一个更新版本,其中包括由美国国家标准技术研究院(NIST)开发的统计测试套件(STS)。它可以在Ubuntu和其他发行版中随时运行:phy.duke.edu/~rgb/General/dieharder.php

–nealmcb
2014年12月20日20:24



#2 楼

1.对您的算法进行单元测试

对于第一个问题,我将构建一个假类,该类将提供一个您知道算法结果的随机数序列。这样,您可以确保在随机函数之上构建的算法能够正常工作。大致如下:

Random r = new RandomStub([1,3,5,3,1,2]);
r.random(); //returns 1
r.random(); //returns 3
...


2。看看您的随机函数是否有意义

对于单元测试,您应该添加多次运行的测试,并断言结果


在以下范围内您进行了设置(因此,骰子掷骰介于1到6之间),并且
显示合理的分布(进行多次测试,看看该分布是否在期望值的x%之内,例如,对于骰子掷骰,您应该看到如果将2滚动了1000次,它的出现时间将在10%到20%之间(1/6 = 16.67%)。

3。算法和随机函数的集成测试

您希望多久将数组按原始排序进行排序?进行几百次排序,并断言只有x%的时间不会改变。一旦使用了真正的随机函数,就无法再进行一次测试了。

根据经验(我写了遗传算法),我想说的是将算法的单元测试,分布测试您的随机函数,然后进行集成测试。

#3 楼

似乎被遗忘的PRNG的一个方面是,它的所有属性本质上都是统计性质的:您不能期望对数组进行改组将导致与您开始使用的数组不同的排列。基本上,如果您使用的是常规PRNG,则唯一可以保证的是,它不使用简单的模式(希望如此),并且在返回的数字集中具有均匀的分布。

正确测试PRNG至少需要运行100次,然后检查输出的分布情况(这是问题第二部分的直接答案)。

第一个问题几乎是相同的:使用{1,2,...,n}进行大约100次测试,并计算每个元素在每个位置的次数。如果改组方法很好,它们应该大致相等。

完全不同的问题是如何测试加密级PRNG。除非您真的知道自己在做什么,否则您可能不应该考虑这个问题。众所周知,人们只需进行几次“优化”或琐碎的编辑就可以破坏(阅读:打开灾难性漏洞)良好的密码系统。 。尽管我的观点仍然成立,但我还是会赞同比尔·蜥蜴的回答。单元测试本质上是布尔值-要么失败,要么成功,因此不适合测试PRNG(或使用PRNG的方法)的性能“好”,因为对该问题的任何答案都是量化的,而不是极地。

评论


我认为您的意思是每个元素在每个位置的出现次数应大致相等。如果它们始终完全相等,那就很不对劲。

–octern
2012年5月3日在21:23

@octern谢谢,我不知道该怎么写...到现在为止是完全错误的...

– K.Steff
2012年5月3日21:56

#4 楼

让它运行很多次并可视化您的数据。

下面是Coding Horror洗牌的示例,您可以看到算法是否正确:

很容易看到,每个可能的项目至少返回一次(边界确定),并且分配也确定。

评论


+1可视化是关键。在“分组密码”文章的ECB部分中,我总是喜欢带有企鹅图片的示例。自动化软件很少能检测到此类规律性

–Maksee
2012年5月6日7:44

嗯可视化的目的是表明分布不正常。天真的洗牌算法使某些命令比其他命令更有可能。请注意2341、2314、2143和1342条向右延伸了多远?

–hvd
16年1月2日,16:16

#5 楼

这有两个部分:测试随机化和测试使用随机化的事物。

测试随机化相对简单。您检查随机数生成器的周期是否符合预期(对于一些样本,该样本使用一些随机种子,且在某个阈值内),并且输出在大样本量上的分布是否符合预期

最好使用确定性伪随机数生成器来测试使用随机化的事物。由于基于种子(其输入)知道随机化的输出,因此您可以根据输入与预期输出进行正常的单元测试。如果您的RNG不是确定性的,请使用确定性(或不是随机的)模拟它。与使用它的代码隔离地测试随机化。

#6 楼

我发现通用指针在处理接受随机输入的代码时很有用:
检查预期随机性的边缘情况(最大和最小值,以及最大+1和最小-1值,如果适用)。 >检查数字具有拐点的位置(在上,上和下)(即-1、0、1或大于1,小于1且对于小数可能会使函数混乱的情况为非负数)。
检查一些完全不在允许输入范围内的地方。
检查一些典型情况。
您还可以添加随机输入,但是对于单元测试而言,具有相同的不良副作用每次运行测试时都不对值进行测试(尽管可以使用种子方法,但可以测试种子S或类似方法中的前1,000个随机数)。

要测试随机函数的输出,确定目标很重要。对于纸牌,目标是测试0-1随机生成器的均匀性,确定结果中是否出现了所有52张纸牌,还是某个其他目标(也许是所有此列表以及更多)?
在特定示例中,您必须假定随机数生成器是不透明的(就像对OS syscall或malloc-进行单元测试没有意义,除非您编写OS)。测量随机数生成器可能很有用,但是您的目标不是编写随机生成器,只是要看到您每次获得52张卡,并且它们会改变顺序。

长话大说,这里实际上有两个测试任务:测试RNG产生正确的分布,以及检查您的卡洗牌代码是否正在使用RNG产生随机结果。如果您正在编写RNG,请使用统计分析来证明您的分布,如果您正在编写洗牌机,请确保每个输出中有52张非重复的卡(这是通过检查使用的情况进行测试的更好案例) RNG)。

#7 楼

您可以依靠安全的随机数生成器

我只是有一个可怕的想法:您不是在编写自己的随机数生成器,是吗?

假设您不是,那么您应该测试您负责的代码,而不是其他人的代码(例如您框架的SecureRandom实现)。

测试代码

要测试代码是否正确响应,通常使用低可见性方法来生成随机数,以便可以轻松地将其覆盖。单元测试课。这种重写的方法有效地模拟了随机数生成器,使您可以完全控制生成的内容和时间。因此,您可以充分行使自己的代码,这是单元测试的目标。

很显然,您将检查边缘条件,并确保改组在给定适当输入的情况下完全按照您的算法指示进行。 br />如果不确定您的语言的安全随机数生成器不是真正的随机性还是有错误(提供超出范围的值等),则需要对输出进行数亿次迭代的详细统计分析。绘制每个数字的出现频率,它应该以相等的概率出现。如果结果偏斜,则应将发现的结果报告给框架设计者。他们肯定会对解决此问题感兴趣,因为安全随机数生成器是许多加密算法的基础。

#8 楼

好吧,您永远不会百分百确定,因此您能做的最好的事情就是数字很可能是随机的。选择一个概率-假设在误差范围内,给定一百万个样本,数字或项目样本将出现x次。将事物运行一百万次,看看它是否在利润范围内。幸运的是,计算机使这种事情变得很容易。

评论


但是,这样的单元测试是否被认为是好的做法?我一直认为单元测试应该尽可能简单:没有循环,分支或任何其他可以避免的事情。

– dlras2
2012年5月3日在18:26

单元测试应该是正确的。如果需要分支,循环,递归-这就是代价。您无法使用单线单元测试对极其复杂,高度优化的类进行单元测试。我已经实现了Dijkstra的算法来对一个类进行一次单元测试。

– K.Steff
2012年5月3日18:36



@ K.Steff,哇。您是否对单元测试进行了单元测试以验证Dijkstra算法是否正确?

–温斯顿·埃韦特(Winston Ewert)
2012年5月3日23:33

好的一点,事实上-是的,但是这次是“琐碎”的测试。但是,它们也是原始程序(A *)的单元测试。我认为这是一个非常好的做法-测试快速算法又会导致性能下降(但正确)。

– K.Steff
2012年5月3日23:40

#9 楼

为了测试随机数源生成的东西至少具有随机性,我将让测试生成相当大的字节序列,将它们写入临时文件,然后将其封装到Fourmilab的ent工具中。输入-t(简短)开关,这样它将生成易于解析的CSV。然后检查各种数字以确保它们是“好”。

要确定哪些数字是好的,请使用已知的随机数源来校准测试。给定一组良好的随机数时,测试应几乎总是通过。因为即使是真正随机的序列也有可能生成看起来是非随机的序列,所以您无法获得肯定通过的测试。您只需选择一些阈值即可使随机序列不太可能导致测试失败。

注意:您不能编写表明PRNG生成“随机”序列的测试。您只能编写一个测试,如果通过则表明PRNG生成的序列是“随机”的概率。欢迎来到随机的喜悦!

#10 楼

情况1:测试随机播放:

考虑一个数组[0,1,2,3,4,5],随机播放它,会出错吗?通常的东西:a)完全没有洗牌,b)洗牌1-5但不洗牌0,洗牌0-4但不洗牌5,洗牌,并且总是生成相同的图案,...测试以将其全部捕获:

随机播放100次,然后在每个插槽中添加值。每个插槽的总和应彼此相似。可以计算平均/标准差。 (5 + 0)/2=2.5,100*2.5 =25。例如,期望值约为25。

如果值超出范围,则极有可能出现假阴性。您可以计算出这个机会有多大。重复测试。好吧-当然,机会很小,测试连续两次失败。但是您没有例程可以自动删除您的源代码,如果单元测试失败,您呢?再次运行!

它可能连续失败3次?也许您应该试试彩票。

情况2:掷骰子

掷骰子问题是相同的问题。投掷骰子6000次。

for (i in 0 to 6000) 
    ++slot [Random.nextInt (6)];
return (slot.max - slot.min) < threshold;


#11 楼

其他答案则说明了如何确保函数是随机的,但不谈论对正确性的测试。
例如,如果函数应该生成介于0和1之间的随机数,请确保结果为在0到1之间。如果要随机播放列表,请确保输入和输出具有相同的元素。等