Oblicz średnią średnią z dwóch liczb


41

zrzeczenie się odpowiedzialności: Średnia wartość jest tworzona przeze mnie

Zdefiniuj średnią arytmetyczną liczb jako Zdefiniuj średnią geometryczną liczb jako Zdefiniuj średnią harmoniczną liczb jako Zdefiniuj średnią kwadratową liczby jako Średnia średnia ( ) jest zdefiniowana następująco: Zdefiniuj cztery sekwencje ( ) jakon

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
MMk,bk,ck,dka0=M1(x1,...,xn),ak,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)
Wszystkie cztery sekwencje są zbieżne do ten sam numer, .MM(x1,x2,...,xn)

Przykład

Średnia wartość 1 i 2 jest obliczana w następujący sposób: zacznij od Następnie Dalsze obliczanie sekwencji powinno być jasne. Można zauważyć, że zbiegają się one do tej samej liczby, około .

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

Wyzwanie

Biorąc pod uwagę dwie dodatnie liczby rzeczywiste, i ( ), obliczyć ich średnią średnicę .aba<bMM(a,b)

Przypadki testowe

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

Notatki

  • Twój program jest ważny, jeśli różnica między jego wyjściem a poprawnym wyjściem nie jest większa niż 1/100000 wartości bezwzględnej różnicy między liczbami wejściowymi.
  • Wynik powinien być pojedynczą liczbą.

To jest , więc wygrywa najkrótszy kod!




11
Jak dokładni powinniśmy być?
Embodiment of Ignorance


1
Czy możemy założyć, że pierwsze dane wejściowe są zawsze mniejsze niż drugie, tak jak we wszystkich przypadkach testowych? (Jeśli nie,
cofnę

Odpowiedzi:


14

Wolfram Język (Mathematica) , 52 bajtów

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

Wypróbuj online!

W pierwszym podejściu użyłem tych wbudowanych
Mean GeometricMean HarmonicMeaniRootMeanSquare

Oto niektóre podstawienia dla zapisywania bajtów

HarmonicMean-> 1/Mean[1/x] autor: @Robin Ryder (zapisane 3 bajty)
GeometricMean-> E^Mean@Log@xautor: @A. Rex (2 bajty zapisane)
RootMeanSquare-> Mean[x^2]^.5według @A. Rex (zapisane 4 bajty)

wreszcie możemy przypisać Meando M(zgodnie z propozycją @ovs) i zaoszczędzić 5 dodatkowych bajtów


Zaoszczędź 2 bajty , przekodowując GeometricMean
Robin Ryder

@RobinRyder Wierzę, że masz na myśli Harmonic .. fajnie!
J42161217

1
Zaoszczędź 8 dodatkowych bajtów :#//.x_:>N@{Mean@x,E^Mean@Log@x,1/Mean[1/x],Mean[x^2]^.5}&
A. Rex

@ovs edytowane .....
J42161217

10

R, 70 69 67 bajtów

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

Wypróbuj online!

-1 bajt z lepszą kondycją.
-2 bajty, przechodząc do bazy 2.

Podobnie jak niektóre inne odpowiedzi, używa wyrażenia średniej geometrycznej jako średniej arytmetycznej na skali logarytmicznej (tutaj w bazie 2):

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

Wykorzystuje również fakt, że , tj. . Warunek jest zatem równoważny , którego używam w pętli while; osiąga się to poprzez nadużywanie składni, której pierwszy element bierze pod uwagę tylko warunek, który jest wektorem, a zatem kolejność, w jakiej są przechowywane środki. (Zauważ, że moglibyśmy również użyć ponieważ jest to minimum cztery, ale nie mogliśmy użyć lub pod warunkiem.)k,dkakbkckdk=max(ak,bk,ck,dk)ak=bk=ck=dkdk=M1(ak,bk,ck,dk)whileckakbk

Kiedy wychodzimy z pętli while, xjest wektorem stałym. Finał ?xoblicza swój sposób sprowadzenia go do skalara.


1
Czy nie powinno to być zamiast ? l o g x nlnxnlogxn
Tau

@Tau Tak, byłem oznaczająca logarytm naturalny przez , który jest domyślny w R. W każdym razie, mam teraz zmienił go do podstawy 2 logarytmu dla -2 bajtów. log
Robin Ryder

6

J , 34 bajty

(31 jako wyrażenie bez przypisania do zmiennej f)

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

Wypróbuj online!

Dla funkcji ai b, a &.: b( „a pod b” ( związane wyzwanie )) jest równoważne (b inv) a b- zastosowanie b, a następnie, po czym odwrotnością b. W tym przypadku średnia geometryczna / harmoniczna / kwadratowa jest odpowiednio średnią arytmetyczną „pod” logarytmem, inwersją i kwadratem.


5

TI-BASIC, 42 35 34 bajtów

-1 bajt dzięki @SolomonUcko

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

Dane wejściowe to lista dwóch liczb całkowitych w Ans.
Dane wyjściowe są zapisywane Ansi automatycznie drukowane po zakończeniu programu.

Formuły stosowane do średnich geometrycznych, harmonicznych i kwadratowych są oparte na wyjaśnieniach użytkownika202729 .

Przykład:

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

Objaśnienie:
(Newlines zostały dodane w celu wyjaśnienia. NIE pojawiają się w kodzie).

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

Uwagi:

TI-BASIC jest językiem tokenizowanym. Liczba znaków nie jest równa liczbie bajtów.

e^(to ten jednobajtowy token.

^-1jest używany dla tego jednobajtowego tokena. Zamiast tego
zdecydowałem się na pisanie, ^-1ponieważ token wygląda jak ֿ¹w bloku kodu.

√(to ten jednobajtowy token.

ΔList(to ten dwubajtowy token.


Myślę, że możesz zapisać nawias, umieszczając średnią geometryczną na końcu.
Solomon Ucko

@ SolomonUcko ah, dzięki za zauważenie! Nie rozważałem tego wcześniej.
Tau

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

5

Java 10, 234 229 214 211 215 206 203 196 180 177 bajtów

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 bajtów dzięki @PeterCordes .
-15 dodatkowych bajtów dzięki @PeterCordes , zainspirowanej odpowiedzią R @RobinRyder .
+4 bajty, ponieważ zakładałem, że dane wejściowe są zamówione w przedsprzedaży.
-27 bajtów dzięki @ OlivierGrégoire .

Wypróbuj online.

Wyjaśnienie:

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

W C można f+=Math.abs(d-D)<1e-9;uzyskać domyślną konwersję z wyniku porównania wartości logicznej na liczbę całkowitą 0/1, a następnie double. Czy Java ma do tego kompaktową składnię? Czy też można to zrobić, f+=Math.abs(d-D)a następnie sprawdzić, czy suma różnic bezwzględnych jest wystarczająco mała ?
Peter Cordes

1
Tak, dla przypadków testowych, f>1e-8działa jako warunek pętli: 229 bajtów. 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];}. Dzięki 1e-9działa wolniej (około dwa razy szybciej niż procesor), co oznacza konieczność wykonania większej liczby iteracji, aby uzyskać w zasadzie 4 * niższą wartość d-D. Dzięki 1e-7jest mniej więcej taka sama jak prędkość 1e-8. Z 1e-6, niektóre cyfry końcowe różnią się dla jednego przypadku.
Peter Cordes

1
@ Odpowiedź RobinRydera wskazuje, że średnia kwadratowa jest zawsze największa, a harmoniczna zawsze najmniejsza, więc być może możesz porzucić fcałkowicie i tylko sprawdzić a[3]-a[2]<4e-9.
Peter Cordes

1
@PeterCordes l==2||masz na myśli ( grał w golfa l<3|). Ale tak, dobra uwaga; Dodałem to. :)
Kevin Cruijssen

2
180 bajtów przez agregację agregujących reduktorów.
Olivier Grégoire

3

Węgiel drzewny , 40 bajtów

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

Wypróbuj online! Link jest do pełnej wersji kodu. Pobiera dane wejściowe jako tablicę liczb. Wyjaśnienie:

W‹⌊θ⌈θ

Powtarzaj, gdy tablica zawiera różne wartości ...

≔⟦....⟧θ

... zamień tablicę na listę wartości:

∕ΣθLθ

... znaczy ...

XΠθ∕¹Lθ

... środek geometryczny ...

∕LθΣ∕¹θ

... średnia harmoniczna ...

₂∕ΣXθ²Lθ

... i pierwiastek średni kwadrat.

I⊟θ

Rzuć element tablicy na ciąg i niejawnie go wydrukuj.




3

05AB1E , 26 24 23 bajtów

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

Wypróbuj online lub zobacz kroki wszystkich przypadków testowych .

-1 bajt dzięki @Grimy .

23 bajtowa alternatywa dla średniej geometrycznej:

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

Wypróbuj online lub zobacz kroki wszystkich przypadków testowych .

Wyjaśnienie:

Δ         # 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 Thanks! Nie mogę uwierzyć, że nie pomyślałem o użyciu długości zamiast Y2/4. :)
Kevin Cruijssen

1
Kolejnych 23, która bogatsza pokazuje średnią geometryczną Podobieństwo do innych te, które: Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н. Niestety nie wygląda na to, żebyśmy mogli refaktoryzować wszystkie te ÅA.
Grimmy,

@Grimy Oh, podoba mi się ta druga wersja. :) EDYCJA: Ups .. dzięki za zauważenie mojego błędu w wyjaśnieniu ..>.>
Kevin Cruijssen

Nie programuję zbyt dobrze w 05ab1e, ale czy możesz obliczyć sumy, a następnie podzielić je wszystkie według długości później?
ktoś

2

Galaretka , 25 24 bajtów

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

Wypróbuj online!

Wyjaśnienie

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

Jestem dość zły w Galaretce, ale czy coś podobnego do P*İLpracy dla środka geometrycznego?
ktoś

@ ktoś musiałby to być, P*Lİ$żeby nie oszczędzać bajtów. Oznaczałoby to, że mogę obniżyć Æmlinię bez kosztowania bajtów, ale podoba mi się fakt, że każdy z nich ma obecnie średnią arytmetyczną.
Nick Kennedy

2

Python 3 , 152 bajty

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)

Wypróbuj online!

Funkcja rekurencyjna fzbiegnie się do precyzji zmiennoprzecinkowej. Działa w zasadzie dla wszystkich list liczb dodatnich o dowolnym rozmiarze, ale jest ograniczony ograniczeniem rekurencji Pythona dla niektórych przypadków testowych.


Alternatywnie, z dokładnością do 9 miejsc po przecinku:

Python 3 , 169 bajtów

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)

Wypróbuj online!


1

C # , 173 bajtów

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

Wypróbuj online!


2
Wydaje się, że tak naprawdę dotyczy zmiennej, którą należy przekazać. Również trzeba to using Systemi using System.Linqw liczbie bajtów, ponieważ są one wymagane dla programu do uruchomienia. Możesz zmienić swój kompilator na C # Visual Interactive Compiler, który nie potrzebuje tych importów. Ponadto, 1.0->1d
Embodiment of Ignorance

1

Czysty , 124 bajty

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]

Wypróbuj online!

Wykonuje operację, aż wynik przestanie się zmieniać.

Hurra, aby uzyskać zmiennoprzecinkową ograniczoną precyzję!


1

Pyth, 32 bajty

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

Spróbuj go online tutaj , lub sprawdzić wszystkie przypadki testowe (bar dwa, patrz uwaga poniżej) od razu tutaj . Akceptuje dane wejściowe jako listę.

Wydaje się, że występują pewne problemy z zaokrąglaniem, ponieważ niektóre dane wejściowe nie są zbieżne poprawnie, gdy powinny. W szczególności przypadek testowy 0.01 100blokuje się przy wartościach [6.748305820749738, 6.748305820749738, 6.748305820749739, 6.748305820749738], a przypadek testowy 1.61 2.41blokuje się przy wartościach [1.9896543776640825, 1.9896543776640825, 1.9896543776640827, 1.9896543776640825]- zauważ w obu przypadkach, że trzecia średnia (średnia harmoniczna) różni się od pozostałych.

Nie jestem pewien, czy ten problem unieważnia mój wpis, ale i tak go publikuję, ponieważ powinien działać. Jeśli nie jest to do przyjęcia, można to naprawić, wprowadzając .RRTprzed [, zaokrąglanie każdego ze średnich do 10 miejsc po przecinku, jak widać w tym zestawie testów .

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

Ponieważ jestem całkiem pewien, że nie będzie powtarzany kalkulacja skakać do poprzednich wartości, można zastąpić .Wt{Hz udo -4 bajtów (i zmiany Zdo G)
ar4093


1

C # (interaktywny kompilator Visual C #) , 177 bajtów

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;

Dzięki @KevinCruijjsen za zwrócenie uwagi, że stosowanie precyzji zmiennoprzecinkowej powoduje problemy! Wyniosłoby 163 bajty, gdyby podwójne były idealnie precyzyjne.

Wypróbuj online!


Dwa ostatnie przypadki testowe dają StackOverflowExceptionprecyzję zmiennoprzecinkową. Zamiast c==g[0]ciebie możesz zrobić coś takiego Math.Abs(c-g[0])<1e-9. Wypróbuj online.
Kevin Cruijssen

@KevinCruijssen Dzięki, to taki ból radzi sobie z liczbami zmiennoprzecinkowymi
Embodiment of Ignorance

1

Kod maszynowy x86 (liczba zmiennoprzecinkowa SIMD przy użyciu 128-bitowego SSE1 i AVX) 94 bajty

Kod maszynowy x86 (SIMD 4x dwukrotnie przy 256-bitowym AVX) 123 bajty

floatprzechodzi pytania testowe w pytaniu, ale z progiem wyjścia pętli wystarczająco małym, aby tak się stało, łatwo utknie w nieskończonej pętli z losowymi danymi wejściowymi.

Instrukcje upakowanej pojedynczej precyzji SSE1 mają 3 bajty, ale SSE2 i proste instrukcje AVX mają 4 bajty. (Instrukcje takie jak pojedyncze skalarne sqrtssmają także 4 bajty długości, dlatego używam ich, sqrtpschociaż zależy mi tylko na niskim elemencie. Nie jest nawet wolniejszy niż sqrts na nowoczesnym sprzęcie). Użyłem AVX do nieniszczącego miejsca docelowego, aby zapisać 2 bajty vs. movaps + op.
W wersji podwójnej możemy jeszcze zrobić kilka, movlhpsaby skopiować 64-bitowe fragmenty (ponieważ często dbamy tylko o niski element sumy poziomej). Pozioma suma 256-bitowego wektora SIMD również wymaga dodatkowej wartości, vextractf128aby uzyskać wysoką połowę, w porównaniu do powolnej, ale małej 2x haddpsstrategii float . Thedoublewersja wymaga także 2x 8-bajtowych stałych zamiast 2x 4-bajtowych. Ogólnie rzecz biorąc, okazuje się, że jest blisko 4/3 wielkości floatwersji.

mean(a,b) = mean(a,a,b,b)dla wszystkich 4 tych środków , więc możemy po prostu powielić dane wejściowe do 4 elementów i nigdy nie musimy implementować długości = 2. W ten sposób możemy na przykład zakodować średnią geometryczną jako 4. root = sqrt (sqrt). I potrzebujemy tylko jednej stałej FP 4.0.

Mamy jeden wektor SIMD ze wszystkich 4 [a_i, b_i, c_i, d_i]. Na tej podstawie obliczamy 4 średnie jako skalary w osobnych rejestrach i tasujemy je z powrotem do następnej iteracji. (Operacje poziome na wektorach SIMD są niewygodne, ale musimy zrobić to samo dla wszystkich 4 elementów w wystarczającej liczbie przypadków, aby się wyrównały. Zacząłem od wersji x87, ale robiło się to bardzo długo i nie było fajnie.)

Warunek pętli wyjście }while(quadratic - harmonic > 4e-5)(lub mniejsza stała dla double) opiera się na @ RobinRyder badawczo odpowiedzi i odpowiedź Java Kevin Cruijssen za : średniej kwadratowej jest zawsze największa wielkość, a średnia harmoniczna jest zawsze najmniejsze (ignorując błędy zaokrąglenia). Możemy więc sprawdzić różnicę między tymi dwoma, aby wykryć zbieżność. Zwracamy średnią arytmetyczną jako wynik skalarny. Zwykle jest pomiędzy tymi dwoma i jest prawdopodobnie najmniej podatna na błędy zaokrąglania.

Wersja pływająca : wywoływalna jak float meanmean_float_avx(__m128);z arg i zwracana wartość w xmm0. (Więc x86-64 System V lub Windows x64, ale nie szybka x64.) Lub zadeklaruj typ zwracany __m128, abyś mógł uzyskać kwadratową i harmoniczną średnią dla testu.

Pozwolenie temu wziąć 2 oddzielne floatargumenty w xmm0 i xmm1 kosztowałoby 1 dodatkowy bajt: potrzebowalibyśmy shufpsznaku imm8 (zamiast po prostu unpcklps xmm0,xmm0), aby przetasować razem i zduplikować 2 dane wejściowe.

    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

(Lista NASM utworzona za pomocą nasm -felf64 mean-mean.asm -l/dev/stdout | cut -b -34,$((34+6))-. Usuń część listy i odzyskaj źródło za pomocą cut -b 34- > mean-mean.asm)

Suma pozioma SIMD i dzielenie przez 4 (tj. Średnia arytmetyczna) jest zaimplementowana w osobnej funkcji, którą my call(ze wskaźnikiem funkcji amortyzującym koszt adresu). Z 4/xprzed / po, lub x^2przed i sqrt po, otrzymujemy średnią harmoniczną i kwadratową. (Bolesne było pisanie tych divinstrukcji zamiast mnożenia przez dokładnie reprezentowalne 0.25).

Średnia geometryczna jest implementowana osobno z pomnożonym i połączonym sqrt. Lub najpierw z jednym sqrt, aby zmniejszyć wielkość wykładnika i może pomóc w precyzji numerycznej. log nie jest dostępny, tylko floor(log2(x))przez AVX512 vgetexpps/pd. Exp jest w pewnym sensie dostępny przez AVX512ER (tylko Xeon Phi), ale z precyzją 2 ^ -23.

Mieszanie 128-bitowych instrukcji AVX i starszych SSE nie stanowi problemu z wydajnością. Mieszanie 256-bitowego AVX ze starszym SSE może odbywać się na Haswell, ale na Skylake potencjalnie tworzy potencjalną fałszywą zależność dla instrukcji SSE. Wydaje mi się, że moja doublewersja pozwala uniknąć niepotrzebnych łańcuchów deponowanych w pętli i wąskich gardeł w zakresie opóźnień / przepustowości div / sqrt.

Wersja podwójna:

   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

Uprząż testowa 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);
        }
    }

}

Kompiluj za pomocą:

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

Oczywiście potrzebujesz procesora z obsługą AVX lub emulatora takiego jak Intel SDE. Aby skompilować na hoście bez macierzystej obsługi AVX, użyj -march=sandybridgelub-mavx

Uruchom: przekazuje zakodowane na stałe przypadki testowe, ale w przypadku wersji zmiennoprzecinkowej przypadkowe przypadki testowe często nie spełniają (b-a)/10000wartości progowej określonej w pytaniu.

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

Błędy FP są wystarczające, aby poczwórne uszkodzenie wyszło mniej niż zero dla niektórych danych wejściowych.

Lub bez a += 1<<11; b += (1<<12)+1;komentarza:

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

Żaden z tych problemów nie występuje double. Skomentuj printfprzed każdym testem, aby zobaczyć, że wyjście jest puste (nic z if(delta too high)bloku).

DO ZROBIENIA: użyj doublewersji jako odniesienia do floatwersji, zamiast tylko patrzeć, jak zbiegają się z poczwórnym uszkodzeniem.


1

JavaScript - 186 bajtów

Pobiera dane wejściowe jako tablicę liczb. Wykorzystuje średnie transformacje w odpowiedzi J42161217, aby skrócić kod.

Wypróbuj 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]

Wyjaśnienie

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

Myślałem, że będę sprytny i wprowadzę relację z logarytmami, ale wygląda na to, że ty i J42161217 dotarliście tam pierwsi!
Pureferret

@Pureferret Nie biorę za to uznania, rażąco to ukradłem: D
asgallant

napisałeś to w JavaScript!
Pureferret

1
To była łatwa część. Gra w golfa była trudna.
asgallant

1
TIL nie został poprawnie skonfigurowany. Do odpowiedzi dodałem link TIL.
asgallant


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.