Implementar uma API para distribuições de probabilidade

9

Introdução

Nesse desafio, sua tarefa é implementar uma coleção de funções simples que, juntas, formam uma mini-biblioteca utilizável para distribuições simples de probabilidade. Para acomodar algumas das línguas mais esotéricas que as pessoas gostam de usar aqui, as seguintes implementações são aceitáveis:

  1. Um trecho de código que define uma coleção de funções nomeadas (ou equivalentes mais próximos).
  2. Uma coleção de expressões que avaliam funções nomeadas ou anônimas (ou equivalentes mais próximos).
  3. Uma única expressão que avalia várias funções nomeadas ou anônimas (ou equivalentes mais próximos).
  4. Uma coleção de programas independentes que recebem entradas da linha de comando, STDIN ou equivalente mais próximo e são enviadas para STDOUT ou equivalente mais próximo.

As funções

Você deve implementar as seguintes funções, usando nomes mais curtos, se desejar.

  1. uniformtoma como entrada dois números de ponto flutuante ae b, e retorna a distribuição uniforme no [a,b]. Você pode assumir isso a < b; o caso a ≥ bé indefinido.
  2. blendtoma como entradas três distribuições de probabilidade P, Qe R. Retorna uma distribuição de probabilidade S, que desenha valores x, ye zde P, Qe R, respectivamente, e produz yse x ≥ 0e zse x < 0.
  3. overtoma como entrada um número de ponto flutuante fe uma distribuição de probabilidade Pe retorna a probabilidade que x ≥ fvale para um número aleatório xretirado P.

Para referência, overpode ser definido da seguinte forma (em 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)

Você pode assumir que todas as distribuições de probabilidade atribuídas oversão construídas usando uniforme blend, e que a única coisa que um usuário fará com uma distribuição de probabilidade é alimentá-lo com blendou over. Você pode usar qualquer tipo de dados conveniente para representar as distribuições: listas de números, seqüências de caracteres, objetos personalizados etc. A única coisa importante é que a API funcione corretamente. Além disso, sua implementação deve ser determinística, no sentido de sempre retornar a mesma saída para as mesmas entradas.

Casos de teste

Seus valores de saída devem estar corretos para pelo menos dois dígitos após o ponto decimal nesses casos de teste.

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
fonte
2
Podemos usar funções internas para fazê-las?
Mutador 22/10/2015
@ AndréMuta Eu esqueci que o Mathematica provavelmente tem built-ins para tudo isso ... mas eu vou permitir, desde que eles sigam as regras.
Zgarb 22/10/2015
Qual é a sua sugestão sobre como representar dados de ponto flutuante no BrainFuck?
flawr
@flawr Para idiomas que não possuem números de ponto flutuante nativos, você pode usar qualquer codificação conveniente para flutuadores entre -10,0 e 10,0 (exclusivo) que tenha no máximo 0,001 de diferença entre valores consecutivos. A saída deve ser precisa com uma diferença de 0,01 para os casos de teste.
Zgarb 22/10

Respostas:

1

CJam, 58 bytes

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

Estes são operadores postfix que trabalham na pilha: 2.0 1.0 3.0 U Ois over(2, uniform(1, 3)).

Contagem de pontuação

{[\]}é a própria função, a :U;atribui ao nome Ue a exibe. Essencialmente, isso não faz parte da função; portanto, pela regra de contagem de pontuação 2, eu precisaria apenas contar {[\]}. Bé definido da mesma forma.

No entanto, Oé recursivo e, se eu não especificar um nome, não há como recursar. Então aqui, eu estaria inclinado a contar a :O;parte. Então minha pontuação é de 5+5+48=58bytes no total.

Explicação

Uestala dois argumentos e faz com que um par em ordem inversa: a b => [b a].

Bestala três argumentos e faz um triplo a fim rodada: a b c => [b c a].

Oestrutura é a seguinte:

{             }: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 Δ.

O subprograma Γ lida com distribuições 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)))

O subprograma Δ lida com distribuições 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
fonte
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 três lambdas, u, b, e o. ue bapenas crie matrizes de dois e três elementos, respectivamente. oassume que uma matriz de dois elementos é uma distribuição uniforme e uma de três elementos é uma mistura de três distribuições. No último caso, ele se chama recursivamente.

histocrata
fonte
2

MATLAB, 73

Hora de um pouco de "programação funcional" no MATLAB. Estas são 3 funções anônimas. Uniforme e mistura são chamados da mesma maneira que os exemplos, mas overos argumentos devem ser trocados. Eu realmente não preciso de uma overdesde as duas primeiras funções de retorno, mas como formalidade fevalé uma função que pode chamar uma função.

%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

Agora, o sistema de análise e avaliação do MATLAB é um pouco instável, para dizer o mínimo. Não permite que você chame diretamente uma função retornada de uma função. Em vez disso, é necessário primeiro salvar o resultado em uma variável. O quarto exemplo pode ser feito da seguinte maneira:

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)

No entanto, é possível contornar isso usando fevalpara chamar todas as funções. Se as seguintes definições forem usadas, os exemplos poderão ser avaliados exatamente como estão escritos.

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
fonte
Funções fazendo funções ... que perverso!
Luis Mendo
1

Mathematica, 129 116 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, be osão uniform, blende overrespectivamente.Wrapper sobre as funções padrão. Substitua os \uF3D2pelo caractere de 3 bytes. Apenas retorna 0e 1para os casos 1, 4 e 7.

LegionMammal978
fonte
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)

Mesma estratégia que a resposta Ruby do histocrata, mas em Python. Fazer recursão sem um combinador Z (o que seria caro) xe ysão definidas como funções auxiliares que avaliam as overtuplas de argumento de 2 e 3 comprimentos ( uniforme blendargumentos, respectivamente).

Casos de teste em ideone

Mego
fonte
0

Matlab, 104 bytes

Espero que isso ainda seja válido, pois isso só funciona para distribuições com suporte em [-10,10], que é o requisito para idiomas que não têm suporte a ponto flutuante. O vetor de suporte e a precisão podem ser facilmente ajustados apenas alterando os números correspondentes. u,o,bé para uniform,blend,over. O pdf é apenas representado como um vetor discreto. Eu acho que essa abordagem pode ser facilmente transferida para outros 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;

Você pode testá-los se definir essas funções primeiro e depois colar 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]
flawr
fonte
O Matlab tem suporte a FP, então acho que isso seria inválido.
usar o seguinte
Eu hesito em permitir isso, já que o Matlab suporta números de ponto flutuante nativamente. Se você pode substituir Xe Dpor MIN_FLOATe MAX_FLOAT(ou como o Matlab os chama), essa é uma abordagem válida.
Zgarb 23/10/2015
Sim, você pode remover realmax/ realmin, você pode até criar um vetor que atravessa todo o número de ponto flutuante, se tiver memória suficiente.
flawr