Код для генерации гауссовых (нормально распределенных) случайных чисел в Ruby - PullRequest
28 голосов
/ 29 апреля 2011

Какой код генерирует нормально распределенные случайные числа в рубине?

(Примечание: я ответил на свой вопрос, но подожду несколько дней, прежде чем принять решение, чтобы узнать, есть ли у кого-нибудь лучший ответ.)

EDIT:

В поисках этого я просмотрел все страницы на SO в результате двух поисков:

+ "нормальное распределение" ruby ​​

и

+ гауссовский + случайный рубин

Ответы [ 4 ]

48 голосов
/ 30 мая 2011

Python's random.gauss () и Boost's 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)

Насколько это возможно в соответствии с законодательством, antonakos отказался от всех авторских и смежных или смежных прав на класс RandomGaussian Ruby. Это произведение опубликовано в: Дании.


Заявление о лицензии не означает, что я забочусь об этом коде. Напротив, я не использую код, я не тестировал его и не программирую на Ruby.

19 голосов
/ 21 ноября 2011

В первоначальном вопросе задавался код, но последующий комментарий автора подразумевал интерес к использованию существующих библиотек.Меня заинтересовало то же самое, и мои поиски обнаружили следующие два рубиновых камня:

gsl - «Интерфейс Ruby для научной библиотеки GNU» (требуется установка GSL).Последовательность вызова для нормально распределенных случайных чисел со средним значением = 0 и заданным стандартным отклонением составляет

 rng = GSL::Rng.alloc
 rng.gaussian(sd)      # a single random sample
 rng.gaussian(sd, 100) # 100 random samples

rubystats - «порт статистических библиотек из PHPMath» (чистый рубин).Последовательность вызова для нормально распределенных случайных чисел с заданным средним и стандартным отклонением составляет

 gen = Rubystats::NormalDistribution.new(mean, sd)
 gen.rng               # a single random sample
 gen.rng(100)          # 100 random samples
11 голосов
/ 13 февраля 2012

+ 1 к ответу @ antonakos.Вот реализация 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 :).

10 голосов
/ 24 мая 2013

Другой вариант, этот, использующий гем дистрибутива , написанный одним из собратьев SciRuby.

Я думаю, его немного проще.

...