ルビーで正規分布の乱数を生成するためのコードは何ですか?
(注:私は自分の質問に答えましたが、誰かがより良い答えを持っているかどうかを確認するために受け入れる前に数日待ちます。)
編集:
これを検索して、2つの検索の結果であるSOのすべてのページを調べました:
+「正規分布」Ruby
そして
+ガウス+ランダムRuby
Pythonの random.gauss() とBoostの normal_distribution はどちらも Box-Muller変換 を使用するので、Rubyも。
def gaussian(mean, stddev, Rand)
theta = 2 * Math::PI * Rand.call
rho = Math.sqrt(-2 * Math.log(1 - Rand.call))
scale = stddev * rho
x = mean + scale * Math.cos(theta)
y = mean + scale * Math.sin(theta)
return x, y
end
このメソッドは、サンプルを1つずつ返すクラスにまとめることができます。
class RandomGaussian
def initialize(mean, stddev, Rand_helper = lambda { Kernel.Rand })
@Rand_helper = Rand_helper
@mean = mean
@stddev = stddev
@valid = false
@next = 0
end
def Rand
if @valid then
@valid = false
return @next
else
@valid = true
x, y = self.class.gaussian(@mean, @stddev, @Rand_helper)
@next = y
return x
end
end
private
def self.gaussian(mean, stddev, Rand)
theta = 2 * Math::PI * Rand.call
rho = Math.sqrt(-2 * Math.log(1 - Rand.call))
scale = stddev * rho
x = mean + scale * Math.cos(theta)
y = mean + scale * Math.sin(theta)
return x, y
end
end
法律の下で可能な範囲で、 antonakos は、RandomGaussian
Rubyクラスのすべての著作権および関連する権利または隣接権を放棄しました。この作品は以下から公開されています:デンマーク。
ライセンスステートメントは、私がこのコードを気にするという意味ではありません。それどころか、私はコードを使用せず、テストもせず、Rubyでプログラミングもしていません。
元の質問はコードを要求しましたが、著者のフォローアップコメントは、既存のライブラリを使用することに関心があることを示唆していました。私は同じことに興味があり、検索すると次の2つが見つかりましたRuby gems:
gsl -"RubyインターフェイスからGNU Scientific Library"(GSLをインストールする必要があります)。平均値が0で指定された正規分布の乱数の呼び出しシーケンス標準偏差は
rng = GSL::Rng.alloc
rng.gaussian(sd) # a single random sample
rng.gaussian(sd, 100) # 100 random samples
rubystats -"PHPMathからの統計ライブラリの移植"(純粋なRuby)。与えられた平均と標準偏差を持つ正規分布の乱数の呼び出しシーケンスは次のとおりです。
gen = Rubystats::NormalDistribution.new(mean, sd)
gen.rng # a single random sample
gen.rng(100) # 100 random samples
@antonakosの回答に+1。これが私が使ってきたBox-Mullerの実装です。それは本質的に同じですが、少しタイトなコードです:
class RandomGaussian
def initialize(mean = 0.0, sd = 1.0, rng = lambda { Kernel.Rand })
@mean, @sd, @rng = mean, sd, rng
@compute_next_pair = false
end
def Rand
if (@compute_next_pair = !@compute_next_pair)
# Compute a pair of random values with normal distribution.
# See http://en.wikipedia.org/wiki/Box-Muller_transform
theta = 2 * Math::PI * @rng.call
scale = @sd * Math.sqrt(-2 * Math.log(1 - @rng.call))
@g1 = @mean + scale * Math.sin(theta)
@g0 = @mean + scale * Math.cos(theta)
else
@g1
end
end
end
もちろん、本当に速度を気にするなら、 ジッグラトアルゴリズム :)を実装する必要があります。
別のオプション、これはSciRubyフェローの1人によって書かれた distribution gemを使用します。
少し使いやすいと思います。
require 'distribution'
normal = Distribution::Normal.rng(1)
norm_distribution = 1_000.times.map {normal.call}