29

在 ruby​​ 中生成正态分布随机数的一些代码是什么?

(注意:我回答了我自己的问题,但我会等几天再接受,看看是否有人有更好的答案。)

编辑:

搜索此内容时,我查看了由两次搜索产生的 SO 上的所有页面:

+“正态分布”红宝石

+高斯 +随机红宝石

4

4 回答 4

50

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

该方法可以封装在一个类中,该类逐个返回样本。

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)

在法律允许的范围内,antonakosRandomGaussian放弃了Ruby 类的所有版权和相关或邻近权利。本作品发表于:丹麦。

于 2011-05-30T15:44:58.130 回答
19

最初的问题要求提供代码,但作者的后续评论暗示了对使用现有库的兴趣。我对此也很感兴趣,我的搜索发现了这两个红宝石:

gsl -“GNU 科学库的 Ruby 接口”(需要您安装 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
于 2011-11-21T00:21:47.043 回答
12

对@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

当然,如果你真的关心速度,你应该实现Ziggurat 算法:)。

于 2012-02-13T19:15:38.210 回答
11

另一种选择,这个使用由 SciRuby 研究员之一编写的分发gem。

我认为它使用起来更简单一些。

require 'distribution'
normal = Distribution::Normal.rng(1)
norm_distribution = 1_000.times.map {normal.call}
于 2013-05-24T17:37:47.983 回答