Calcola la media media di due numeri


41

disclaimer: la media media è inventata da me

Definire la media aritmetica di n numeri come

M1(x1,...,Xn)=X1+X2+...+Xnn
Definire la media geometrica dinnumeri come
M0(x1,...,xn)=x1x2...xnn
Definire la media armonica dinnumeri come
M1(x1,...,xn)=n1x2+1x2+...+1xn
Definire media quadratica dinnumeri come
M2(x1,...,xn)=x12+x22+...+xn2n
The Mean medio (MM) è definito come segue: Definire quattro sequenze (ak,bk,ck,dk) come
a0=M1(x1,...,xn),b0=M0(x1,...,xn),c0=M1(x1,...,xn),d0=M2(x1,...,xn),ak+1=M1(ak,bk,ck,dk),bk+1=M0(ak,bk,ck,dk),ck+1=M1(ak,bk,ck,dk),dk+1=M2(ak,bk,ck,dk)
Tutte le quattro sequenze convergono allo stesso numero,MM(x1,x2,...,xn) .

Esempio

La media media di 1 e 2 è calcolato come segue: iniziare con

a0=(1+2)/2=1.5,b0=12=21.4142,c0=211+12=431.3333,d0=12+222=521.5811.
Quindi
un'1=1.5+1.4142+1.3333+1,581141.4571,B1=1.5*1.4142*1.3333*1,581141.4542,c1=411.5+11.4142+11.3333+11,58111.4512,d1=1.52+1.41422+1.33332+1,5811241,4601.
L'ulteriore calcolo delle sequenze dovrebbe essere chiaro. Si può vedere che convergono allo stesso numero, circa1.45568889.

Sfida

Dati due numeri reali positivi, un' e B ( un'<B ), calcolare la loro media media MM(un',B) .

Casi test

1 1 => 1
1 2 => 1.45568889
100 200 => 145.568889
2.71 3.14 => 2.92103713
0.57 1.78 => 1.0848205
1.61 2.41 => 1.98965438
0.01 100 => 6.7483058

Gli appunti

  • Il programma è valido se la differenza tra il suo output e il corretto output non è maggiore di 1/100000 del valore assoluto della differenza tra i numeri di input.
  • L'output dovrebbe essere un singolo numero.

Questo è , quindi vince il codice più corto!




11
Quanto dovremmo essere precisi?
Incarnazione dell'ignoranza il


1
Possiamo supporre che il primo input sia sempre più piccolo del secondo, come in tutti i casi di test? (In caso contrario, eseguirò il rollback della mia risposta Java.)
Kevin Cruijssen il

Risposte:


14

Wolfram Language (Mathematica) , 52 byte

#//.x_:>N@{M@x,E^M@Log@x,1/M[1/x],M[x^2]^.5}&
M=Mean

Provalo online!

Nel mio primo approccio ho usato questi builtin
Mean GeometricMean HarmonicMean eRootMeanSquare

Ecco alcune sostituzioni per il salvataggio di byte

HarmonicMean-> 1/Mean[1/x] di @Robin Ryder (3 byte salvati)
GeometricMean-> E^Mean@Log@xdi @A. Rex (2 byte salvati)
RootMeanSquare -> Mean[x^2]^.5di @A. Rex (4 byte salvati)

infine possiamo assegnare Meana M(come proposto dalla @ovs) e risparmiare più 5 byte


Salva 2 byte ricodificando GeometricMean
Robin Ryder il

@RobinRyder Credo che intendi Harmonic .. bello!
J42161217

1
Salva altri 8 byte :#//.x_:>N@{Mean@x,E^Mean@Log@x,1/Mean[1/x],Mean[x^2]^.5}&
A. Rex

@ovs modificato .....
J42161217

10

R, 70 69 67 byte

x=scan();`?`=mean;while(x-?x)x=c((?x^2)^.5,?x,2^?log2(x),1/?1/x);?x

Provalo online!

-1 byte con un migliore condizionamento.
-2 byte passando alla base 2.

Come alcune altre risposte, utilizza l'espressione della media geometrica come media aritmetica sulla scala del log (qui in base 2):

M0(X1,...,Xn)=2M1(log2X1,...,log2Xn).

Utilizza anche il fatto che K,dKun'KBKcK , ovvero dK=max(un'K,BK,cK,dK) . La condizione un'K=BK=cK=dK è quindi equivalente a d k = M 1 ( a k , b k , c k , d k )dK=M1(un'K,BK,cK,dK), che è quello che uso nel ciclo while; ciò si ottiene abusando della sintassi di whilecui considera il primo elemento solo quando la condizione è un vettore, quindi l'ordine in cui sono memorizzati i mezzi. (Si noti che potremmo anche usare cK invece poiché è il minimo dei quattro, ma non abbiamo potuto usare un'K o BK nella condizione.)

Quando usciamo dal ciclo while, xè un vettore costante. Il finale ?xcalcola la sua media per ridurlo a uno scalare.


1
Non dovrebbe essere invece di l o g x n ? lnXnlogXn
Tau

@Tau Sì, stavo denotando il logaritmo naturale con , che è l'impostazione predefinita in R. Comunque, ora l'ho cambiato in logaritmo di base 2 per -2 byte. log
Robin Ryder

6

J , 34 byte

(31 come espressione senza assegnazione alla variabile f)

f=:1{(^.z,%z,*:z,[z=:(+/%#)&.:)^:_

Provalo online!

Per le funzioni ae b, a &.: b("a under b" ( sfida correlata )) equivale a (b inv) a b: applicare b, quindi a, quindi inverso di b. In questo caso, la media geometrica / armonica / quadratica è la media aritmetica "sotto" logaritmo, inversione e quadrato rispettivamente.


5

TI-BASIC, 42 35 34 byte

-1 byte grazie a @SolomonUcko

While max(ΔList(Ans:{mean(Ans),√(mean(Ans²)),mean(Ans^-1)^-1,e^(mean(ln(Ans:End:Ans(1

L'input è un elenco di due numeri interi in Ans.
L'output viene archiviato Anse stampato automaticamente al termine del programma.

Le formule usate per mezzi geometrici, armonici e quadratici si basano sulla spiegazione di user202729 .

Esempio:

{1,2
           {1 2}
prgmCDGFB
     1.455688891
{100,200
       {100 200}
prgmCDGFB
     145.5688891

Spiegazione:
(Newline sono stati aggiunti per chiarimenti. NON compaiono nel codice.)

While max(ΔList(Ans           ;loop until all elements of the current list are equal
                              ; the maximum of the change in each element will be 0
{                             ;create a list containing...
 mean(Ans),                   ; the arithmetic mean
 √(mean(Ans²)),               ; the quadratic mean
 mean(Ans^-1)^-1,             ; the harmonic mean
 e^(mean(ln(Ans               ; and the geometric mean
End
Ans(1                         ;keep the first element in "Ans" and implicitly print it

Gli appunti:

TI-BASIC è un linguaggio tokenizzato. Il conteggio dei caratteri non equivale al conteggio dei byte.

e^(è questo token a un byte.

^-1viene utilizzato per questo token a un byte.
Ho optato ^-1invece per la scrittura perché il token è simile a ֿ¹quando si trova in un blocco di codice.

√(è questo token a un byte.

ΔList(è questo token a due byte.


Penso che puoi salvare una parentesi mettendo l'ultima media geometrica.
Solomon Ucko

@ SalomonUcko ah, grazie per averlo notato! Non l'ho considerato prima.
Tau,

max(DeltaList(Ans-> variance(Ans.
Lirtosiast

5

Java 10, 234 229 214 211 215 206 203 196 180 177 byte

a->{for(;a[1]-a[0]>4e-9;){double l=a.length,A[]={0,0,0,1};for(var d:a){A[2]+=d/l;A[3]*=Math.pow(d,1/l);A[0]+=1/d;A[1]+=d*d;}A[0]=l/A[0];A[1]=Math.sqrt(A[1]/l);a=A;}return a[0];}

-5 byte grazie a @PeterCordes .
-15 byte in più grazie a @PeterCordes , ispirato alla risposta R di @RobinRyder .
+4 byte perché ho ipotizzato che gli input siano preordinati.
-27 byte grazie a @ OlivierGrégoire .

Provalo online.

Spiegazione:

a->{                        // Method with double-array parameter and double return-type
  for(;a[1]-a[0]            //  Loop as long as the difference between the 2nd and 1st items
                >4e-9;){    //  is larger than 0.000000004:
    double l=a.length,      //   Set `l` to the amount of values in the array `a`
           A[]={0,0,0,1};   //   Create an array `A`, filled with the values [0,0,0,1]
    for(var d:a){           //   Inner loop over the values of `a`:
      A[2]+=d/l;            //    Calculate the sum divided by the length in the third spot
      A[3]*=Math.pow(d,1/l);//    The product of the power of 1/length in the fourth spot
      A[0]+=1/d;            //    The sum of 1/value in the first spot
      A[1]+=d*d;            //    And the sum of squares in the second spot
    }                       //   After the inner loop:
                            //   (the third spot of the array now holds the Arithmetic Mean)
                            //   (the fourth spot of the array now holds the Geometric Mean)
    A[0]=l/A[0];            //   Divide the length by the first spot
                            //   (this first spot of the array now holds the Harmonic Mean)
    A[1]=Math.sqrt(A[1]/l); //   Take the square of the second spot divided by the length
                            //   (this second spot of the array now holds the Quadratic Mean)
    a=A;                    //   And then replace input `a` with array `A`
  }                         //  After the outer loop when all values are approximately equal:
  return a[0];}             //  Return the value in the first spot as result

In C potresti f+=Math.abs(d-D)<1e-9;ottenere una conversione implicita da un risultato di confronto booleano in un numero intero 0/1 e quindi double. Java ha qualche sintassi compatta per questo? Oppure è possibile fare f+=Math.abs(d-D)e quindi verificare che la somma delle differenze assolute sia abbastanza piccola ?
Peter Cordes,

1
Sì, per i tuoi casi di test, f>1e-8funziona come una condizione di loop: 229 byte. a->{for(double f=1,D,A[],l;f>1e-8;a=A){D=a[0];A=new double[]{f=0,1,0,0};for(var d:a){f+=Math.abs(d-D);A[0]+=d;A[1]*=d;A[2]+=1/d;A[3]+=d*d;}A[0]/=l=a.length;A[1]=Math.pow(A[1],1/l);A[2]=l/A[2];A[3]=Math.sqrt(A[3]/l);}return a[0];}. Con 1e-9, gira più lentamente (circa il doppio del tempo della CPU), dovendo fare più iterazioni per d-Dridurre sostanzialmente di 4 * . Con 1e-7, ha circa la stessa velocità di 1e-8. Con 1e-6, alcune delle cifre finali differiscono per un caso.
Peter Cordes,

1
@La risposta di RobinRyder sottolinea che la media quadratica è sempre la più grande e l'armonica sempre la più piccola, quindi forse puoi abbandonare del ftutto e solo controllare a[3]-a[2]<4e-9.
Peter Cordes,

1
@PeterCordes l==2||intendi (golfed to l<3|). Ma sì, buon punto; L'ho aggiunto. :)
Kevin Cruijssen il

2
180 byte aggregando i riduttori aggregabili.
Olivier Grégoire il

3

Carbone , 40 byte

W‹⌊θ⌈θ≔⟦∕ΣθLθXΠθ∕¹Lθ∕LθΣ∕¹θ₂∕ΣXθ²Lθ⟧θI⊟θ

Provalo online! Il collegamento è alla versione dettagliata del codice. Accetta input come una matrice di numeri. Spiegazione:

W‹⌊θ⌈θ

Ripeti mentre l'array contiene valori diversi ...

≔⟦....⟧θ

... sostituisci l'array con un elenco di valori:

∕ΣθLθ

... il cattivo ...

XΠθ∕¹Lθ

... la media geometrica ...

∕LθΣ∕¹θ

... il significato armonico ...

₂∕ΣXθ²Lθ

... e la radice significa quadrato.

I⊟θ

Trasmetti un elemento dell'array in stringa e lo stampa implicitamente.




3

05AB1E , 26 24 23 byte

Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н

Provalo online o vedi i passaggi di tutti i casi di test .

-1 byte grazie a @Grimy .

Alternativa a 23 byte per Media geometrica:

Δ©P®gzm®ÅA®zÅAz®nÅAt)}н

Provalo online o vedi i passaggi di tutti i casi di test .

Spiegazione:

Δ         # Loop until the list no longer changes:
 ©        #  Store the current list in variable `®` (without popping)
          #  (which is the implicit input-list in the first iteration)
          #  Arithmetic mean:
  ÅA      #   Builtin to calculate the arithmetic mean of the list
          #  Geometric mean:
  ®.²     #   Take the base-2 logarithm of each value in the list `®`
     ÅA   #   Get the arithmetic mean of that list
       o  #   And take 2 to the power of this mean
          #  Harmonic mean:
  ®z      #   Get 1/x for each value x in the list `®`
    ÅA    #   Get the arithmetic mean of that list
      z   #   And calculate 1/y for this mean y
          #  Quadratic mean:
  ®n      #   Take the square of each number x in the list from the register
    ÅA    #   Calculate the arithmetic mean of this list
      t   #   And take the square-root of that mean
  )       #  Wrap all four results into a list
        # After the list no longer changes: pop and push its first value
          # (which is output implicitly as result)

23:Δ©P®gzm®ÅA®zÅAz®nÅAt)}н
Grimmy,

@Grimy Grazie! Non riesco a credere di non aver pensato di usare la lunghezza invece che Yper 2/4. :)
Kevin Cruijssen il

1
Altri 23 che migliora mostra la somiglianza di media geometrica per gli altri: Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н. Sfortunatamente, non sembra che possiamo riformattare tutti quei messaggi ÅA.
Grimmy,

@Grimy Oh, mi piace questa seconda versione. :) EDIT: Oops .. grazie per aver notato il mio errore nella spiegazione ..>.>
Kevin Cruijssen

Non programma molto bene in 05ab1e, ma puoi calcolare somme e poi dividerle tutte per la lunghezza in seguito?
mio pronome è monicareinstate il

2

Gelatina , 25 24 byte

Wẋ4¹ÆlÆeƭ²½ƭİ4ƭÆm$€⁺µÐLḢ

Provalo online!

Spiegazione

                    µÐL | Repeat until unchanged:
W                       |   Wrap as a list
 ẋ4                     |   Copy list 4 times
                   ⁺    |   Do twice:
                 $€     |     For each copy of the list:
             4ƭ         |     One of these functions, cycling between them:
   ¹                    |       Identity
    ÆlÆeƭ               |       Alternate between log and exp
         ²½ƭ            |       Alternate between square and square root
            İ           |       Reciprocal
               Æm       |    Then take the mean
                       Ḣ| Finally take the first item

Sono abbastanza cattivo con Jelly, ma potrebbe significare qualcosa di simile a P*İLlavorare per il significato geometrico?
mio pronome è monicareinstate il

@someone dovrebbe essere P*Lİ$così non risparmierà i byte. Significherebbe che potrei riportare Æmindietro una linea senza costare byte, ma mi piace molto il fatto che ognuno abbia attualmente una media aritmetica al suo centro.
Nick Kennedy,

2

Python 3 , 152 byte

from math import*
s=sum
def f(*a):l=len(a);return 2>len({*a})and{*a}or f(s(a)/l,l/s(map(pow,a,l*[-1])),exp(s(map(log,a))/l),(s(map(pow,a,l*[2]))/l)**.5)

Provalo online!

La funzione ricorsiva fconverge in precisione a virgola mobile. Funziona in linea di principio per tutti gli elenchi di numeri positivi di qualsiasi dimensione, ma è limitato dalla ricorsione di Python che limita un errore di arrotondamento per alcuni casi di test.


In alternativa, accontentandosi di una precisione di 9 decimali:

Python 3 , 169 byte

from math import*
s=sum
def f(*a):l=len(a);return(2>len({round(i,9)for i in a}))*a[0]or f(s(a)/l,l/s(map(pow,a,l*[-1])),exp(s(map(log,a))/l),(s(map(pow,a,l*[2]))/l)**.5)

Provalo online!


1

C # , 173 byte

double m(int n,params double[]a)=>(n<1?a[0]:m(n-1,a.Sum()/a.Length,Math.Pow(a.Aggregate((t,x)=>t*x),1.0/a.Length),a.Length/a.Sum(x=>1/x),Math.Sqrt(a.Sum(x=>x*x)/a.Length)));

Provalo online!


2
Questo sembra davvero su una variabile che deve essere passata. Inoltre, è necessario includere using Systeme using System.Linqnel conteggio dei byte, poiché sono necessari per l'esecuzione del programma. È possibile modificare il compilatore nel compilatore interattivo Visual C #, che non necessita di tali importazioni. Inoltre, 1.0->1d
Incarnazione dell'ignoranza il

1

Pulito , 124 byte

import StdEnv
f=avg o limit o iterate\l=let n=toReal(length l)in[avg l,prod l^(1.0/n),n/sum[1.0/x\\x<-l],avg[x*x\\x<-l]^0.5]

Provalo online!

Esegue l'operazione fino a quando il risultato non smette di cambiare.

Evviva per virgola mobile di precisione limitata!


1

Pyth, 32 byte

h.Wt{H[.OZ@*FZJlZcJscL1Z@.O^R2Z2

Provalo online qui o verifica tutti i casi di test (barra due, vedi nota sotto) contemporaneamente qui . Accetta input come elenco.

Sembrano esserci alcuni problemi con l'arrotondamento, poiché alcuni input non convergono correttamente quando altrimenti dovrebbero. In particolare, il caso di test 0.01 100rimane bloccato sui valori [6.748305820749738, 6.748305820749738, 6.748305820749739, 6.748305820749738]e il caso di test 1.61 2.41rimane bloccato [1.9896543776640825, 1.9896543776640825, 1.9896543776640827, 1.9896543776640825]- in entrambi i casi si nota che la terza media (media armonica) differisce dalle altre.

Non sono sicuro che questo problema invalidi la mia iscrizione, ma la sto pubblicando comunque come dovrebbe funzionare. Se ciò non è accettabile, può essere corretto inserendo .RRTprima del [, per arrotondare ciascuno dei mezzi a 10 decimali, come si vede in questa suite di test .

h.Wt{H[.OZ@*FZJlZcJscL1Z@.O^R2Z2)Q   Implicit: Q=eval(input())
                                     Trailing )Q inferred
 .W                              Q   Funcitonal while: While condition is true, call inner. Starting value Q
   t{H                               Condition function: current input H
    {H                                 Deduplicate H
   t                                   Discard first value
                                         Empty list is falsey, so while is terminated when means converge
      [.OZ@*FZJlZcJscL1Z@.O^R2Z2)    Inner function: current input Z
              JlZ                      Take length of Z, store in J
       .OZ                             (1) Arithmetic mean of Z
           *FZ                         Product of Z
          @   J                        (2) Jth root of the above
                     L Z               Map each element of Z...
                    c 1                ... to its reciprocal
                   s                   Sum the above
                 cJ                    (3) J / the above
                            R Z        Map each element of Z...
                           ^ 2         ... to its square
                         .O            Arithmetic mean of the above
                        @      2       (4) Square root of the above
      [                         )      Wrap results (1), (2), (3), and (4) in a list
                                         This is used as the input for the next iteration of the loop
h                                    Take the first element of the result, implicit print

Poiché sono abbastanza sicuro che il calcolo ripetuto non salterà ai valori precedenti, potresti sostituirlo .Wt{Hcon u-4 byte (e passare Za G)
ar4093,


1

C # (compilatore interattivo Visual C #) , 177 byte

double f(double[]g)=>g.All(c=>Math.Abs(c-g[0])<1e-9)?g[0]:f(new[]{g.Sum()/(z=g.Length),Math.Pow(g.Aggregate((a,b)=>a*b),1d/z),z/g.Sum(x=>1/x),Math.Sqrt(g.Sum(x=>x*x)/z)});int z;

Grazie a @KevinCruijjsen per aver sottolineato che l'utilizzo della precisione in virgola mobile stava causando problemi! Sarebbe 163 byte se i doppi fossero perfettamente precisi.

Provalo online!


Gli ultimi due casi di test danno una StackOverflowExceptionprecisione in virgola mobile. Invece di c==g[0]te potresti fare qualcosa del genere Math.Abs(c-g[0])<1e-9. Provalo online.
Kevin Cruijssen,

@KevinCruijssen Grazie, è un tale dolore gestire i numeri in virgola mobile
Incarnazione dell'ignoranza

1

codice macchina x86 (float SIMD 4x con SSE1 e AVX a 128 bit) 94 byte

codice macchina x86 (doppio SIMD 4x con AVX a 256 bit) 123 byte

float supera i casi di test nella domanda, ma con una soglia di uscita dell'anello abbastanza piccola da farlo accadere, è facile che si blocchi in un ciclo infinito con input casuali.

Le istruzioni SSE1 a precisione singola impacchettata sono lunghe 3 byte, ma SSE2 e le semplici istruzioni AVX sono lunghe 4 byte. (Le istruzioni singole scalari come sqrtsssono anche lunghe 4 byte, motivo per cui utilizzo sqrtpsanche se mi interessa solo l'elemento basso. Non è nemmeno più lento di sqrtss sull'hardware moderno). Ho usato AVX per destinazioni non distruttive per salvare 2 byte rispetto a movaps + op.
Nella doppia versione possiamo ancora fare un paio movlhpsper copiare blocchi a 64 bit (perché spesso ci preoccupiamo solo dell'elemento basso di una somma orizzontale). Anche la somma orizzontale di un vettore SIMD a 256 bit richiede un extra vextractf128per ottenere la metà alta, rispetto alla strategia 2x lenta ma piccola haddpsper il float . Ildouble versione richiede anche 2x costanti da 8 byte, anziché 2x 4 byte. Complessivamente esce quasi 4/3 della dimensione delfloat versione.

mean(a,b) = mean(a,a,b,b)per tutti questi 4 di questi mezzi , quindi possiamo semplicemente duplicare l'input fino a 4 elementi e non dobbiamo mai implementare length = 2. Quindi possiamo hardcode media geometrica come 4th-root = sqrt (sqrt), per esempio. E abbiamo solo bisogno di una costante FP, 4.0.

Abbiamo un singolo vettore SIMD di tutti e 4 [a_i, b_i, c_i, d_i]. Da ciò, calcoliamo i 4 mezzi come scalari in registri separati e li mescoliamo di nuovo insieme per la successiva iterazione. (Le operazioni orizzontali sui vettori SIMD sono scomode, ma dobbiamo fare la stessa cosa per tutti e 4 gli elementi in casi sufficienti che si bilancino. Ho iniziato con una versione x87 di questo, ma stava diventando molto lungo e non divertente.)

La condizione di uscita ad anello di }while(quadratic - harmonic > 4e-5)(o una costante più piccola per double) si basa sulla risposta R di @ RobinRyder e sulla risposta Java di Kevin Cruijssen : la media quadratica è sempre la grandezza maggiore e la media armonica è sempre la più piccola (ignorando gli errori di arrotondamento). Quindi possiamo controllare il delta tra quei due per rilevare la convergenza. Restituiamo la media aritmetica come risultato scalare. Di solito è tra quei due ed è probabilmente il meno suscettibile agli errori di arrotondamento.

Versione float : richiamabile come float meanmean_float_avx(__m128);con arg e restituisce valore in xmm0. (Quindi x86-64 System V o Windows x64 vectorcall, ma non x64 fastcall.) O dichiarare il tipo restituito in __m128modo da poter ottenere la media quadratica e armonica per il test.

Lasciando questo prendere 2 floatarg separati in xmm0 e xmm1 costerebbe 1 byte extra: avremmo bisogno di un shufpscon un imm8 (invece di solo unpcklps xmm0,xmm0) per mescolare insieme e duplicare 2 input.

    40  address                    align 32
    41          code bytes         global meanmean_float_avx
    42                             meanmean_float_avx:
    43 00000000 B9[52000000]           mov      ecx, .arith_mean      ; allows 2-byte call reg, and a base for loading constants
    44 00000005 C4E2791861FC           vbroadcastss  xmm4, [rcx-4]    ; float 4.0
    45                             
    46                                 ;; mean(a,b) = mean(a,b,a,b) for all 4 types of mean
    47                                 ;; so we only ever have to do the length=4 case
    48 0000000B 0F14C0                 unpcklps xmm0,xmm0          ; [b,a] => [b,b,a,a]
    49                             
    50                                 ; do{ ... } while(quadratic - harmonic > threshold);
    51                             .loop:
    52                             ;;; XMM3 = geometric mean: not based on addition.  (Transform to log would be hard.  AVX512ER has exp with 23-bit accuracy, but not log.  vgetexp = floor(lofg2(x)), so that's no good.)
    53                                 ;; sqrt once *first*, making magnitudes closer to 1.0 to reduce rounding error.  Numbers are all positive so this is safe.
    54                                 ;; both sqrts first was better behaved, I think.
    55 0000000E 0F51D8                 sqrtps   xmm3, xmm0                 ; xmm3 = 4th root(x)
    56 00000011 F30F16EB               movshdup xmm5, xmm3                 ; bring odd elements down to even
    57 00000015 0F59EB                 mulps    xmm5, xmm3
    58 00000018 0F12DD                 movhlps  xmm3, xmm5                 ; high half -> low
    59 0000001B 0F59DD                 mulps    xmm3, xmm5                 ; xmm3[0] = hproduct(sqrt(xmm))
    60                             ;    sqrtps   xmm3, xmm3                 ; sqrt(hprod(sqrt)) = 4th root(hprod)
    61                                 ; common final step done after interleaving with quadratic mean
    62                             
    63                             ;;; XMM2 = quadratic mean = max of the means
    64 0000001E C5F859E8               vmulps   xmm5, xmm0,xmm0
    65 00000022 FFD1                   call     rcx                ; arith mean of squares
    66 00000024 0F14EB                 unpcklps xmm5, xmm3         ; [quad^2, geo^2, ?, ?]
    67 00000027 0F51D5                 sqrtps   xmm2, xmm5         ; [quad,   geo,   ?, ?]
    68                             
    69                             ;;; XMM1 = harmonic mean = min of the means
    70 0000002A C5D85EE8               vdivps   xmm5, xmm4, xmm0    ; 4/x
    71 0000002E FFD1                   call     rcx                ; arithmetic mean (under inversion)
    72 00000030 C5D85ECD               vdivps   xmm1, xmm4, xmm5    ; 4/.  (the factor of 4 cancels out)
    73                             
    74                             ;;; XMM5 = arithmetic mean
    75 00000034 0F28E8                 movaps   xmm5, xmm0
    76 00000037 FFD1                   call     rcx
    77                             
    78 00000039 0F14E9                 unpcklps  xmm5, xmm1           ;     [arith, harm, ?,?]
    79 0000003C C5D014C2               vunpcklps xmm0, xmm5,xmm2      ; x = [arith, harm, quad, geo]
    80                             
    81 00000040 0F5CD1                 subps    xmm2, xmm1        ; largest - smallest mean: guaranteed non-negative
    82 00000043 0F2E51F8               ucomiss  xmm2, [rcx-8]     ; quad-harm > convergence_threshold
    83 00000047 73C5                   jae     .loop
    84                             
    85                                 ; return with the arithmetic mean in the low element of xmm0 = scalar return value
    86 00000049 C3                     ret
    87                             
    88                             ;;; "constant pool" between the main function and the helper, like ARM literal pools
    89 0000004A ACC52738           .fpconst_threshold:   dd 4e-5    ; 4.3e-5 is the highest we can go and still pass the main test cases
    90 0000004E 00008040           .fpconst_4:    dd 4.0
    91                             .arith_mean:               ; returns XMM5 = hsum(xmm5)/4.
    92 00000052 C5D37CED               vhaddps   xmm5, xmm5         ; slow but small
    93 00000056 C5D37CED               vhaddps   xmm5, xmm5
    94 0000005A 0F5EEC                 divps     xmm5, xmm4        ; divide before/after summing doesn't matter mathematically or numerically; divisor is a power of 2
    95 0000005D C3                     ret

    96 0000005E 5E000000           .size:      dd $ - meanmean_float_avx
       0x5e = 94 bytes

(Elenco NASM creato con nasm -felf64 mean-mean.asm -l/dev/stdout | cut -b -34,$((34+6))-. Rimuovi la parte elenco e ripristina l'origine concut -b 34- > mean-mean.asm )

La somma orizzontale SIMD e la divisione per 4 (cioè la media aritmetica) sono implementate in una funzione separata che noi call(con un puntatore a funzione per ammortizzare il costo dell'indirizzo). Con 4/xbefore / after, oppure x^2before e sqrt after, otteniamo la media armonica e la media quadratica. (È stato doloroso scrivere queste divistruzioni invece di moltiplicarle per un valore esattamente rappresentabile0.25 .)

La media geometrica viene implementata separatamente con moltiplicare e concatenare sqrt. O con un sqrt prima per ridurre la grandezza dell'esponente e forse aiutare la precisione numerica. il registro non è disponibile, solo floor(log2(x))tramite AVX512 vgetexpps/pd. Exp è disponibile in genere tramite AVX512ER (solo Xeon Phi), ma con una precisione solo 2 ^ -23.

La combinazione di istruzioni AVX a 128 bit e SSE legacy non è un problema di prestazioni. Il mixaggio di AVX a 256 bit con SSE legacy può essere su Haswell, ma su Skylake crea potenzialmente una potenziale falsa dipendenza per le istruzioni SSE. Penso che la mia doubleversione eviti inutili dep chain portate in loop e colli di bottiglia su latenza / throughput div / sqrt / throughput.

Doppia versione:

   108                             global meanmean_double_avx
   109                             meanmean_double_avx:
   110 00000080 B9[E8000000]           mov      ecx, .arith_mean
   111 00000085 C4E27D1961F8           vbroadcastsd  ymm4, [rcx-8]    ; float 4.0
   112                             
   113                                 ;; mean(a,b) = mean(a,b,a,b) for all 4 types of mean
   114                                 ;; so we only ever have to do the length=4 case
   115 0000008B C4E37D18C001           vinsertf128   ymm0, ymm0, xmm0, 1       ; [b,a] => [b,a,b,a]
   116                             
   117                             .loop:
   118                             ;;; XMM3 = geometric mean: not based on addition.
   119 00000091 C5FD51D8               vsqrtpd      ymm3, ymm0     ; sqrt first to get magnitude closer to 1.0 for better(?) numerical precision
   120 00000095 C4E37D19DD01           vextractf128 xmm5, ymm3, 1           ; extract high lane
   121 0000009B C5D159EB               vmulpd       xmm5, xmm3
   122 0000009F 0F12DD                 movhlps      xmm3, xmm5              ; extract high half
   123 000000A2 F20F59DD               mulsd        xmm3, xmm5              ; xmm3 = hproduct(sqrt(xmm0))
   124                                ; sqrtsd       xmm3, xmm3             ; xmm3 = 4th root = geomean(xmm0)   ;deferred until quadratic
   125                             
   126                             ;;; XMM2 = quadratic mean = max of the means
   127 000000A6 C5FD59E8               vmulpd   ymm5, ymm0,ymm0
   128 000000AA FFD1                   call     rcx                ; arith mean of squares
   129 000000AC 0F16EB                 movlhps  xmm5, xmm3         ; [quad^2, geo^2]
   130 000000AF 660F51D5               sqrtpd   xmm2, xmm5         ; [quad  , geo]
   131                             
   132                             ;;; XMM1 = harmonic mean = min of the means
   133 000000B3 C5DD5EE8               vdivpd   ymm5, ymm4, ymm0    ; 4/x
   134 000000B7 FFD1                   call     rcx                 ; arithmetic mean under inversion
   135 000000B9 C5DB5ECD               vdivsd   xmm1, xmm4, xmm5    ; 4/.  (the factor of 4 cancels out)
   136                             
   137                             ;;; XMM5 = arithmetic mean
   138 000000BD C5FC28E8               vmovaps  ymm5, ymm0
   139 000000C1 FFD1                   call     rcx
   140                             
   141 000000C3 0F16E9                 movlhps     xmm5, xmm1            ;     [arith, harm]
   142 000000C6 C4E35518C201           vinsertf128 ymm0, ymm5, xmm2, 1   ; x = [arith, harm, quad, geo]
   143                             
   144 000000CC C5EB5CD1               vsubsd   xmm2, xmm1               ; largest - smallest mean: guaranteed non-negative
   145 000000D0 660F2E51F0             ucomisd  xmm2, [rcx-16]           ; quad - harm > threshold
   146 000000D5 77BA                   ja      .loop
   147                             
   148                                 ; vzeroupper ; not needed for correctness, only performance
   149                                 ; return with the arithmetic mean in the low element of xmm0 = scalar return value
   150 000000D7 C3                     ret
   151                             
   152                             ; "literal pool" between the function
   153 000000D8 95D626E80B2E113E   .fpconst_threshold:   dq 1e-9
   154 000000E0 0000000000001040   .fpconst_4:    dq 4.0            ; TODO: golf these zeros?  vpbroadcastb and convert?
   155                             .arith_mean:                     ; returns YMM5 = hsum(ymm5)/4.
   156 000000E8 C4E37D19EF01           vextractf128 xmm7, ymm5, 1
   157 000000EE C5D158EF               vaddpd       xmm5, xmm7
   158 000000F2 C5D17CED               vhaddpd      xmm5, xmm5      ; slow but small
   159 000000F6 C5D35EEC               vdivsd     xmm5, xmm4        ; only low element matters
   160 000000FA C3                     ret

   161 000000FB 7B000000           .size:      dd $ - meanmean_double_avx

    0x7b = 123 bytes

Cablaggio di prova

#include <immintrin.h>
#include <stdio.h>
#include <math.h>

static const struct ab_avg {
    double a,b;
    double mean;
} testcases[] = {
    {1, 1, 1},
    {1, 2, 1.45568889},
    {100, 200, 145.568889},
    {2.71, 3.14, 2.92103713},
    {0.57, 1.78, 1.0848205},
    {1.61, 2.41, 1.98965438},
    {0.01, 100, 6.7483058},
};

// see asm comments for order of  arith, harm, quad, geo
__m128 meanmean_float_avx(__m128);       // or float ...
__m256d meanmean_double_avx(__m128d);    // or double ...
int main(void) {
    int len = sizeof(testcases) / sizeof(testcases[0]);
    for(int i=0 ; i<len ; i++) {
        const struct ab_avg *p = &testcases[i];
#if 1
        __m128 arg = _mm_set_ps(0,0, p->b, p->a);
        double res = meanmean_float_avx(arg)[0];
#else
        __m128d arg = _mm_loadu_pd(&p->a);
        double res = meanmean_double_avx(arg)[0];
#endif
        double allowed_diff = (p->b - p->a) / 100000.0;
        double delta = fabs(p->mean - res);
        if (delta > 1e-3 || delta > allowed_diff) {
            printf("%f %f => %.9f but we got %.9f.  delta = %g allowed=%g\n",
                   p->a, p->b, p->mean, res, p->mean - res, allowed_diff);
        }
    }



    while(1) {
        double a = drand48(), b = drand48();  // range= [0..1)
        if (a>b) {
            double tmp=a;
            a=b;
            b=tmp; // sorted
        }
//      a *= 0.00000001;
//      b *= 123156;
        // a += 1<<11;  b += (1<<12)+1;  // float version gets stuck inflooping on 2048.04, 4097.18 at fpthreshold = 4e-5

        // a *= 1<<11 ; b *= 1<<11;   // scaling to large magnitude makes sum of squares loses more precision
        //a += 1<<11; b+= 1<<11;   // adding to large magnitude is hard for everything, catastrophic cancellation
#if 1
        printf("testing float %g, %g\n", a, b);
        __m128 arg = _mm_set_ps(0,0, b, a);
        __m128 res = meanmean_float_avx(arg);
        double quad = res[2], harm = res[1];  // same order as double... for now
#else
        printf("testing double %g, %g\n", a, b);
        __m128d arg = _mm_set_pd(b, a);
        __m256d res = meanmean_double_avx(arg);
        double quad = res[2], harm = res[1];
#endif
        double delta = fabs(quad - harm);
        double allowed_diff = (b - a) / 100000.0; // calculated in double even for the float case.
        // TODO: use the double res as a reference for float res
        // instead of just checking quadratic vs. harmonic mean

        if (delta > 1e-3 || delta > allowed_diff) {
            printf("%g %g we got q=%g, h=%g, a=%g.  delta = %g,  allowed=%g\n",
                   a, b, quad, harm, res[0], quad-harm, allowed_diff);
        }
    }

}

Costruisci con:

nasm -felf64 mean-mean.asm &&
gcc -no-pie -fno-pie -g -O2 -march=native mean-mean.c mean-mean.o

Ovviamente hai bisogno di una CPU con supporto AVX o di un emulatore come Intel SDE. Per compilare su un host senza supporto AVX nativo, utilizzare -march=sandybridgeo-mavx

Esegui: passa i casi di test codificati, ma per la versione float, i casi di test casuali spesso non (b-a)/10000superano la soglia impostata nella domanda.

$ ./a.out
 (note: empty output before the first "testing float" means clean pass on the constant test cases)
testing float 3.90799e-14, 0.000985395
3.90799e-14 0.000985395 we got q=3.20062e-10, h=3.58723e-05, a=2.50934e-05.  delta = -3.5872e-05,  allowed=9.85395e-09
testing float 0.041631, 0.176643
testing float 0.0913306, 0.364602
testing float 0.0922976, 0.487217
testing float 0.454433, 0.52675
0.454433 0.52675 we got q=0.48992, h=0.489927, a=0.489925.  delta = -6.79493e-06,  allowed=7.23169e-07
testing float 0.233178, 0.831292
testing float 0.56806, 0.931731
testing float 0.0508319, 0.556094
testing float 0.0189148, 0.767051
0.0189148 0.767051 we got q=0.210471, h=0.210484, a=0.21048.  delta = -1.37389e-05,  allowed=7.48136e-06
testing float 0.25236, 0.298197
0.25236 0.298197 we got q=0.274796, h=0.274803, a=0.274801.  delta = -6.19888e-06,  allowed=4.58374e-07
testing float 0.531557, 0.875981
testing float 0.515431, 0.920261
testing float 0.18842, 0.810429
testing float 0.570614, 0.886314
testing float 0.0767746, 0.815274
testing float 0.118352, 0.984891
0.118352 0.984891 we got q=0.427845, h=0.427872, a=0.427863.  delta = -2.66135e-05,  allowed=8.66539e-06
testing float 0.784484, 0.893906
0.784484 0.893906 we got q=0.838297, h=0.838304, a=0.838302.  delta = -7.09295e-06,  allowed=1.09422e-06

Gli errori FP sono sufficienti a far fuoriuscire il danno quad a meno di zero per alcuni input.

O con non a += 1<<11; b += (1<<12)+1;commentato:

testing float 2048, 4097
testing float 2048.04, 4097.18
^C  (stuck in an infinite loop).

Nessuno di questi problemi si verifica con double. Commenta printfprima di ogni test per vedere che l'output è vuoto (niente dal if(delta too high)blocco).

TODO: usa la doubleversione come riferimento per la floatversione, invece di guardare semplicemente a come convergono con il danno quad.


1

Javascript - 186 byte

Accetta input come una matrice di numeri. Utilizza le trasformazioni medie nella risposta di J42161217 per abbreviare il codice.

Provalo online

f=(v,l=[m=(w,k=0)=>w.map(x=>k+=x)&&k/w.length,w=>1/m(w.map(x=>1/x)),w=>Math.E**m(w.map(x=>Math.log(x))),w=>m(w.map(x=>x**2))**.5].map(x=>x(v)).sort((a,b)=>a-b))=>l[3]-l[0]>1e-5?f(l):l[0]

Spiegazione

f = (
  v,
  l=[
    m=(w,k=0)=>w.map(x=>k+=x)&&k/w.length,  // m = w => arithmetic mean of values in w
    w=>1/m(w.map(x=>1/x)),                  // w => harmonic mean of values in w   
    w=>Math.E**m(w.map(x=>Math.log(x))),    // w => geometric mean of values in w   
    w=>m(w.map(x=>x**2))**.5                // w => quadratic mean of values in w   
  ].map(x=>x(v))                            // get array of each mean using input v, stored in l
  .sort((a,b)=>a-b)                         // sort the outputs
) =>
  l[3] - l[0] > 1e-5 ?                      // is the difference between the largest
                                            // and smallest means > 1/100000?
    f(l) :                                  // if yes, get the mean mean of the means
    l[0]                                    // if no, arbitrarily return the smallest value
                                            // as close enough

Pensavo che sarei stato intelligente e avrei implementato la relazione con i logaritmi, ma sembra che tu e J42161217 siete arrivati ​​prima!
Pureferret,

@Pureferret Non me lo merito, l'ho palesemente rubato: D
asgallant

l'hai scritto in JavaScript però!
Pureferret,

1
Questa è stata la parte facile. Giocare a golf è stato difficile.
asgallant

1
Il TIL non è stato configurato correttamente. Ho aggiunto un link TIL alla risposta.
asgallant


0

SNOBOL4 (CSNOBOL4) , 296 byte

	X =INPUT
	Y =INPUT
	A =(X + Y) / 2.
	P =X * Y
	G =P ^ 0.5
	H =P / A
	Q =(2 * (A ^ 2) - P) ^ 0.5
O	OUTPUT =EQ(Q,A) Q	:S(END)
	M =A
	N =G
	O =H
	P =Q
	A =(M + N + O + P) / 4
	G =(M * N * O * P) ^ 0.25
	H =4 / (1 / M + 1 / N + 1 / O + 1 / P)
	Q =((M ^ 2 + N ^ 2 + O ^ 2 + P ^ 2) / 4) ^ 0.5	:(O)
END

Provalo online!

Implementazione semplice. Usa un trucco dalla mia risposta a una domanda correlata al golf un po 'di più.

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.