web-dev-qa-db-ja.com

Rubyでガウス(正規分布)乱数を生成するコード

ルビーで正規分布の乱数を生成するためのコードは何ですか?

(注:私は自分の質問に答えましたが、誰かがより良い答えを持っているかどうかを確認するために受け入れる前に数日待ちます。)

編集:

これを検索して、2つの検索の結果であるSOのすべてのページを調べました:

+「正規分布」Ruby

そして

+ガウス+ランダムRuby

29
Eponymous

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

CC0(CC0)

法律の下で可能な範囲で、 antonakos は、RandomGaussian Rubyクラスのすべての著作権および関連する権利または隣接権を放棄しました。この作品は以下から公開されています:デンマーク。


ライセンスステートメントは、私がこのコードを気にするという意味ではありません。それどころか、私はコードを使用せず、テストもせず、Rubyでプログラミングもしていません。

48
antonakos

元の質問はコードを要求しましたが、著者のフォローアップコメントは、既存のライブラリを使用することに関心があることを示唆していました。私は同じことに興味があり、検索すると次の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
19
ronen

@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

もちろん、本当に速度を気にするなら、 ジッグラトアルゴリズム :)を実装する必要があります。

11
fearless_fool

別のオプション、これはSciRubyフェローの1人によって書かれた distribution gemを使用します。

少し使いやすいと思います。

require 'distribution'
normal = Distribution::Normal.rng(1)
norm_distribution = 1_000.times.map {normal.call}
10
Ryanmt