【发布时间】: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