RushCheckでFloatのカスタマイズをしてみた
poor man's rubyでRushCheckを使うため、Floatのカスタマイズをしてみました。バグが出やすいところを集中的にテストできるようにランダムの分布を制御できるようにしました。
統計とか詳しくないので、これでいいのか知らないですが、正規分布の組み合わせで分布を表すようにしました。
ソースコードです。
# = specialfloat.rb require 'rushcheck/float' class SpecialFloat<Float def self.normal_dist_list [ # 頻度 期待値, 分散 [ 100, 0.0, 3.0], [ 100, 100.0, 3.0], [ 50, 50.0, 10.0], ] end @@total_weight = normal_dist_list.inject(0) {|t, e| t += e[0]} def self.arbitrary RushCheck::Gen.new do |n, r| cw = @@total_weight rc = nil normal_dist_list.each do |e| a, r = lrand(r) if cw == 0 or (a * cw).to_i < e[0] then rc = dist_normal(e[1], e[2], n, r) break end cw -= e[0] end rc end end private extend RushCheck::HsRandom # 渡されたランダムジェネレータを使って(0..1)の一様乱数を得る def self.lrand(r) n, r = random(r, 0, 1) [n.abs, r] end # 期待値e, 分散vの正規分布に従う乱数を返す def self.dist_normal(e, v, n, r) sn = ((1..12).inject(0.0) {|t, i| a, r = lrand(r) t += a }) - 6 sn * v + e end end
normal_dist_listメソッドで分布を制御します。normal_dist_listは正規分布のパラメータの配列です。
正規分布のパラメータは、次の3要素の配列です。
例えば、0と100付近に特異点があるので、そのあたりをしっかりチェックしたい、でも他も一応見ておきたいという時、こんな感じで指定できます。
def self.normal_dist_list [ # 頻度 期待値, 分散 [ 100, 0.0, 3.0], [ 100, 100.0, 3.0], [ 50, 50.0, 10.0], ] end
これの分散がどうなるか分かりやすくするため、gnuplotを使ってグラフ化できるようにしました。今日1枚が上のサンプルの分布図です。rgplot(http://rubyforge.org/projects/rgplot)を使っています。
分布を表示するリストです。実行すると、カレントディレクトリにfoo.pngが生成されますので注意してください。
require 'rubygems' require 'rushcheck' require 'gnuplot' require 'specialfloat' OutputFormat = "png" OutputFile = "foo.png" Gnuplot.open do |gp| Gnuplot::Plot.new( gp ) do |plot| plot.terminal OutputFormat plot.output OutputFile plot.title "Special Float Distination" data = [] rc = RushCheck::Assertion.new(SpecialFloat)do |a| data.push a true end rc.check(RushCheck::Config.new(10000)) ma = data.max.to_i + 10 mi = data.min.to_i - 10 x = [] y = [] (mi*10..ma*10).each { |n| p n x << (n / 10.0) y << 0 } data.each do |n| y[(n * 10).to_i - mi*10] += 1 end plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds| ds.with = "dot" ds.notitle end end end