Implementare un'API per le distribuzioni di probabilità


9

introduzione

In questa sfida, il tuo compito è implementare una raccolta di semplici funzioni che insieme formano una mini-libreria utilizzabile per semplici distribuzioni di probabilità. Per soddisfare alcune delle lingue più esoteriche che le persone amano usare qui, sono accettabili le seguenti implementazioni:

  1. Uno snippet di codice che definisce una raccolta di funzioni denominate (o equivalenti più vicini).
  2. Una raccolta di espressioni che valutano funzioni con nome o anonime (o equivalenti più vicini).
  3. Una singola espressione che valuta diverse funzioni con nome o anonime (o equivalenti più vicini).
  4. Una raccolta di programmi indipendenti che accettano input dalla riga di comando, STDIN o equivalente più vicino e l'output a STDOUT o equivalente più vicino.

Le funzioni

Dovrai implementare le seguenti funzioni, usando nomi più corti se lo desideri.

  1. uniformprende come input due numeri in virgola mobile ae b, e restituisce la distribuzione uniforme [a,b]. Puoi presumere che a < b; il caso a ≥ bnon è definito.
  2. blendprende come ingressi tre distribuzioni di probabilità P, Qe R. Esso restituisce una distribuzione di probabilità S, che attira i valori x, ye zda P, Qe R, rispettivamente, e le rese y, se x ≥ 0e zse x < 0.
  3. overaccetta come input un numero in virgola mobile fe una distribuzione di probabilità Pe restituisce la probabilità che x ≥ fvale per un numero casuale xestratto da P.

Per riferimento, overpuò essere definito come segue (in pseudocodice):

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)

Si può presumere che tutte le distribuzioni di probabilità fornite oversiano costruite usando uniforme blend, e che l'unica cosa che un utente sta per fare con una distribuzione di probabilità sia alimentare blendo over. È possibile utilizzare qualsiasi tipo di dati conveniente per rappresentare le distribuzioni: elenchi di numeri, stringhe, oggetti personalizzati, ecc. L'unica cosa importante è che l'API funzioni correttamente. Inoltre, l'implementazione deve essere deterministica, nel senso di restituire sempre lo stesso output per gli stessi input.

Casi test

I valori di output devono essere corretti per almeno due cifre dopo il punto decimale in questi casi di test.

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

2
Possiamo usare le funzioni integrate per realizzarle?
Mutador,

@ AndréMuta Ho dimenticato che probabilmente Mathematica ha dei built-in per tutto questo ... ma li permetterò, purché seguano le regole.
Zgarb,

Qual è il tuo suggerimento su come rappresentare i dati in virgola mobile in BrainFuck?
Flawr,

@flawr Per le lingue che non hanno numeri in virgola mobile nativi, è possibile utilizzare qualsiasi codifica conveniente per float tra -10,0 e 10,0 (esclusivo) che presenta al massimo una differenza di 0,001 tra valori consecutivi. L'output deve essere accurato entro una differenza di 0,01 per i casi di test.
Zgarb,

Risposte:


1

CJam, 58 byte

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

Questi sono operatori postfix che lavorano nello stack: 2.0 1.0 3.0 U Oè over(2, uniform(1, 3)).

Conteggio dei punteggi

{[\]}è la funzione stessa, la :U;assegna al nome Ue la apre. Essenzialmente questo non fa parte della funzione, quindi contando il punteggio 2, dovrei solo contare {[\]}. Bè definito in modo simile.

Tuttavia, Oè ricorsivo e se non specifico un nome, non c'è modo di ricorrere. Quindi qui, sarei propenso a contare la :O;parte. Quindi il mio punteggio è di 5+5+48=58byte in totale.

Spiegazione

Uapre due argomenti e fa una coppia in ordine inverso: a b => [b a].

Bapre tre argomenti e fa un triplo in ordine ruotato: a b c => [b c a].

OLa struttura è la seguente:

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

Il sottoprogramma Γ gestisce distribuzioni uniformi:

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

Il sottoprogramma Δ gestisce le distribuzioni miste:

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)

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)}

Definisce tre lambda, u, b, e o. ue bbasta creare matrici a due e tre elementi rispettivamente. opresuppone che una matrice a due elementi sia una distribuzione uniforme e una a tre elementi sia una miscela di tre distribuzioni. In quest'ultimo caso si chiama ricorsivamente.


2

MATLAB, 73

Tempo per un po 'di "programmazione funzionale" in MATLAB. Queste sono 3 funzioni anonime. Uniforme e fusione sono chiamati allo stesso modo degli esempi, ma per overgli argomenti devono essere scambiati. Non ho davvero bisogno di una overfin dalle prime due funzioni di ritorno, ma come una formalità fevalè una funzione che può chiamare una funzione.

%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

Ora il sistema di analisi e valutazione di MATLAB è un po 'traballante per non dire altro. Non consente di chiamare direttamente una funzione che è stata restituita da una funzione. Invece, si deve prima salvare il risultato in una variabile. Il quarto esempio potrebbe essere fatto come segue:

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)

Tuttavia, è possibile aggirare il problema utilizzando fevalper chiamare tutte le funzioni. Se vengono utilizzate le seguenti definizioni, gli esempi possono essere valutati esattamente come sono scritti.

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)

Funzioni che creano funzioni ... che perversi!
Luis Mendo,

1

Mathematica, 129 116 byte

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 osono uniform, blende overrispettivamente. Involucro delle funzioni standard. Sostituisci la \uF3D2s con il carattere a 3 byte. Restituisce solo 0e 1per i casi 1, 4 e 7.


1

Python, 146 byte

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)

Stessa strategia della risposta di Ruby dell'istocrate, ma in Python. Effettuare la ricorsione senza un combinatore Z (che sarebbe costoso) xe ysono definiti come funzioni di supporto che valutano le overtuple degli argomenti di lunghezza 2 e 3 ( uniforme blendargomenti, rispettivamente).

Casi di prova su ideone


0

Matlab, 104 byte

Spero che sia ancora valido, poiché funziona solo per le distribuzioni con supporto in [-10,10] che è il requisito per le lingue che non hanno supporto in virgola mobile. Il vettore di supporto e la precisione possono essere facilmente regolati semplicemente modificando i numeri corrispondenti. u,o,bè per uniform,blend,over. Il pdf è appena rappresentato come un vettore discreto. Penso che questo approccio possa essere facilmente trasferito in altre lingue.

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;

Puoi testarli se definisci prima quelle funzioni e poi incolla questo codice:

[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]

Matlab ha il supporto FP, quindi penso che questo non sarebbe valido.
LegionMammal978,

Sono titubante nel permetterlo, dal momento che Matlab supporta nativamente i numeri in virgola mobile. Se puoi sostituire Xe Dcon MIN_FLOATe MAX_FLOAT(o come li chiama Matlab), allora questo è un approccio valido.
Zgarb,

Sì, potresti usare realmax/ realmin, potresti persino creare un vettore che passi attraverso tutti i numeri in virgola mobile se hai memoria sufficiente.
Flawr,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.