【问题标题】:Testing Unpredictable Functions测试不可预测的函数
【发布时间】:2011-12-20 00:02:41
【问题描述】:

我目前正忙于在 Ruby 中实现有趣的数据结构,并且在测试没有可预测输出的函数时遇到了问题。我目前正在处理Bloom Filter,为了完整起见,我已经在下面包含了实现:

require "zlib"

class BloomFilter
  def initialize(size=100, hash_count=3)
    raise(ArgumentError, "negative or zero buffer size") if size <= 0
    raise(ArgumentError, "negative or zero hash count") if hash_count <= 0

    @size = size
    @hash_count = hash_count
    @buffer = Array.new(size, false)
  end

  def insert(element)
    hash(element).each { |i| @buffer[i] = true}
  end

  def maybe_include?(element)
    hash(element).map { |i| @buffer[i] }.inject(:&)
  end

  private :hash
  def hash(element)
    hashes = []

    1.upto(@hash_count) do |i|
      hashes << Zlib.crc32(element, i)
    end

    hashes.map { |h| h % @size }
  end
end

布隆过滤器的一个问题是它有可能通过错误地返回 true 来返回误报,以包含从未插入过滤器的元素。

有时过滤器的行为方式很容易测试:

b = BloomFilter.new(50, 5)

b.insert("hello")
puts b.maybe_include?("hello") # => true
puts b.maybe_include?("goodbye") # => false

但是,它有时会逆势而行,并以不可预测的方式表现。 (我在这里减小了缓冲区的大小,以便快速找到冲突。)

b = BloomFilter.new(5, 4)

b.insert("testing")
puts b.maybe_include?("testing") # => true
puts b.maybe_include?("not present") # => false
puts b.maybe_include?("false positive") # => true (oops)

突然之间,我们得到了字符串“false positive”,提供了一个......误报。我的问题是我们如何测试这个?

  • 如果我们选择刚刚发生的值来配合我们的测试,那么我 感觉测试变得太脆弱了。例如,如果我们改变 散列函数,那么我们可能仍然有一个完全正确的 Bloom 由于我们选择的值而导致某些测试开始失败的过滤器 测试原始实现。

  • 我的第二个想法是测试过滤器的行为是否符合预期 只需检查我们是否大致得到expected number of false positives 通过改变散列函数的数量和大小 内部缓冲区。虽然这种方法可能会测试整体粗糙 过滤器的正确性我担心它无法捕获 导致它针对个别情况报告不正确值的错误(例如 false 底片)。

我是否对上述两种测试方法的有效性过于悲观,还是我错过了一种方法来测试输出不可预测的类(例如 Bloom Filter)?

【问题讨论】:

    标签: testing probability bloom-filter


    【解决方案1】:

    你说得对,选择碰巧起作用的值是个坏主意。但是,您的第二个想法还不错。

    您应该始终能够测试应该在布隆过滤器中的值是否存在。您可以随机生成一些字符串,并检查阈值数量是否为误报。这样,如果您更改哈希函数,您的单元测试仍然可以工作,并且仍然会报告过滤器具有可接受的误报率。

    【讨论】:

      【解决方案2】:

      测试是为了确认您的期望。如果您无法自己推理 Bloom 过滤器将返回什么(考虑到脆弱性,正如您所提到的),您就不能期望有这种期望。 (我发誓我不是想双关语:P)

      我的第一个直觉是在所有有趣的哈希算法上确认 N 个生成的输入的误报百分比。这可以让您自动执行与手动执行这些测试一样多的安全性。

      为了实现这一点,我建议您对测试代码进行足够的分解,以便您可以简单地表达为:

      未经验证的代码

      class BloomFilterTestCase << TestCase
        def bloom_incidence(alg, pop, false_positives)
          define_method("test_bloom_incidence_${alg}_${pop}_${false_positives}") do  
            # code code code
          end
        end
      
        bloom_incidence :naive, 50, 0.05
      end
      

      【讨论】:

        【解决方案3】:

        布隆过滤器是一种节省空间的概率数据结构,用于测试元素是否是集合的成员。可能会出现误报,但不会出现误报。

        仅从 Bloom 过滤器的作用描述来看,应该清楚测试误报是没有意义的。阳性测试的结果本质上是不确定的,因此您不能对其进行测试以期望得到特定结果。您唯一可以保证并因此进行测试的事情是:

        • 函数返回一个布尔值
        • 函数不会抛出任何错误
        • 没有假阴性

        【讨论】:

          猜你喜欢
          • 2023-03-19
          • 2013-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-01-24
          • 2020-08-03
          • 2016-10-24
          • 1970-01-01
          相关资源
          最近更新 更多