2011-04-28 53 views
26

¿Qué código sirve para generar números aleatorios distribuidos normalmente en ruby?Código para generar números aleatorios gaussianos (normalmente distribuidos) en Ruby

(Nota: Yo respondí a mi propia pregunta, pero voy a esperar unos días antes de aceptar a ver si alguien tiene una mejor respuesta.)

EDIT:

Buscando por esto, mirado a todas las páginas en resultante SO de las dos búsquedas:

+ "distribución normal" ruby ​​

y

+ + gaussiano aleatorio rubí

+0

¿Alguna pregunta relacionada con el cheque (ver el panel lateral derecho)? – Gumbo

+0

Sí, revisé y aunque hay lugares que tienen el algoritmo, nadie lo ha codificado en Ruby. Es una tarea tan común que realmente debería estar en la biblioteca estándar. Pero en su defecto, creo que el código de copiar y pegar debe ser encontrado en StackOverflow. – Eponymous

+0

Puede ser una buena idea mencionar lo que ha marcado, de modo que las personas que piensan en responder no lo verifiquen, a menos que piensen que se han perdido algo. –

Respuesta

43

de random.gauss() Python y Boost normal_distribution ambos utilizan el Box-Muller transform, por lo que debe ser lo suficientemente bueno para Ruby también.

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 

El método se puede incluir en una clase que devuelve las muestras una a una.

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)

En la medida que lo permita la ley, antonakos haya renunciado a los derechos de autor y derechos afines o vecinos a la clase Rubí RandomGaussian. Este trabajo se publica desde: Dinamarca.


La declaración de la licencia no significa que me preocupe este código. Por el contrario, no uso el código, no lo he probado y no programo en Ruby.

+0

¿Podría agregar una licencia permisiva a su código (BSD/CC-0 o algo así) (ya que está destinado a la reutilización de cortar y pegar) – Eponymous

+4

@ Eponymous Done. – antonakos

+2

Up-vote solo para el bloque sig;) – drewish

19

La pregunta original pedía el código, pero el comentario de seguimiento del autor implicaba un interés en el uso de las bibliotecas existentes. Estaba interesado en lo mismo, y mis búsquedas encontraron estas dos gemas ruby:

gsl - "Interfaz Ruby para la Biblioteca Científica GNU" (requiere que instale GSL). La secuencia de marcación por números aleatorios distribuidos normalmente con media = 0 y una desviación estándar dada es

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

rubystats - "un puerto de las bibliotecas de estadísticas de PHPMath" (rubí puro). La secuencia de llamada para números aleatorios normalmente distribuidos con una media y desviación estándar dada es

gen = Rubystats::NormalDistribution.new(mean, sd) 
gen.rng    # a single random sample 
gen.rng(100)   # 100 random samples 
10

+1 en la respuesta de @ antonakos. Aquí está la implementación de Box-Muller que he estado usando; que es esencialmente idéntico, pero un poco de código más estricto:

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 = [email protected]_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 

Por supuesto, si realmente se preocupaba por la velocidad, se debe implementar la Ziggurat Algorithm :).

8

Otra opción, esta usando la gema distribution, escrita por uno de los compañeros de SciRuby.

Es un poco más fácil de usar, creo.

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

Al mirar este código, no tengo idea de cuál es la desviación estándar esperada. –

+1

Vaya aquí: http://rubydoc.info/gems/distribution/0.7.0/Distribution/Normal/Ruby_ Verá que el rng (1) especifica la media, y que puede especificar la desviación estándar desea pasar el parámetro adicional 'Distribution :: Normal.rng (mean, standard_deviation)' al que llama para proporcionar un valor aleatorio en esa distribución. – Ryanmt

+0

El enlace correcto es: [distribución] (https://rubygems.org/gems/distribution) – pisaruk

Cuestiones relacionadas