Calcular la media de dos números


41

descargo de responsabilidad: la media es hecha por mí

Definir la media aritmética de n números como

M1(x1,...,xn)=x1+x2+...+xnn
Definir la media geométrica dennúmeros como
M0(x1,...,xn)=x1x2...xnn
Definir la media armónica dennúmeros como
M1(x1,...,xn)=n1x2+1x2+...+1xn
Definir la media cuadrática dennúmeros como
M2(x1,...,xn)=x12+x22+...+xn2n
The Mean media (MM) se define como sigue: definir cuatro secuencias (ak,bk,ck,dk) como
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)
Todas las cuatro secuencias convergen para el mismo número,MM(x1,x2,...,xn) .

Ejemplo

La media media de 1 y 2 se calcula de la siguiente manera: comience con

a0=(1+2)/2=1.5,b0=12=21.4142,c0=211+12=431.3333,d0=12+222=521.5811.
Entonces
a1=1.5+1.4142+1.3333+1.581141.4571,b1=1.51.41421.33331.581141.4542,c1=411.5+11.4142+11.3333+11.58111.4512,d1=1.52+1.41422+1.33332+1.5811241.4601.
El cálculo adicional de las secuencias debe ser claro. Se puede ver que convergen al mismo número, aproximadamente1.45568889.

Reto

Dados dos números reales positivos, a y b ( a<b ), calcule su media media MM(a,b) .

Casos de prueba

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

Notas

  • Su programa es válido si la diferencia entre su salida y la salida correcta no es mayor que 1/100000 del valor absoluto de la diferencia entre los números de entrada.
  • La salida debe ser un solo número.

Este es el , por lo que gana el código más corto.




11
¿Qué tan precisos se supone que debemos ser?
Encarnación de la ignorancia


1
¿Podemos suponer que la primera entrada es siempre más pequeña que la segunda, como en todos sus casos de prueba? (Si no, revertiré mi respuesta Java.)
Kevin Cruijssen

Respuestas:


14

Wolfram Language (Mathematica) , 52 bytes

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

Pruébalo en línea!

En mi primer acercamiento usé estos builtins
Mean GeometricMean HarmonicMeanyRootMeanSquare

Aquí hay algunas sustituciones para guardar bytes

HarmonicMean-> 1/Mean[1/x] por @Robin Ryder (3 bytes guardados)
GeometricMean-> E^Mean@Log@xpor @A. Rex (2 bytes guardados)
RootMeanSquare-> Mean[x^2]^.5por @A. Rex (4 bytes guardados)

finalmente podemos asignar Meana M(según lo propuesto por @ovs) y guardar 5 bytes más


Ahorre 2 bytes recodificando GeometricMean
Robin Ryder

@RobinRyder Creo que te refieres a Harmonic ... ¡bien!
J42161217

1
Ahorre 8 bytes más :#//.x_:>N@{Mean@x,E^Mean@Log@x,1/Mean[1/x],Mean[x^2]^.5}&
A. Rex

@ovs editado .....
J42161217

10

R, 70 69 67 bytes

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

Pruébalo en línea!

-1 byte con mejor acondicionamiento.
-2 bytes cambiando a la base 2.

M0(x1,,xn)=2M1(log2x1,,log2xn).

k,dkakbkckdk=max(ak,bk,ck,dk)ak=bk=ck=dkdk=M1(ak,bk,ck,dk)whileckakbk

Cuando salimos del ciclo while, xes un vector constante. El final ?xcalcula su media para reducirlo a un escalar.


1
lnxnlogxn

log

6

J , 34 bytes

(31 como una expresión sin la asignación a variable f)

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

Pruébalo en línea!

Para funciones ay b, a &.: b("a debajo de b" ( desafío relacionado )) es equivalente a (b inv) a b- aplicar b, luego a, luego inverso de b. En este caso, la media geométrica / armónica / cuadrática es la media aritmética "debajo" del logaritmo, la inversión y el cuadrado, respectivamente.


5

TI-BASIC, 42 35 34 bytes

-1 byte gracias a @SolomonUcko

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

La entrada es una lista de dos enteros en Ans.
La salida se almacena enAns y se imprime automáticamente cuando se completa el programa.

Las fórmulas utilizadas para medios geométricos, armónicos y cuadráticos se basan en la explicación del usuario 202729 .

Ejemplo:

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

Explicación:
(Se han agregado nuevas líneas para aclaración. NO aparecen en el código).

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

Notas:

TI-BASIC es un lenguaje tokenizado. El recuento de caracteres no es igual al recuento de bytes.

e^(es este token de un byte.

^-1se usa para este token de un byte.
En su lugar, opté por escribir ^-1porque el token se ve ֿ¹cuando está en un bloque de código.

√(es este token de un byte.

ΔList(es este token de dos bytes.


Creo que puedes guardar un paréntesis colocando la media geométrica al final.
Solomon Ucko

@SolomonUcko ah, ¡gracias por notarlo! No lo consideré antes.
Tau

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

5

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

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 bytes gracias a @PeterCordes .
-15 bytes más gracias a @PeterCordes , inspirado en la respuesta R de @RobinRyder .
+4 bytes porque supuse que las entradas están pre ordenadas.
-27 bytes gracias a @ OlivierGrégoire .

Pruébalo en línea.

Explicación:

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

En C, podría f+=Math.abs(d-D)<1e-9;obtener una conversión implícita de un resultado de comparación booleano a un entero 0/1 y luego double. ¿Java tiene alguna sintaxis compacta para eso? ¿O es posible hacer f+=Math.abs(d-D)y luego verificar que la suma de las diferencias absolutas sea lo suficientemente pequeña ?
Peter Cordes

1
Sí, para sus casos de prueba, f>1e-8funciona como una condición de bucle: 229 bytes. 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, se ejecuta más lento (aproximadamente el doble del tiempo de CPU), teniendo que hacer más iteraciones para obtener esencialmente 4 * d-Dmenos de ese tamaño. Con 1e-7, es aproximadamente la misma velocidad que 1e-8. Con 1e-6algunos de los dígitos finales difieren para un caso.
Peter Cordes

1
La respuesta de @ RobinRyder señala que la media cuadrática es siempre la más grande y la armónica siempre la más pequeña, por lo que tal vez pueda deshacerse por fcompleto y solo verificar a[3]-a[2]<4e-9.
Peter Cordes

1
@PeterCordes l==2||te refieres (golfed l<3|). Pero sí, buen punto; Lo he agregado :)
Kevin Cruijssen

2
180 bytes agregando reductores acumulables.
Olivier Grégoire

3

Carbón , 40 bytes.

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

Pruébalo en línea! El enlace es a la versión detallada del código. Toma la entrada como una matriz de números. Explicación:

W‹⌊θ⌈θ

Repita mientras la matriz contiene diferentes valores ...

≔⟦....⟧θ

... reemplaza la matriz con una lista de valores:

∕ΣθLθ

... el significado...

XΠθ∕¹Lθ

... la media geométrica ...

∕LθΣ∕¹θ

... el significado armónico ...

₂∕ΣXθ²Lθ

... y la raíz del cuadrado medio.

I⊟θ

Convierta un elemento de la matriz en una cadena e imprímalo implícitamente.




3

05AB1E , 26 24 23 bytes

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

Pruébelo en línea o vea los pasos de todos los casos de prueba .

-1 byte gracias a @Grimy .

Alternativa de 23 bytes para la media geométrica:

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

Pruébelo en línea o vea los pasos de todos los casos de prueba .

Explicación:

Δ         # 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 Gracias! No puedo creer que no haya pensado en usar la longitud en lugar de Y2/4. :)
Kevin Cruijssen

1
Otros 23 que los apostantes muestra la similitud de la media geométrica de los otros: Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н. Desafortunadamente, no parece que podamos refactorizar todos esos ÅAs.
Grimmy

@ Grimy Oh, me gusta esta segunda versión. :) EDITAR: Vaya ... gracias por notar mi error en la explicación ..>.>
Kevin Cruijssen

No programo muy bien en 05ab1e, pero ¿puedes calcular sumas y luego dividirlas por la longitud más adelante?
alguien el

2

Jalea , 25 24 bytes

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

Pruébalo en línea!

Explicación

                    µÐ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

Soy bastante malo en Jelly, pero ¿podría algo similar al P*İLtrabajo para la media geométrica?
alguien

@ alguien debería ser P*Lİ$así que no guardaría bytes. Significaría que podría Æmvolver a bajar una línea sin costar bytes, pero me gusta mucho el hecho de que cada uno tiene actualmente una media aritmética en su núcleo.
Nick Kennedy

2

Python 3 , 152 bytes

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)

Pruébalo en línea!

La función recursiva fconvergerá a la precisión de coma flotante. Funciona en principio para todas las listas de números positivos de cualquier tamaño, pero está limitado por el límite de recurrencia de Python, un error de redondeo para algunos casos de prueba.


Alternativamente, conformarse con precisión de 9 decimales:

Python 3 , 169 bytes

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)

Pruébalo en línea!


1

C # , 173 bytes

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

Pruébalo en línea!


2
Esto parece realmente en una variable que debe pasarse. Además, debe incluir using Systemy using System.Linqen su recuento de bytes, ya que son necesarios para que el programa se ejecute. Puede cambiar su compilador al Compilador interactivo visual de C #, que no necesita esas importaciones. Además, 1.0->1d
Encarnación de la ignorancia

1

Limpio , 124 bytes

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]

Pruébalo en línea!

Realiza la operación hasta que el resultado deja de cambiar.

¡Hurra por la precisión limitada del punto flotante!


1

Pyth, 32 bytes

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

Pruébelo en línea aquí , o verifique todos los casos de prueba (barra dos, vea la nota a continuación) de una vez aquí . Acepta entradas como una lista.

Parece haber algunos problemas con el redondeo, ya que ciertas entradas no convergen correctamente cuando de lo contrario deberían. En particular, el caso de prueba 0.01 100se atasca en los valores [6.748305820749738, 6.748305820749738, 6.748305820749739, 6.748305820749738], y el caso de prueba 1.61 2.41se atasca en [1.9896543776640825, 1.9896543776640825, 1.9896543776640827, 1.9896543776640825]- observe en ambos casos que la tercera media (media armónica) difiere de las demás.

No estoy seguro de si este problema invalida mi entrada, pero lo estoy publicando de todos modos, ya que debería funcionar. Si esto no es aceptable, se puede arreglar inscribiéndolo .RRTantes de [, para redondear cada uno de los medios a 10 decimales, como se ve en este conjunto de pruebas .

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

Desde que estoy bastante seguro de que el cálculo se repite a no saltar a los valores anteriores, se puede sustituir .Wt{Hcon ude -4 bytes (y el cambio Za G)
ar4093


1

C # (compilador interactivo de Visual C #) , 177 bytes

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;

¡Gracias a @KevinCruijjsen por señalar que usar la precisión de coma flotante estaba causando problemas! Sería 163 bytes si los dobles fueran perfectamente precisos.

Pruébalo en línea!


Los dos últimos casos de prueba se StackOverflowExceptiondeben a la precisión de coma flotante. En lugar de c==g[0]usted podría hacer algo así Math.Abs(c-g[0])<1e-9. Pruébalo en línea.
Kevin Cruijssen

@KevinCruijssen Gracias, es un dolor lidiar con los números de coma flotante
Encarnación de la ignorancia

1

Código de máquina x86 (SIMD 4x flotante usando SSE1 y AVX de 128 bits) 94 bytes

Código de máquina x86 (SIMD 4x doble con AVX de 256 bits) 123 bytes

floatpasa los casos de prueba en la pregunta, pero con un umbral de salida de bucle lo suficientemente pequeño como para que eso suceda, es fácil que se quede atascado en un bucle infinito con entradas aleatorias.

Las instrucciones SSE1 de precisión simple empaquetada tienen 3 bytes de longitud, pero las instrucciones SSE2 y AVX simples tienen 4 bytes de longitud. (Las instrucciones simples escalares sqrtsstambién tienen una longitud de 4 bytes, por eso lo utilizo sqrtpsaunque solo me importa el elemento bajo. Ni siquiera es más lento que sqrtss en el hardware moderno). Usé AVX para un destino no destructivo para guardar 2 bytes vs.movaps + op.
En la versión doble todavía podemos hacer un par movlhpspara copiar fragmentos de 64 bits (porque a menudo solo nos importa el elemento bajo de una suma horizontal). La suma horizontal de un vector SIMD de 256 bits también requiere un extra vextractf128para obtener la mitad alta, en comparación con la estrategia lenta pero pequeña de 2x haddpspara flotación . losdoubleLa versión también necesita 2x constantes de 8 bytes, en lugar de 2x 4 bytes. En general, sale cerca de 4/3 del tamaño de la floatversión.

mean(a,b) = mean(a,a,b,b)para los 4 de estos medios , por lo que simplemente podemos duplicar la entrada de hasta 4 elementos y nunca tener que implementar length = 2. Por lo tanto, podemos codificar la media geométrica como 4th-root = sqrt (sqrt), por ejemplo. Y sólo tenemos una constante FP, 4.0.

Tenemos un solo vector SIMD de los 4 [a_i, b_i, c_i, d_i]. A partir de eso, calculamos los 4 medios como escalares en registros separados y los volvemos a mezclar para la próxima iteración. (Las operaciones horizontales en los vectores SIMD son inconvenientes, pero necesitamos hacer lo mismo para los 4 elementos en suficientes casos que se equilibren. Comencé con una versión x87 de esto, pero se estaba haciendo muy larga y no era divertida).

La condición de salida de bucle de }while(quadratic - harmonic > 4e-5)(o una constante menor para double) se basa en la respuesta R de @ RobinRyder y la respuesta Java de Kevin Cruijssen : la media cuadrática es siempre la magnitud más grande y la media armónica es siempre la más pequeña (ignorando los errores de redondeo). Entonces podemos verificar el delta entre esos dos para detectar la convergencia. Devolvemos la media aritmética como el resultado escalar. Generalmente está entre esos dos y es probablemente el menos susceptible a los errores de redondeo.

Versión flotante : invocable como float meanmean_float_avx(__m128);con el valor arg y return en xmm0. (Por lo tanto, x86-64 System V o Windows x64 vectorcall, pero no x64 fastcall). O declare el tipo de retorno __m128para que pueda obtener la media cuadrática y armónica para la prueba.

Dejar que esto tome 2 floatargs separados en xmm0 y xmm1 costaría 1 byte adicional: necesitaríamos un shufpscon un imm8 (en lugar de solo unpcklps xmm0,xmm0) para mezclar y duplicar 2 entradas.

    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

(Listado NASM creado con nasm -felf64 mean-mean.asm -l/dev/stdout | cut -b -34,$((34+6))-. Elimine la parte del listado y recupere la fuente con cut -b 34- > mean-mean.asm)

SIMD suma horizontal y dividir por 4 (es decir, media aritmética) se implementa en una función separada que nosotros call(con un puntero de función para amortizar el costo de la dirección). Con 4/xbefore / after, o x^2before y sqrt after, obtenemos la media armónica y la media cuadrática. (Fue doloroso escribir estas divinstrucciones en lugar de multiplicarlo por uno exactamente representable 0.25).

La media geométrica se implementa por separado con sqrt multiplicado y encadenado. O con un sqrt primero para reducir la magnitud del exponente y quizás ayudar a la precisión numérica. el registro no está disponible, solo a floor(log2(x))través de AVX512 vgetexpps/pd. Exp está disponible a través de AVX512ER (solo Xeon Phi), pero con solo 2 ^ -23 de precisión.

Mezclar instrucciones AVX de 128 bits y SSE heredado no es un problema de rendimiento. Mezclar AVX de 256 bits con SSE heredado puede estar en Haswell, pero en Skylake simplemente crea una potencial dependencia falsa para las instrucciones de SSE. Creo que mi doubleversión evita cadenas dep innecesarias transportadas en bucle y cuellos de botella en la latencia / rendimiento div / sqrt.

Versión doble:

   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

Arnés de prueba C

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

}

Construir con:

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

Obviamente necesita una CPU con soporte AVX, o un emulador como Intel SDE. Para compilar en un host sin soporte AVX nativo, use -march=sandybridgeo-mavx

Ejecutar: pasa los casos de prueba codificados, pero para la versión flotante, los casos de prueba aleatorios a menudo fallan el (b-a)/10000umbral establecido en la pregunta.

$ ./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

Los errores de FP son suficientes para que el daño cuádruple sea menor que cero para algunas entradas.

O con sin a += 1<<11; b += (1<<12)+1;comentar:

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

Ninguno de estos problemas ocurre double. Comente printfantes de cada prueba para ver que la salida está vacía (nada del if(delta too high)bloque).

TODO: use la doubleversión como referencia para la floatversión, en lugar de solo mirar cómo convergen con quad-harm.


1

Javascript - 186 bytes

Toma la entrada como una matriz de números. Utiliza las transformaciones medias en la respuesta de J42161217 para acortar el código.

Pruébalo en línea

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]

Explicación

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

Pensé que iba a ser inteligente e implementar la relación con los logaritmos, ¡ pero parece que tú y J42161217 llegaron primero!
Pureferret

@Pureferret No tengo crédito por eso, lo robé descaradamente: D
asgallant

¡aunque lo escribiste en JavaScript!
Pureferret

1
Esa fue la parte fácil. Jugar al golf fue difícil.
Asgallant

1
El TIL no se configuró correctamente. Agregué un enlace TIL a la respuesta.
Asgallant


0

SNOBOL4 (CSNOBOL4) , 296 bytes

	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

Pruébalo en línea!

Implementación directa. Utiliza un truco de mi respuesta a una pregunta relacionada con el golf un poco más.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.