Calcular a média média de dois números


41

isenção de responsabilidade: a média média é composta por mim

Defina a média aritmética de números como Defina a média geométrica de números como Defina a média harmônica de números como Defina a média quadrática de números como A média da média ( ) é definida da seguinte maneira: Defina quatro seqüências ( ) comon

M1(x1,...,xn)=x1+x2+...+xnn
n
M0(x1,...,xn)=x1x2...xnn
n
M1(x1,...,xn)=n1x2+1x2+...+1xn
n
M2(x1,...,xn)=x12+x22+...+xn2n
MMak,bk,ck,dk
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 as quatro seqüências convergem para o mesmo número, .MM(x1,x2,...,xn)

Exemplo

A média da média de 1 e 2 é calculada da seguinte forma: comece com Então O cálculo adicional das seqüências deve ser claro. Pode-se observar que eles convergem para o mesmo número, aproximadamente .

a0=(1+2)/2=1.5,b0=12=21.4142,c0=211+12=431.3333,d0=12+222=521.5811.
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.
1.45568889

Desafio

Dados dois números reais positivos, e ( ), calcular a média média .aba<bMM(a,b)

Casos de teste

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

  • Seu programa é válido se a diferença entre a saída e a saída correta não for maior que 1/100000 do valor absoluto da diferença entre os números de entrada.
  • A saída deve ser um único número.

Isso é , então o código mais curto vence!




11
Quão preciso devemos ser?
Modalidade de ignorância


1
Podemos assumir que a primeira entrada é sempre menor que a segunda, como em todos os seus casos de teste? (Se não, vou reverter minha resposta Java.)
Kevin Cruijssen

Respostas:


14

Wolfram Language (Mathematica) , 52 bytes

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

Experimente online!

Na minha primeira abordagem, usei esses componentes internos
Mean GeometricMean HarmonicMeaneRootMeanSquare

Aqui estão algumas substituições para salvar bytes

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

finalmente, podemos atribuir Meana M(como proposto por @ovs) e salvar mais 5 bytes


Economize 2 bytes recodificando GeometricMean
Robin Ryder

@RobinRyder Eu acredito que você quer dizer Harmonic .. nice!
J42161217 01/04

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

@ovs editada .....
J42161217 01/04

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

Experimente online!

-1 byte com melhor condicionamento.
-2 bytes alternando para a base 2.

Como algumas outras respostas, isso usa a expressão da média geométrica como média aritmética na escala de log (aqui na base 2):

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

Ele também usa o fato de que , ou seja, . A condição é, portanto, equivalente a , que é o que eu uso no loop while; isso é conseguido abusando da sintaxe da qual considera apenas o primeiro elemento quando a condição é um vetor, daí a ordem na qual os meios são armazenados. (Observe que também poderíamos usar pois é o mínimo dos quatro, mas não poderíamos usar ou na condição.)k,dkakbkckdk=max(ak,bk,ck,dk)ak=bk=ck=dkdk=M1(ak,bk,ck,dk)whileckakbk

Quando saímos do loop while, xé um vetor constante. O final ?xcalcula sua média para reduzi-lo a um escalar.


1
Não deveria ser vez de ? l o g x nlnxnlogxn
Tau

@Tau Sim, eu estava denotando logaritmo natural por , que é o padrão em R. De qualquer forma, agora mudei para logaritmo de base 2 para -2 bytes. log
Robin Ryder

6

J , 34 bytes

(31 como uma expressão sem a atribuição à variável f)

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

Experimente online!

Para funções ae b, a &.: b("a sob b" ( desafio relacionado )) é equivalente a (b inv) a b- aplique b, então a, então inversa de b. Nesse caso, a média geométrica / harmônica / quadrática é a média aritmética "abaixo" do logaritmo, inversão e quadrado, respectivamente.


5

TI-BASIC, 42 35 34 bytes

-1 byte graças a @SolomonUcko

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

Entrada é uma lista de dois números inteiros em Ans.
A saída é armazenada Anse impressa automaticamente quando o programa é concluído.

As fórmulas usadas para médias geométricas, harmônicas e quadráticas são baseadas na explicação do usuário202729 .

Exemplo:

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

Explicação:
(Novas linhas foram adicionadas para esclarecimento. Elas NÃO aparecem no 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 é uma linguagem tokenizada. Contagem de caracteres não é igual à contagem de bytes.

e^(é esse token de um byte.

^-1é usado para este token de um byte.
Optei por escrever, ^-1porque o token parece estar ֿ¹em um bloco de código.

√(é esse token de um byte.

ΔList(é esse token de dois bytes.


Eu acho que você pode salvar um parêntese colocando a média geométrica por último.
Solomon Ucko 01/04

@ SolomonUcko ah, obrigado por perceber! Não considerou isso antes.
Tau

max(DeltaList(Ans-> variance(Ans.
lirtosiast 12/06

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 graças a @PeterCordes .
-15 mais bytes graças a @PeterCordes , inspirado na resposta R. de @RobinRyder .
+4 bytes porque assumi que as entradas são pré-encomendadas.
-27 bytes graças a @ OlivierGrégoire .

Experimente online.

Explicação:

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

Em C, você pode f+=Math.abs(d-D)<1e-9;obter e obter uma conversão implícita de um resultado booleano de comparação para um número inteiro 0/1 e depois double. O Java tem alguma sintaxe compacta para isso? Ou é possível fazer f+=Math.abs(d-D)e depois verificar se a soma das diferenças absolutas é pequena o suficiente?
Peter Cordes

1
Sim, para seus casos de teste, f>1e-8funciona como uma condição de loop: 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];}. Com 1e-9, ele roda mais devagar (cerca de duas vezes o tempo de CPU), tendo que fazer mais iterações para d-Dreduzir essencialmente 4 * esse tamanho. Com 1e-7, é aproximadamente a mesma velocidade que 1e-8. Com 1e-6, alguns dos dígitos finais diferem em um caso.
Peter Cordes

1
A resposta de @ RobinRyder aponta que a média quadrática é sempre a maior e a harmônica sempre a menor, então talvez você possa se livrar fcompletamente e apenas verificar a[3]-a[2]<4e-9.
Peter Cordes

1
@ PeterCordes que l==2||você quer dizer ( jogou golfe l<3|). Mas sim, bom argumento; Eu adicionei. :)
Kevin Cruijssen

2
180 bytes agregando redutores agregáveis.
Olivier Grégoire

3

Carvão , 40 bytes

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

Experimente online! Link é a versão detalhada do código. Recebe a entrada como uma matriz de números. Explicação:

W‹⌊θ⌈θ

Repita enquanto a matriz contém valores diferentes ...

≔⟦....⟧θ

... substitua a matriz por uma lista de valores:

∕ΣθLθ

... O significativo...

XΠθ∕¹Lθ

... a média geométrica ...

∕LθΣ∕¹θ

... o meio harmônico ...

₂∕ΣXθ²Lθ

... e a raiz do quadrado médio.

I⊟θ

Transmitir um elemento da matriz para string e imprimi-lo implicitamente.




3

05AB1E , 26 24 23 bytes

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

Experimente online ou veja as etapas de todos os casos de teste .

-1 byte graças a @Grimy .

Alternativa de 23 bytes para a média geométrica:

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

Experimente online ou veja as etapas de todos os casos de teste .

Explicação:

Δ         # 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 11/09

@ Grimy Thanks! Não posso acreditar que eu não tinha pensado em usar o comprimento em vez de Yem 2/4. :)
Kevin Cruijssen 12/09

1
Outros 23, que traz melhoria mostra a similaridade da média geométrica para os outros: Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н. Infelizmente, não parece que podemos refatorar todos esses ÅAs.
Grimmy 12/09

@ Grimy Oh, eu gosto desta segunda versão. :) EDIT: Opa .. obrigado por perceber o meu erro na explicação ..>.>
Kevin Cruijssen 12/09

Eu não programo muito bem o 05ab1e, mas você pode calcular somas e depois dividi-las todas pela extensão mais tarde?
alguém

2

Geléia , 25 24 bytes

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

Experimente online!

Explicação

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

Eu sou bastante ruim em Jelly, mas poderia algo semelhante P*İLfuncionar para a média geométrica?
alguém

@ alguém precisaria ser P*Lİ$para não salvar bytes. Isso significaria que eu poderia reduzir Æmuma linha sem custar bytes, mas eu gosto bastante do fato de que cada uma atualmente possui uma média aritmética em seu 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)

Experimente online!

Função recursiva f, convergirá para precisão de ponto flutuante. Funciona em princípio para todas as listas de números positivos de qualquer tamanho, mas é limitado pelo limite de recursão do Python, um erro de arredondamento para alguns casos de teste.


Como alternativa, resolvendo a precisão de 9 casas decimais:

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)

Experimente online!


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

Experimente online!


2
Isso parece realmente em uma variável que deve ser passada. Além disso, você deve incluir using Systeme using System.Linqna sua contagem de bytes, pois eles são necessários para a execução do programa. Você pode alterar seu compilador para o C # Visual Interactive Compiler, que não precisa dessas importações. Além disso, 1.0->1d
Modalidade de ignorância

1

Limpo , 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]

Experimente online!

Executa a operação até que o resultado pare de mudar.

Hurrah para ponto flutuante de precisão limitada!


1

Pitão, 32 bytes

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

Experimente online aqui ou verifique todos os casos de teste (barra dois, veja a nota abaixo) de uma vez aqui . Aceita entrada como uma lista.

Parece haver alguns problemas com o arredondamento, pois certas entradas não convergem corretamente quando deveriam. Em particular, o caso de teste 0.01 100fica preso em valores [6.748305820749738, 6.748305820749738, 6.748305820749739, 6.748305820749738]e o caso de teste 1.61 2.41fica preso em [1.9896543776640825, 1.9896543776640825, 1.9896543776640827, 1.9896543776640825]- observe nos dois casos que a terceira média (média harmônica) difere das outras.

Não tenho certeza se esse problema invalida minha entrada, mas estou postando assim mesmo, pois deve funcionar. Se isso não for aceitável, pode ser corrigido inserindo .RRTantes de [, para arredondar cada uma das médias para 10 casas decimais, como visto nesta suíte de testes .

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 eu tenho certeza que o cálculo repetido não vai pular para os valores anteriores, você poderia substituir .Wt{Hcom ua -4 bytes (e mudança Zde G)
ar4093

1

Japt v2.0a0 -g, 42 38 bytes

â ÊÉ?ß[Ux²÷(V=UÊ)¬Ux÷V U×qV V÷Ux!÷1]:U

Tem que haver um caminho mais curto ... Isso é uma monstruosidade! Guardado 4 bytes graças a @Shaggy!

Tente


38 bytes . Mas eu concordo, deve haver um caminho mais curto!
Shaggy

1

C # (compilador interativo do 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;

Obrigado a @KevinCruijjsen por apontar que o uso da precisão de ponto flutuante estava causando problemas! Seriam 163 bytes se os dobros fossem perfeitamente precisos.

Experimente online!


Os dois últimos casos de teste fornecem uma StackOverflowExceptionprecisão de ponto flutuante. Em vez de c==g[0]você poderia fazer algo parecido Math.Abs(c-g[0])<1e-9. Experimente online.
Kevin Cruijssen

@KevinCruijssen Obrigado, é uma dor lidar com números de ponto flutuante
Modalidade de Ignorância

1

código de máquina x86 (float SIMD 4x usando SSE1 e AVX de 128 bits) 94 bytes

código de máquina x86 (SIMD 4x duplo usando AVX de 256 bits) 123 bytes

floatpassa nos casos de teste da pergunta, mas com um limite de saída de loop pequeno o suficiente para que isso aconteça, é fácil ficar preso em um loop infinito com entradas aleatórias.

As instruções de precisão única compactada do SSE1 têm 3 bytes de comprimento, mas as instruções SSE2 e AVX simples têm 4 bytes. (As instruções escalares únicas, como sqrtsstambém têm 4 bytes, é por isso que eu uso sqrtpsmesmo que me importe apenas com o elemento baixo. Não é nem mais lento que o sqrtss no hardware moderno). Usei o AVX para destinos não destrutivos para economizar 2 bytes vs. movaps + op.
Na versão dupla, ainda podemos movlhpscopiar algumas partes de 64 bits (porque geralmente nos importamos apenas com o elemento baixo de uma soma horizontal). A soma horizontal de um vetor SIMD de 256 bits também exige um extra vextractf128para obter a metade alta, em comparação com a estratégia lenta, porém pequena, de 2x haddpspara flutuação . odoubleA versão também precisa de constantes 2x de 8 bytes, em vez de 2x de 4 bytes. No geral, sai perto de 4/3 do tamanho da floatversão.

mean(a,b) = mean(a,a,b,b)para todos os quatro desses meios , para que possamos duplicar a entrada até 4 elementos e nunca ter que implementar length = 2. Assim, podemos codificar a média geométrica como quarta raiz = sqrt (sqrt), por exemplo. E nós só precisamos de uma constante FP 4.0,.

Temos um único vetor SIMD de todos os 4 [a_i, b_i, c_i, d_i]. A partir disso, calculamos as 4 médias como escalares em registros separados e as embaralhamos novamente para a próxima iteração. (As operações horizontais nos vetores SIMD são inconvenientes, mas precisamos fazer a mesma coisa para todos os 4 elementos em casos suficientes para equilibrar. Comecei em uma versão x87 disso, mas estava ficando muito longa e não divertida.)

A condição de saída de loop de }while(quadratic - harmonic > 4e-5)(ou uma constante menor para double) é baseada na resposta R de @ RobinRyder e na resposta Java de Kevin Cruijssen : média quadrática é sempre a maior magnitude e média harmônica é sempre a menor (ignorando erros de arredondamento). Portanto, podemos verificar o delta entre esses dois para detectar convergência. Retornamos a média aritmética como resultado escalar. Geralmente é entre os dois e é provavelmente o menos suscetível a erros de arredondamento.

Versão flutuante : chamável como float meanmean_float_avx(__m128);no argumento arg e retorne o valor em xmm0. (Portanto, x86-64 System V ou Windows x64 vectorcall, mas não x64 fastcall.) Ou declare o tipo de retorno __m128para que você possa obter a média quadrática e harmônica dos testes.

Deixar isso levar dois floatargumentos separados em xmm0 e xmm1 custaria 1 byte extra: precisaríamos de um shufpscom um imm8 (em vez de apenas unpcklps xmm0,xmm0) para embaralhar e 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

(Listagem NASM criada com nasm -felf64 mean-mean.asm -l/dev/stdout | cut -b -34,$((34+6))-. Retire a parte da listagem e recupere a fonte cut -b 34- > mean-mean.asm)

A soma e divisão horizontal SIMD por 4 (ou seja, média aritmética) é implementada em uma função separada que nós call(com um ponteiro de função para amortizar o custo do endereço). Com 4/xantes / depois, ou x^2antes e sqrt depois, obtemos a média harmônica e a média quadrática. (Foi doloroso escrever essas divinstruções em vez de multiplicá- las por uma representação exata 0.25).

A média geométrica é implementada separadamente com sqrt multiplicado e encadeado. Ou com um sqrt primeiro para reduzir a magnitude do expoente e talvez ajudar na precisão numérica. o log não está disponível, apenas floor(log2(x))via AVX512 vgetexpps/pd. Exp está disponível via AVX512ER (somente Xeon Phi), mas com apenas 2 ^ -23 de precisão.

A mistura de instruções AVX de 128 bits e SSE herdado não é um problema de desempenho. A mistura do AVX de 256 bits com o SSE herdado pode estar no Haswell, mas no Skylake, apenas cria uma potencial dependência falsa em potencial para as instruções do SSE. Acho que minha doubleversão evita cadeias de dep transportadas por loop desnecessárias e gargalos na latência / taxa de transferência div / sqrt.

Versão dupla:

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

}

Construa com:

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

Obviamente, você precisa de uma CPU com suporte a AVX ou de um emulador como o Intel SDE. Para compilar em um host sem suporte nativo ao AVX, use -march=sandybridgeou-mavx

Execução: passa nos casos de teste codificados, mas para a versão flutuante, os casos aleatórios geralmente falham no (b-a)/10000limite definido na pergunta.

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

Os erros de FP são suficientes para que o dano quad seja menor que zero para algumas entradas.

Ou com não a += 1<<11; b += (1<<12)+1;comentado:

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

Nenhum desses problemas ocorre com double. Comente o printfantes de cada teste para ver se a saída está vazia (nada do if(delta too high)bloco).

TODO: use a doubleversão como referência para a floatversão, em vez de apenas ver como eles estão convergindo com dano quad.


1

Javascript - 186 bytes

Recebe a entrada como uma matriz de números. Usa as transformações médias na resposta de J42161217 para encurtar o código.

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

Explicação

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

Eu pensei que seria inteligente e implementaria o relacionamento com logaritmos, mas parece que você e J42161217 chegaram lá primeiro!
Pureferret 03/04

@Pureferret Não aceito nenhum crédito por isso, roubei descaradamente: D
asgallant 03/04

você escreveu em JavaScript!
Pureferret 03/04

1
Essa era a parte fácil. Jogar golfe foi difícil.
asgallant 03/04

1
O TIL não foi configurado corretamente. Eu adicionei um link TIL à resposta.
asgallant 04/04


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.