测试集合中不​​存在的新随机值

时间:2009-09-03 15:14:52

标签: unit-testing random

在测试期间,我一直在测试一段接收数字列表的代码,并且应该返回列表中不存在的新随机密钥。 有效范围是介于1到1,000,000之间的任何数字 - 这使得在测试中难以暴力破解。

测试此方法的最佳方法是什么?我考虑过用较小的范围(比方说100)进行测试,但考虑到基本的随机化算法,一旦列表接近其最大尺寸,这也会花费太长时间。

5 个答案:

答案 0 :(得分:3)

您可以在1-1000000中选择一个随机数,然后线性向前搜索,直到找到一个空闲位置(最终在1000000无法匹配后重叠为1)。 这样,数字的分布不是线性的(当集合大部分为空时,但随后变得越来越糟),但这比每次检查一个随机值要快得多(我希望随机性的偏差对于测试而言并不重要)但OTOH你确定你只需要一次对random()的调用,并且它不能用超过1000000次的检查来找到空的空间。

答案 1 :(得分:1)

我想知道你是否可以分两部分打破你的功能(或测试或两者):

  1. 随机数生成(至少属于您的代码的部分,而不是我猜的标准API调用,除非您也想测试它)。例如,根据您的技术,您可以拥有此代码(或更精炼的版本):
  2. 调用方法的事实必须返回一个不在列表中的值。

    public class RandomGenerator {  
      public int getValue() {  
        return `<random implementation>`;  
      }  
    }
    
    public class RandomNewGenerator {
       RandomGenerator randomGenerator = new RandomGenerator();
       public int getValue(List<Integer> ints) { 
          // note that a Set<Integer> would be more efficient
          while(true) {
            Integer i = randomGenerator.getValue();
            if (!ints.contains(i)) {
              return i;
            }
          }
       }
    }
    
  3.   

    在实际代码中,我会更改内容(使用接口,使用Spring注入等等)...

    这样,在您对RandomNewGenerator的测试中,您可以使用返回已知值系列的实现覆盖RandomGenerator。 然后,您可以在不面对任何随机的情况下测试您的RandomNewGenerator。

    我相信这确实是JUnit测试的精神,使它们变得简单,快速,甚至更好:可重复!最后的质量实际上允许您的测试用作回归测试,这非常方便。


    示例测试代码:

        public class RandomNewGeneratorTest {
          // do the set up 
          private List<Integer> empties = ...//
          private List<Integer> basics =  ...  // set up to include 1,2, 7, 8
    
          private class Random extends RandomNewGenerator {
             int current;
             Random(int initial) {
                current = initial;
             }
             public int getValue() {  
               return current++; // incremental values for test, not random
             }
           }
    
          public void testEmpty() {
             RandomNewGenerator randomNewGenerator = new RandomNewGenerator();
             // do a simple injection of dependency
             randomNewGenerator.randomGenerator = new Random(1); 
             // random starts at 1, builds up
             assertEquals(1, randomNewGenerator.getValue(empties);
             assertEquals(2, randomNewGenerator.getValue(empties);
             assertEquals(3, randomNewGenerator.getValue(empties);
             assertEquals(4, randomNewGenerator.getValue(empties);
          }
    
          public void testBasic() {
             RandomNewGenerator randomNewGenerator = new RandomNewGenerator();
             // do a simple injection of dependency
             randomNewGenerator.randomGenerator = new Random(5); 
             // random starts at 5, builds up
             // I expect 7, 8 to be skipped
             assertEquals(5, randomNewGenerator.getValue(basics);
             assertEquals(6, randomNewGenerator.getValue(basics);
             assertEquals(9, randomNewGenerator.getValue(basics);
          }
    
        }
    

    请注意,此代码仅为原始样本。你可以以任何你需要的方式改变它,例如通过向随机生成器提供它必须返回的一系列值。例如,您可以测试连续两次返回相同的数字。

答案 2 :(得分:0)

长什么意思?将值与列表中的1.000.000值进行比较应该只需要几毫秒。我看不到其他解决方案,然后比较所有值除了列表已排序,你可以缩小范围进行检查。您当然可以对列表进行排序,然后执行不超过20步的二进制搜索,但排序将比线性搜索更加昂贵。

我刚刚在一台非常慢的电脑上进行了测试,在C#中扫描一个给定数字的1.000.000数字的列表需要大约20毫秒。使用数组需要14毫秒。那不够快吗?二进制搜索在0.3微秒内完成了这项工作。最后使用哈希集查找只花了大约90纳秒。

如果你必须编写算法,我建议做一个简单的技巧。带到列表 - 一个带有分配的编号,一个带有未分配的编号,从1到1.000.000的所有编号开始。如果您需要一个新数字,只需获得一个介于零(包含)和未分配数字列表长度(随机数)之间的随机数,选择此索引处的数字并将其移动到指定的数字列表。完成。

我也测试了这种方法,使用未分配的数字的哈希集来加速删除和分配的列表花了大约460毫秒来从未分配的数字列表中获取所有1.000.000个数字。数字。在给定范围内生成新的唯一随机数仅约460纳秒。您必须使用排序字典来避免随机数生成器和散列算法之间的干扰。

最后你也可以把1到1.000.000之间的数字,但是它们放入一个列表中,将它们拖放一段时间,然后从列表中取出一个接一个。除了洗牌之前的最初时间,这将立即运行。

答案 3 :(得分:0)

可能有效的一种方法是获取初始列表并为1到1,000,000的所有索引i填充100万个元素向量,如果i,则填充1,如果为i,则填充0 s未被采纳。

计算初始列表的大小,调用此大小j

生成随机数0 <= j < sj。循环遍历数组,找到{{1}}元素为0,然后返回该元素。

编辑:仔细检查一下@ lapo的回答 - 我的回答似乎相同,但速度有点慢。

答案 4 :(得分:0)

一旦您选择的数字组开始变得过满,Lapo答案的分布就不是线性的。通过以下修改,您将获得均匀的整数分布:

  • 将一组初始数字保存在一个位数组中,其中位数组中的每个元素都对应于初始数组中的数字。 True表示项目中存在项目,否则为false。

  • 接下来,创建一个1到1,000,000的整数数组。 Shuffle数组。这组数字将是您的新密钥。

  • 按住指向新密钥列表中最后一个索引的指针。如果要生成新密钥,请将指针递增到新密钥中的下一个项目;您可以在恒定时间内测试它是否已在初始设置中选择。如果该项已存在于集合中,则将指针递增到新键中的下一项,否则返回该项并将其在位数组中的状态设置为true。