두 숫자의 평균 평균을 계산


41

면책 조항 : 평균 평균은 나에 의해 구성됩니다

산술 평균 정의 n 과 같은 숫자

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
평균 평균 (MM이하로) 정의된다 네 서열 (정의K,B의K,C에서K,D(k)를같은)0=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)
네 시퀀스가 동일한 번호로 수렴,MM(x1,x2,...,xn) .

아래와 같이 1 및 2의 평균은 평균 계산된다 : 시작 0 = ( 1 + 2 ) / 2 = 1.5 , B 0 =

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와 같은 수로 수렴한다는 것을 알 수 있습니다.

도전

양수 실수 ab ( a<b )가 주어지면 평균 평균 MM(a,b) .

테스트 사례

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

노트

  • 출력과 올바른 출력의 차이가 입력 숫자 차이의 절대 값의 1/100000보다 크지 않으면 프로그램이 유효합니다.
  • 출력은 단일 숫자 여야합니다.

이것은 이므로 가장 짧은 코드가 승리합니다!




11
우리는 얼마나 정확해야합니까?
무지의 구현


1
모든 테스트 사례에서와 같이 첫 번째 입력이 항상 두 번째 입력보다 작다고 가정 할 수 있습니까? (그렇지 않으면 Java 응답을 롤백 할 것입니다.)
Kevin Cruijssen

답변:


14

Wolfram Language (Mathematica) , 52 바이트

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

온라인으로 사용해보십시오!

첫 번째 접근 방식에서 나는이 내장을 사용
Mean GeometricMean HarmonicMean했고RootMeanSquare

다음은 바이트 절약을위한 대체 방법입니다.

HarmonicMean-> 1/Mean[1/x] @Robin Ryder (3 바이트 저장)
GeometricMean-> E^Mean@Log@x@A. 렉스 (2 바이트 저장)
RootMeanSquare-> Mean[x^2]^.5@A. 렉스 (4 바이트 저장)

마지막 Mean으로 M(@ovs에서 제안한대로) 할당 하고 5 바이트를 더 절약 할 수 있습니다.


GeometricMean
Robin Ryder

@RobinRyder 나는 당신이 고조파를 의미한다고 생각합니다 .. 좋은!
J42161217

1
8 바이트 더 절약 :#//.x_:>N@{Mean@x,E^Mean@Log@x,1/Mean[1/x],Mean[x^2]^.5}&
A. Rex

@ovs 편집 .....
J42161217

10

R, 70 69 67 바이트

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

온라인으로 사용해보십시오!

더 나은 컨디셔닝으로 -1 바이트.
기본 2로 전환하여 -2 바이트

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

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

while 루프를 종료하면 x상수 벡터가됩니다. 마지막 ?x은 평균을 스칼라로 줄이기 위해 평균을 계산합니다.


1
lnxnlogxn

log

6

J , 34 바이트

(변수에 할당되지 않은 표현식으로 31 f)

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

온라인으로 사용해보십시오!

함수 ab, a &.: b( "a under b"( 관련 시도 ))는 (b inv) a bb를 적용한 다음 a를 적용한 다음 b의 역함수와 같습니다. 이 경우 기하 / 고조파 / 이차 평균은 각각 산술 평균 "밑", 대수 및 제곱입니다.


5

TI-BASIC, 42 35 34 바이트

@SolomonUcko 덕분에 -1 바이트

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

입력은의 두 정수 목록입니다 Ans.
출력은Ans프로그램이 완료되면 자동으로 인쇄됩니다.

기하, 고조파 및 2 차 수단에 사용되는 공식은 user202729의 설명을 기반으로합니다 .

예:

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

설명 :
(명확하게하기 위해 줄 바꿈이 추가되었습니다. 코드에는 나타나지 않습니다 .)

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

노트:

TI-BASIC은 토큰 화 된 언어입니다. 문자 수는 바이트 수와 같지 않습니다 .

e^( 1 바이트 토큰.

^-1사용되는 이러한 하나의 바이트의 토큰. 코드 블록에있을 때 토큰처럼 보이기 때문에 대신
쓰기 ^-1를 선택했습니다 ֿ¹.

√( 1 바이트 토큰.

ΔList( 2 바이트 토큰.


기하학적 평균을 마지막에 넣어서 괄호를 절약 할 수 있다고 생각합니다.
Solomon Ucko

@SolomonUcko 아, 주목 해 주셔서 감사합니다! 전에는 고려하지 않았습니다.
타우

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

5

자바 10 234 229 214 211 215 206 203 196 180 177 바이트

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];}

@PeterCordes 덕분에 -5 바이트 . @RobinRyder 의 R 답변 에서 영감을 얻은 @PeterCordes
덕분에 -15 바이트 더 바이트 . 입력이 사전 주문되었다고 가정했기 때문에 +4 바이트. @ OlivierGrégoire 덕분에 -27 바이트 .

온라인으로 사용해보십시오.

설명:

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

C에서는 f+=Math.abs(d-D)<1e-9;부울 비교 결과에서 0/1 정수로 암시 적으로 변환 한 다음 얻을 수 double있습니다. Java에 대한 간단한 구문이 있습니까? 아니면 절대 차이 f+=Math.abs(d-D) 이 충분히 작은 지 확인하고 할 수 있습니까?
Peter Cordes

1
테스트 사례의 경우 f>1e-8루프 조건 (229 바이트)으로 작동합니다. 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];}. 를 사용하면 1e-9느리게 (CPU 시간의 약 두 배) 실행되므로 기본적으로 4 * d-D줄어 듭니다. 으로 1e-7, 그것은 1E-8와 같은 속도에 관하여이다. 을 사용 1e-6하면 일부 후행 숫자 중 일부가 다릅니다.
Peter Cordes

1
@RobinRyder의 답변에 따르면 이차 평균은 항상 가장 크며 고조파는 항상 가장 작으므로 f완전히 도랑을 풀 수 있습니다 a[3]-a[2]<4e-9.
Peter Cordes

1
@PeterCordes는 l==2||당신에게 (골프 l<3|)합니다. 그러나 그렇습니다. 좋은 지적입니다. 나는 그것을 추가했다. :)
Kevin Cruijssen

2
집계 가능한 감속기를 집계하여 180 바이트
Olivier Grégoire

3

, 40 바이트

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

온라인으로 사용해보십시오! 링크는 자세한 버전의 코드입니다. 숫자 배열로 입력을받습니다. 설명:

W‹⌊θ⌈θ

배열에 다른 값이 포함되어있는 동안 반복하십시오 ...

≔⟦....⟧θ

... 배열을 값 목록으로 바꿉니다.

∕ΣθLθ

... 평균...

XΠθ∕¹Lθ

... 기하학적 평균 ...

∕LθΣ∕¹θ

... 고조파 평균 ...

₂∕ΣXθ²Lθ

...와 제곱 평균 제곱입니다.

I⊟θ

배열의 요소를 문자열로 캐스트하고 내재적으로 인쇄하십시오.




3

05AB1E , 26 24 23 바이트

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

온라인으로 시도 하거나 모든 테스트 사례의 단계를 참조하십시오 .

@Grimy 덕분에 -1 바이트 .

기하 평균에 대한 23 바이트 대안 :

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

온라인으로 시도 하거나 모든 테스트 사례의 단계를 참조하십시오 .

설명:

Δ         # 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)}н
그리미

트윗 담아 가기 Y2/4 대신 길이를 사용하는 것에 대해 생각하지 않았다는 것을 믿을 수 없습니다 . :)
Kevin Cruijssen

1
더 나은 또 다른 23은 다른 것들과 기하학적 평균의 유사성을 보여줍니다 Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н. 불행히도 우리가 모든 것을 리팩토링 할 수있는 것처럼 보이지 않습니다 ÅA.
그리미

@ 그림 오, 나는이 두 번째 버전을 좋아한다. :) 편집 : 죄송합니다. 설명에 내 실수를 지적 해 주셔서 감사합니다 ..>.>
Kevin Cruijssen

05ab1e에서 잘 프로그래밍하지는 않지만 합계를 계산 한 다음 나중에 길이로 나눌 수 있습니까?
누군가

2

젤리 , 25 24 바이트

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

온라인으로 사용해보십시오!

설명

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

나는 젤리에 상당히 나쁘지만 P*İL기하학적 의미 에서 작동하는 것과 비슷한 것이 있습니까?
누군가

@someone은 P*Lİ$바이트를 저장하지 않도록해야합니다. 그것은 Æm바이트 비용을 들이지 않고 줄을 되돌릴 수 있음을 의미 하지만, 각각이 현재 핵심에 산술 평균을 가지고 있다는 사실을 아주 좋아합니다.
Nick Kennedy

2

파이썬 3 , 152 바이트

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)

온라인으로 사용해보십시오!

재귀 함수 f는 부동 소수점 정밀도로 수렴됩니다. 원칙적으로 모든 크기 의 모든 양수 목록에 대해 작동 하지만 일부 테스트 사례의 경우 파이썬의 재귀 제한 반올림 오류로 제한 됩니다.


또는 9 자리 정밀도로 정착합니다.

파이썬 3 , 169 바이트

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)

온라인으로 사용해보십시오!


1

C # , 173 바이트

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

온라인으로 사용해보십시오!


2
이것은 실제로 전달되어야하는 변수에있는 것 같습니다. 또한, 포함해야 using System하고 using System.Linq그들이 실행할 수있는 프로그램이 필요하기 때문에, 당신의 바이트 수에. 컴파일러를 C # Visual Interactive Compiler로 변경할 수 있으며 가져 오기가 필요하지 않습니다. 또한 1.0->1d
무지의 구현

1

클린 , 124 바이트

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]

온라인으로 사용해보십시오!

결과 변경이 멈출 때까지 작업을 수행합니다.

제한된 정밀 부동 소수점을위한 Hurray!


1

Pyth, 32 바이트

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

여기 에서 온라인으로 시도 하거나 모든 테스트 사례 (2 번, 아래 참고 참조)를 한 번에 확인 하십시오 . 입력을 목록으로 허용합니다.

반올림에 문제가있는 것 같습니다. 특정 입력이 그렇지 않으면 올바르게 수렴되지 않기 때문입니다. 특히, 테스트 케이스 0.01 100는 값이 고착 [6.748305820749738, 6.748305820749738, 6.748305820749739, 6.748305820749738]되고 테스트 케이스 1.61 2.41는 고착 [1.9896543776640825, 1.9896543776640825, 1.9896543776640827, 1.9896543776640825]되어 있습니다. 두 경우 모두 3 차 평균 (고조파 평균)이 서로 다릅니다.

나는 확실하지이 문제가 내 항목을 무효화하는 경우,하지만 난 그것으로 어쨌든 그것을 게시하도록하겠습니다 해야 작동합니다. 이것이 허용되지 않는 경우 , 이 테스트 스위트 에서 볼 수 있듯이, 각 수단을 소수점 이하 10 자리로 반올림 .RRT하기 위해를 앞에 배치하여 수정할 수 있습니다 .[

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

나는 확신 반복 계산이 이전 값으로 움직이지 않을 것이다거야 때문에, 당신은 대체 할 수 .Wt{Hu대한 -4 바이트 (변화 ZG)
ar4093

1

Japt v2.0a0 -g, 42 38 바이트

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

더 짧은 방법이 있어야합니다 ... 이것은 괴물입니다! @Shaggy 덕분에 4 바이트가 절약되었습니다!

시도 해봐


38 바이트 . 그러나 나는 더 짧은 방법이 있어야한다는 것에 동의한다!
Shaggy

1

C # (Visual C # 대화 형 컴파일러) , 177 바이트

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;

부동 소수점 정밀도를 사용하는 데 문제가 있음을 지적한 @KevinCruijjsen에게 감사합니다! double이 완벽하게 정확하면 163 바이트입니다.

온라인으로 사용해보십시오!


마지막 두 테스트 사례는 StackOverflowException부동 소수점 정밀도로 인한 것입니다. 대신 c==g[0]당신은 같은 것을 할 수 Math.Abs(c-g[0])<1e-9있습니다. 온라인으로 사용해보십시오.
Kevin Cruijssen

@KevinCruijssen 감사합니다, 그것은 부동 소수점 숫자를 다루는 고통입니다
무지의 구현

1

x86 머신 코드 (128 비트 SSE1 & AVX를 사용한 SIMD 4x 플로트) 94 바이트

x86 머신 코드 (256 비트 AVX를 사용하는 SIMD 4x 이중) 123 바이트

float문제의 테스트 사례를 통과하지만 루프 종료 임계 값이 충분히 작아서 임의의 입력으로 무한 루프에 빠지기 쉽습니다.

SSE1 묶음 단 정밀도 명령어의 길이는 3 바이트이지만 SSE2와 간단한 AVX 명령어의 길이는 4 바이트입니다. (같은 스칼라 단일 명령의 sqrtss길이도 4 바이트이므로 sqrtps낮은 요소 만 신경 쓰더라도 사용합니다. 최신 하드웨어의 sqrtss보다 느리지 않습니다). 비파괴 대상으로 AVX를 사용하여 movaps + op에 비해 2 바이트를 절약했습니다.
이중 버전에서는 movlhps64 비트 청크를 복사하기 위해 여전히 몇 가지 작업을 수행 할 수 있습니다 (종종 수평 합의 하위 요소 만 고려하기 때문에). 256 비트 SIMD 벡터의 수평 합은 또한 floatvextractf128느리지 만 작은 2 배 haddps전략에 비해 높은 절반을 얻는 데 추가 가 필요합니다 . 그만큼double버전에는 2x 4 바이트 대신 2x 8 바이트 상수가 필요합니다. 전체적으로 float버전 크기의 4/3에 가깝습니다 .

mean(a,b) = mean(a,a,b,b)이 4 가지 방법 모두에 대해 입력을 4 개까지 간단히 복제 할 수 있으며 length = 2를 구현할 필요가 없습니다. 따라서 우리는 예를 들어 기하 평균을 4th-root = sqrt (sqrt)로 하드 코딩 할 수 있습니다. 그리고 하나의 FP 상수 만 필요합니다 4.0.

우리는 모두 4의 단일 SIMD 벡터를 가지고 있습니다 [a_i, b_i, c_i, d_i]. 이로부터 우리는 4 개의 평균을 별도의 레지스터에서 스칼라로 계산하고 다음 반복을 위해 다시 섞습니다. (SIMD 벡터에 대한 수평 작업은 불편하지만, 균형을 잡을 수있는 충분한 경우에 4 가지 요소 모두에 대해 동일한 작업을 수행해야합니다. x87 버전으로 시작했지만 시간이 오래 걸리고 재미 있지 않았습니다.)

의 루프 종료 조건 }while(quadratic - harmonic > 4e-5)(또는보다 작은 상수 double)@RobinRyder의 R answerKevin Cruijssen의 Java 답변을 기반으로합니다 . 2 차 평균은 항상 가장 큰 크기이며 고조파 평균은 항상 가장 작습니다 (반올림 오류 무시). 수렴을 감지하기 위해이 둘 사이의 델타를 확인할 수 있습니다. 산술 평균을 스칼라 결과로 반환합니다. 일반적으로 그 둘 사이에 있으며 반올림 오류에 가장 취약합니다.

부동 버전 : float meanmean_float_avx(__m128);arg와 마찬가지로 호출 가능하며 xmm0의 반환 값. (따라서 x86-64 System V 또는 Windows x64 벡터 호출이지만 x64 빠른 호출은 아닙니다.) 또는 return-type을 다음과 같이 선언하십시오.__m128 하여 2 차 및 고조파 평균을 테스트 할 수 있도록합니다.

이 테이크 2 개 분리시키는 floatXMM0와 XMM1에 인수하는 것은 1 개의 여분 바이트를 요할 것입니다 : 우리는 필요한 것 shufps(대신의 IMM8와 unpcklps xmm0,xmm0함께 셔플 2 개 입력을 중복).

    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

(으로 작성된 NAS 리스팅 nasm -felf64 mean-mean.asm -l/dev/stdout | cut -b -34,$((34+6))-. 리스팅 부분을 제거하고cut -b 34- > mean-mean.asm )

SIMD 수평 합과 4로 나누기 (즉, 산술 평균)는 별도의 함수 call(주소의 비용을 상각하기위한 함수 포인터 )로 구현됩니다 . 와 4/x전 / 후 또는 x^2이전과 SQRT 후, 우리는 조화 평균과 차 평균을 얻는다. ( div정확하게 표현할 수있는 것을 곱하는 대신 이 지시 사항 을 작성하는 것은 고통 스러웠다0.25 .)

기하 평균은 곱셈 및 연쇄 sqrt로 별도로 구현됩니다. 또는 1 sqrt를 먼저 사용하여 지수 크기를 줄이고 숫자 정밀도를 도울 수 있습니다. floor(log2(x))AVX512를 통해서만 로그를 사용할 수 없습니다 vgetexpps/pd. Exp는 AVX512ER (Xeon Phi 만 해당)을 통해 사용할 수 있지만 정확도는 2 ^ -23입니다.

128 비트 AVX 명령어와 레거시 SSE를 혼합하는 것은 성능 문제가 아닙니다. 256 비트 AVX와 레거시 SSE를 혼합하는 작업은 Haswell에있을 수 있지만 Skylake에서는 SSE 명령에 대한 잠재적 인 잘못된 종속성을 만들 수 있습니다. 내 double버전은 불필요한 루프 전송 딥 체인과 div / sqrt 대기 시간 / 처리량의 병목 현상을 피 한다고 생각 합니다.

이중 버전 :

   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

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

}

로 빌드 :

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

AVX를 지원하는 CPU 또는 Intel SDE와 같은 에뮬레이터가 필요합니다. 기본 AVX 지원없이 호스트에서 컴파일하려면 -march=sandybridge또는-mavx

실행 : 하드 코딩 된 테스트 사례를 통과하지만 플로트 버전의 경우 임의 테스트 사례가 종종 (b-a)/10000질문에 설정된 임계 값에 실패합니다 .

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

FP 오류는 일부 입력에서 쿼드 하모가 0보다 작게 나오기에 충분합니다.

또는 a += 1<<11; b += (1<<12)+1;주석 처리되지 않은 상태 :

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

이 문제들 중 어떤 것도 발생하지 않습니다 double. printf각 테스트 전에 주석 처리 하여 출력이 비어 있는지 확인하십시오.if(delta too high) 블록 ).

TODO : double버전 float이 쿼드 하마와 수렴하는 방식을 보는 대신 버전을 참조로 사용하십시오 .


1

자바 스크립트-186 바이트

숫자 배열로 입력을받습니다. J42161217의 답변 에서 평균 변환을 사용하여 코드를 줄입니다.

온라인으로 사용해보십시오

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]

설명

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

나는 영리하고 로그와관계를 구현할 것이라고 생각 했지만 J42161217이 먼저 도착한 것처럼 보입니다!
Pureferret

내가 그에 대한 신용을하지 @Pureferret, 나는 노골적으로 훔친 : D
asgallant

그래도 JavaScript로 작성했습니다!
Pureferret

1
그것은 쉬운 부분이었습니다. 골프는 힘들었다.
asgallant

1
TIL이 올바르게 구성되지 않았습니다. 답변에 TIL 링크를 추가했습니다.
asgallant


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.