Implemente una API para distribuciones de probabilidad

9

Introducción

En este desafío, su tarea es implementar una colección de funciones simples que juntas formen una mini biblioteca utilizable para distribuciones de probabilidad simples. Para acomodar algunos de los lenguajes más esotéricos que a la gente le gusta usar aquí, las siguientes implementaciones son aceptables:

  1. Un fragmento de código que define una colección de funciones con nombre (o equivalentes más cercanos).
  2. Una colección de expresiones que evalúan funciones nombradas o anónimas (o equivalentes más cercanos).
  3. Una sola expresión que evalúa varias funciones con nombre o anónimas (o equivalentes más cercanos).
  4. Una colección de programas independientes que toman entradas de la línea de comando, STDIN o equivalente más cercano, y salen a STDOUT o equivalente más cercano.

Las funciones

Deberá implementar las siguientes funciones, utilizando nombres más cortos si lo desea.

  1. uniformtoma como entrada dos números de punto flotante ay b, y devuelve la distribución uniforme sobre [a,b]. Puedes asumir eso a < b; El caso a ≥ bno está definido.
  2. blendtoma como entradas tres distribuciones de probabilidad P, Qy R. Devuelve una distribución de probabilidad S, que extrae valores x, yy zde P, Qy R, respectivamente, y produce yif x ≥ 0, y zif x < 0.
  3. overtoma como entrada un número de coma flotante fy una distribución de probabilidad P, y devuelve la probabilidad que se x ≥ fmantiene para un número aleatorio xextraído de P.

Como referencia, overse puede definir de la siguiente manera (en pseudocódigo):

over(f, uniform(a, b)):
    if f <= a: return 1.0
    else if f >= b: return 0.0
    else: return (b - f)/(b - a)

over(f, blend(P, Q, R)):
    p = over(0.0, P)
    return p*over(f, Q) + (1-p)*over(f, R)

Puede suponer que todas las distribuciones de probabilidad dadas overse construyen usando uniformy blend, y que lo único que un usuario va a hacer con una distribución de probabilidad es alimentarlo blendo over. Puede usar cualquier tipo de datos conveniente para representar las distribuciones: listas de números, cadenas, objetos personalizados, etc. Lo único importante es que la API funciona correctamente. Además, su implementación debe ser determinista, en el sentido de que siempre devuelve la misma salida para las mismas entradas.

Casos de prueba

Sus valores de salida deben ser correctos al menos a dos dígitos después del punto decimal en estos casos de prueba.

over(4.356, uniform(-4.873, 2.441)) -> 0.0
over(2.226, uniform(-1.922, 2.664)) -> 0.09550806803314438
over(-4.353, uniform(-7.929, -0.823)) -> 0.49676329862088375
over(-2.491, uniform(-0.340, 6.453)) -> 1.0
over(0.738, blend(uniform(-5.233, 3.384), uniform(2.767, 8.329), uniform(-2.769, 6.497))) -> 0.7701533851999125
over(-3.577, blend(uniform(-3.159, 0.070), blend(blend(uniform(-4.996, 4.851), uniform(-7.516, 1.455), uniform(-0.931, 7.292)), blend(uniform(-5.437, -0.738), uniform(-8.272, -2.316), uniform(-3.225, 1.201)), uniform(3.097, 6.792)), uniform(-8.215, 0.817))) -> 0.4976245638164541
over(3.243, blend(blend(uniform(-4.909, 2.003), uniform(-4.158, 4.622), blend(uniform(0.572, 5.874), uniform(-0.573, 4.716), blend(uniform(-5.279, 3.702), uniform(-6.564, 1.373), uniform(-6.585, 2.802)))), uniform(-3.148, 2.015), blend(uniform(-6.235, -5.629), uniform(-4.647, -1.056), uniform(-0.384, 2.050)))) -> 0.0
over(-3.020, blend(blend(uniform(-0.080, 6.148), blend(uniform(1.691, 6.439), uniform(-7.086, 2.158), uniform(3.423, 6.773)), uniform(-1.780, 2.381)), blend(uniform(-1.754, 1.943), uniform(-0.046, 6.327), blend(uniform(-6.667, 2.543), uniform(0.656, 7.903), blend(uniform(-8.673, 3.639), uniform(-7.606, 1.435), uniform(-5.138, -2.409)))), uniform(-8.008, -0.317))) -> 0.4487803553043079
Zgarb
fuente
2
¿Podemos usar funciones incorporadas para hacerlas?
Mutador
@ AndréMuta Olvidé que Mathematica probablemente tiene incorporados para todo esto ... pero los permitiré, siempre y cuando sigan las reglas.
Zgarb
¿Cuál es su sugerencia sobre cómo representar datos de punto flotante en BrainFuck?
flawr
@flawr Para los idiomas que no tienen números nativos de coma flotante, puede usar cualquier codificación conveniente para flotantes entre -10.0 y 10.0 (exclusivo) que tenga como máximo 0.001 de diferencia entre valores consecutivos. La salida debe ser precisa con una diferencia de 0.01 para los casos de prueba.
Zgarb

Respuestas:

1

CJam, 58 bytes

{[\]}:U;
{[@]}:B;
{_,2={~1$-@@-\/0e>1e<}{6Yb@f*\.{O})[_1@-].*:+}?}:O;

Estos son operadores de postfix que funcionan en la pila: 2.0 1.0 3.0 U Ois over(2, uniform(1, 3)).

Cuenta de puntaje

{[\]}es la función en sí, la :U;asigna al nombre Uy la muestra. Esencialmente, esto no es parte de la función, por lo que al contar la regla 2, solo tendría que contar {[\]}. Bse define de manera similar.

Sin embargo, Oes recursivo, y si no especifico un nombre, no hay forma de recurrir. Así que aquí, me inclinaría a contar la :O;parte. Entonces mi puntaje es 5+5+48=58bytes en total.

Explicación

Uestallidos dos argumentos y hace un par en orden inverso: a b => [b a].

Bhace estallar tres argumentos y hace un triple con el fin girada: a b c => [b c a].

OLa estructura es la siguiente:

{             }:O;   Define O as this function:
 _,2=        ?       If the argument list's length is 2:
     {~Γ}            Append the list to the stack and execute subprogram Γ.
         {~Δ}        Else, do the same, but execute subprogram Δ.

El subprograma Γ maneja distribuciones uniformes:

Executed ops      Explanation   Stack contents
============      ===========   ==============
                  Initial       f; b; a
1$                Copy b        f; b; a; b
  -               Difference    f; b; (a-b)
   @@             Rotate x2     (a-b); f, b
     -            Difference    (a-b); (f-b)
      \/          Flip divide   (f-b)/(a-b)
        0e>       Clamp low     max(0, (f-b)/(a-b))
           1e<    Clamp high    min(1, max(0, (f-b)/(a-b)))

El subprograma Δ maneja distribuciones combinadas:

Executed ops              Explanation    Stack contents
============              ===========    ==============
                          Initial        f; [Q R P]
6Yb                       Push [1,1,0]   f; [Q R P]; [1 1 0]
   @                      Rotate         [Q R P]; [1 1 0]; f
    f*                    Multiply each  [Q R P]; [f f 0]
      \                   Swap           [f f 0]; [Q R P]
       .{O}               Pairwise O     [q r p]
           )              Uncons         [q r] p
            [_1@-]        [p, 1-p]       [q r] [p 1-p]
                  .*:+    Dot product    q*p+r*(1-p)
Lynn
fuente
2

Ruby, 103

u=b=->*a{a}
o=->f,d{d[2]?(p=o[0,d[0]])*o[f,d[1]]+(1-p)*o[f,d[2]]:(f<a=d[0])?1:(f>b=d[1])?0:(b-f)/(b-a)}

Define tres lambdas, u, b, y o. uy bsimplemente cree matrices de dos y tres elementos respectivamente. oasume que una matriz de dos elementos es una distribución uniforme y una de tres elementos es una combinación de tres distribuciones. En este último caso se llama a sí mismo recursivamente.

histocrat
fuente
2

MATLAB, 73

Tiempo para una pequeña "programación funcional" en MATLAB. Estas son 3 funciones anónimas. Uniforme y mezcla se llaman de la misma manera que los ejemplos, pero overlos argumentos deben intercambiarse. Realmente no necesito una overdesde las dos primeras funciones de retorno, pero como formalidad fevales una función que puede llamar a una función.

%uniform
@(a,b)@(x)(x<b)*min(1,(b-x)/(b-a))
%blend
@(P,Q,R)@(x)P(0)*(Q(x)-R(x))+R(x)
%over
@feval

Ahora el sistema de análisis y evaluación de MATLAB es un poco inestable por decir lo menos. No le permite llamar directamente a una función que fue devuelta por una función. En cambio, primero se debe guardar el resultado en una variable. El cuarto ejemplo podría hacerse de la siguiente manera:

x=uniform(-5.233,3.384);y=uniform(2.767,8.329);z=uniform(-2.769,6.497);over(blend(x,y,z),0.738)

Sin embargo, es posible evitar esto utilizando fevalpara llamar a todas las funciones. Si se utilizan las siguientes definiciones, los ejemplos se pueden evaluar exactamente como se escriben.

uniform=@(a,b)@(x)(x<b)*min(1,(b-x)/(b-a))
blend=@(P,Q,R)@(x)feval(P,0)*(feval(Q,x)-feval(R,x))+feval(R,x)
over=@(x,f)feval(f,x)
Feersum
fuente
Funciones haciendo funciones ... ¡ qué perverso!
Luis Mendo
1

Mathematica, 129116 bytes

u=UniformDistribution@{##}&;b=If[x<0,z,y]~TransformedDistribution~{x\uF3D2#,y\uF3D2#2,z\uF3D2#3}&;o=Probability[x>=#,x\uF3D2#2]&

u, by oare uniform, blendy overrespectivamente. Envolver sobre las funciones estándar. Reemplace la \uF3D2s con el carácter de 3 bytes. Solo regresa 0y 1para los casos 1, 4 y 7.

LegionMammal978
fuente
1

Python, 146 bytes

u=lambda*a:a
b=u
x=lambda f,a,b:[int(f<=a),(b-f)/(b-a)][a<f<b]
y=lambda f,p,q,r:o(0,p)*o(f,q)+(1-o(0,p))*o(f,r)
o=lambda f,p:[x,y][len(p)-2](f,*p)

La misma estrategia que la respuesta de Ruby del histocrático, pero en Python. Para hacer recursión sin un combinador Z (lo que sería costoso), xy yse definen como funciones auxiliares que evalúan overtuplas de argumentos de 2 y 3 longitudes ( uniformy blendargumentos, respectivamente).

Casos de prueba sobre ideona

Mego
fuente
0

Matlab, 104 bytes

Espero que esto siga siendo válido, ya que esto solo funciona para distribuciones con soporte en [-10,10], que es el requisito para los idiomas que no tienen soporte de punto flotante. El vector de soporte y la precisión se pueden ajustar fácilmente simplemente alterando los números correspondientes. u,o,bes para uniform,blend,over. El pdf solo se representa como un vector discreto. Creo que este enfoque se puede transferir fácilmente a otros idiomas.

D=1e-4;X=-10:D:10;
u=@(a,b)(1/(b-a))*(a<X&X<b);
o=@(x,d)sum(d.*(X>x))*D;
b=@(p,q,r)o(0,p).*q+(1-o(0,p)).*r;

Puede probarlos si define esas funciones primero y luego simplemente pegue este código:

[o(4.356, u(-4.873, 2.441)) , 0.0;
o(2.226, u(-1.922, 2.664)) , 0.09550806803314438;
o(-4.353, u(-7.929, -0.823)) , 0.49676329862088375;
o(-2.491, u(-0.340, 6.453)) , 1.0;
o(0.738, b(u(-5.233, 3.384), u(2.767, 8.329), u(-2.769, 6.497))) , 0.7701533851999125;
o(-3.577, b(u(-3.159, 0.070), b(b(u(-4.996, 4.851), u(-7.516, 1.455), u(-0.931, 7.292)), b(u(-5.437, -0.738), u(-8.272, -2.316), u(-3.225, 1.201)), u(3.097, 6.792)), u(-8.215, 0.817))) , 0.4976245638164541;
o(3.243, b(b(u(-4.909, 2.003), u(-4.158, 4.622), b(u(0.572, 5.874), u(-0.573, 4.716), b(u(-5.279, 3.702), u(-6.564, 1.373), u(-6.585, 2.802)))), u(-3.148, 2.015), b(u(-6.235, -5.629), u(-4.647, -1.056), u(-0.384, 2.050)))) , 0.0;
o(-3.020, b(b(u(-0.080, 6.148), b(u(1.691, 6.439), u(-7.086, 2.158), u(3.423, 6.773)), u(-1.780, 2.381)), b(u(-1.754, 1.943), u(-0.046, 6.327), b(u(-6.667, 2.543), u(0.656, 7.903), b(u(-8.673, 3.639), u(-7.606, 1.435), u(-5.138, -2.409)))), u(-8.008, -0.317))) , 0.4487803553043079]
falla
fuente
Matlab tiene soporte de FP, por lo que creo que esto no sería válido.
LegionMammal978
Dudo en permitir esto, ya que Matlab admite números de coma flotante de forma nativa. Si puede reemplazar Xy Dcon MIN_FLOATy MAX_FLOAT(o como los llame Matlab), entonces este es un enfoque válido.
Zgarb
Sí, podría usar realmax/ realmin, incluso podría hacer un vector que atraviese todos los números de coma flotante si tiene suficiente memoria.
flawr