Code pour générer des nombres aléatoires gaussiens (normalement distribués) dans Ruby
Quel est le code pour générer des nombres aléatoires normalement distribués dans ruby?
(Note: j'ai répondu à ma propre question, mais j'attendrai quelques jours avant d'accepter de voir si quelqu'un a une meilleure réponse.)
Modifier:
En cherchant ceci, j'ai regardé toutes les pages sur SO résultant des deux recherches:
+"distribution normale" ruby
Et
+ gaussien + Ruby aléatoire
4 réponses
Python aléatoire.gauss () et Boost normal_distribution utilisent tous les deux la transformation Box-Muller , donc cela devrait aussi être suffisant pour 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
La méthode peut être enveloppée dans une classe qui renvoie les échantillons un par un.
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
Dans la mesure du possible en vertu de la loi, antonakos a renoncé à tous les droits d'auteur et droits connexes ou voisins à la classe RandomGaussian
Ruby. Ce travail est publié à partir de: Danemark.
La déclaration de licence ne signifie pas que je me soucie de ce code. Au contraire, je n'utilise pas le code, je ne l'ai pas testé, et je ne Programme pas en Ruby.
La question initiale demandait du code, mais le commentaire de suivi de l'auteur impliquait un intérêt à utiliser les bibliothèques existantes. J'étais intéressé par la même chose, et mes recherches ont révélé ces deux gemmes de rubis:
Gsl - "Interface Ruby à la bibliothèque scientifique GNU" (nécessite l'installation de GSL). La séquence d'appel pour les nombres aléatoires normalement distribués avec moyenne = 0 et un écart type donné est
rng = GSL::Rng.alloc
rng.gaussian(sd) # a single random sample
rng.gaussian(sd, 100) # 100 random samples
Rubystats - " un port des bibliothèques de statistiques de PHPMath" (ruby pur). La séquence d'appel pour les nombres aléatoires normalement distribués avec une moyenne et un écart type donnés est
gen = Rubystats::NormalDistribution.new(mean, sd)
gen.rng # a single random sample
gen.rng(100) # 100 random samples
+ 1 sur la réponse de @antonakos. Voici l'implémentation de Box-Muller que j'ai utilisée; c'est essentiellement identique mais un code légèrement plus serré:
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
Bien sûr, si vous vous souciez vraiment de la vitesse, vous devriez implémenter l'algorithme Ziggurat :).
Une autre option, celle-ci utilisant la gem distribution , écrite par L'un des boursiers SciRuby.
C'est un peu plus simple à utiliser, je pense.
require 'distribution'
normal = Distribution::Normal.rng(1)
norm_distribution = 1_000.times.map {normal.call}