2つの数値の平均を計算する


41

免責事項:平均値は私によって構成されています

数値の 算術平均を 定義数値の 幾何平均を数値の 調和平均を数値の 二次平均をとして 定義する 平均値()は次のように定義されます:4つのシーケンス()を次のように定義します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、KBはKCのkはdはkは0=M1X1xはNak,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)
すべての4つの配列は、に収束します同じ番号。MM(x1,x2,...,xn)

1と2の平均は次のように計算されます: 次に シーケンスのさらなる計算は明確でなければなりません。それらは同じ数、約に収束することがます。

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

チャレンジ

2つの正の実数と()を与え、それらの平均値計算します。aba<bMM(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
すべてのテストケースのように、最初の入力が常に2番目の入力よりも小さいと仮定できますか?(そうでない場合は、Javaの回答をロールバックします。)
Kevin Cruijssen

回答:


14

Wolfram言語(Mathematica)、52バイト

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

オンラインでお試しください!

私の最初のアプローチでは、これらのビルトイン
Mean GeometricMean HarmonicMeanRootMeanSquare

バイトを保存するためのいくつかの代替があります

HarmonicMean-> 1/Mean[1/x] by @Robin Ryder(3バイト保存)
GeometricMean-> E^Mean@Log@xby @A。レックス(2バイト保存)
RootMeanSquare-> Mean[x^2]^.5by @A。レックス(4バイト保存)

最後MeanM(@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.レックス

@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バイト。

他のいくつかの答えと同様に、これは、対数スケールの算術平均として幾何平均の式を使用します(ここでは2を底とする):

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

また、、つまりという事実も使用します。したがって、条件は、と。これは、whileループで使用するものです。これは、条件がベクトルの場合に最初の要素のみを考慮する構文、つまり平均が格納される順序を乱用することで実現されます。(は4つの最小値であるため、代わりにを使用することもできますが、条件でまたはを使用することはできません。)k,dkakbkckdk=max(ak,bk,ck,dk)、K = B 、K = C K = D K D 、K = M 1k個のA k b kak=bk=ck=dkdk=M1(ak,bk,ck,dk)whileckakbk

whileループを終了するxと、定数ベクトルになります。最終版は?x、平均を計算してスカラーに減らします。


1
代わりにであってはいけませんか? l o glnxnlogxn
タウ

@Tauはい、自然対数をで示していました。これはRのデフォルトです。とにかく、-2バイトの2を底とする対数に変更しました。log
ロビンライダー

6

J、34バイト

(変数への割り当てのない式として31 f

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

オンラインでお試しください!

関数aおよびのb場合、a &.: b(「bの下のa」(関連するチャレンジ))は同等です(b inv) a b-bを適用し、次に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

入力は、の2つの整数のリストですAns
出力はAnsプログラムに保存され、プログラムが完了すると自動的に印刷されます。

幾何平均、調和平均、二次平均に使用される式は、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ֿ¹

√(、この 1バイトのトークンが。

ΔList(、この 2バイトのトークンが。


幾何平均を最後に置くことで括弧を節約できると思います。
ソロモンウッコ

@SolomonUckoああ、気づいてくれてありがとう!以前はそれを考慮しませんでした。
タウ

max(DeltaList(Ans-> variance(Ans
リルトシアスト

5

Java 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)合計が十分に小さいことを確認してから確認できますか?
ピーターコーデス

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時間の約2倍)、基本的に4 * d-D小さくするために繰り返しを行う必要があります。では1e-7、1e-8とほぼ同じ速度です。では1e-6、末尾の数字のいくつかは、一つのケースで異なります。
ピーターコーデス

1
@RobinRyderの答えは、二次平均が常に最大であり、調和が常に最小であるため、おそらくf完全に捨ててチェックすることしかできないことを指摘していますa[3]-a[2]<4e-9
ピーターコーデス

1
@PeterCordesのl==2||意味(ゴルフをするl<3|)。しかし、はい、良い点; 追加しました。:)
ケビン・クルーッセン

2
集約可能なレデューサーを集約することにより、180バイト
オリビエグレゴワール

3

、40バイト

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

オンラインでお試しください!リンクは、コードの詳細バージョンです。入力を数値の配列として受け取ります。説明:

W‹⌊θ⌈θ

配列に異なる値が含まれている間繰り返します...

≔⟦....⟧θ

...配列を値のリストに置き換えます:

∕ΣθLθ

... 平均...

XΠθ∕¹Lθ

...幾何平均...

∕LθΣ∕¹θ

...調和平均...

₂∕ΣXθ²Lθ

...および二乗平均平方根。

I⊟θ

配列の要素を文字列にキャストし、暗黙的に印刷します。




3

05AB1E26 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)}н
グリムミー

@Grimyありがとう!Y2/4の代わりに長さを使うことを考えていなかったとは信じられません。:)
ケビンクルーッセン

1
より良い別の23は、幾何平均と他の平均の類似性を示していますΔ©ÅA®.²ÅAo®zÅAz®nÅAt)}н。残念ながら、これらすべてをリファクタリングできるようには見えませんÅA
グリムミー

@Grimyああ、私はこの2番目のバージョンが好きです。:)編集:おっと。説明の間違いに気づいてくれてありがとう。..>。>
ケビン・クルーッセン

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つまり、バイトを消費せずに行を戻すことができますが、現在、それぞれのコアに算術平均があるという事実がとても気に入っています。
ニックケネディ

2

Python 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、浮動小数点の精度に収束します。原則として、あらゆるサイズの正数のすべてのリストで機能しますが、一部のテストケースでは、Pythonの再帰制限により丸め誤差が制限されます。


または、9桁の精度にセトリングします。

Python 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に変更できます。C#Visual Interactive Compilerはこれらのインポートを必要としません。また、1.0->1d
無知の具体化


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平均(調和平均)が異なること、両方の場合にメモ。

この問題がエントリを無効にするかどうかはわかりませんが、動作するはずなので、とにかく投稿しています。これが受け入れられない場合、このテストスイートに見られるように、の.RRT前にインスタリングして[各平均値を小数点以下10桁に丸めることで修正できます

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

C#(Visual C#Interactive Compiler)、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バイトになります。

オンラインでお試しください!


最後の2つのテストケースは、StackOverflowException浮動小数点の精度に起因します。c==g[0]あなたの代わりに何かをすることができましたMath.Abs(c-g[0])<1e-9オンラインでお試しください。
ケビンクルーッセン

@KevinCruijssenおかげで、浮動小数点数を扱うのはそんなに苦痛です
無知の

1

x86マシンコード(128ビットSSE1&AVXを使用したSIMD 4x float)94バイト

x86マシンコード(256ビットAVXを使用したSIMD 4xダブル)123バイト

float問題のテストケースに合格しますが、それを実現するのに十分なループ終了しきい値を使用すると、ランダム入力の無限ループに陥りやすくなります。

SSE1パック単精度命令の長さは3バイトですが、SSE2および単純なAVX命令の長さは4バイトです。(スカラー-シングル命令のようなsqrtssものも4バイトの長さsqrtpsです。そのため、low要素だけを気にしているのに使用します。最新のハードウェアのsqrtssよりも遅くありません)。非破壊的な宛先にAVXを使用して、movaps + opに対して2バイトを節約しました。
ダブルバージョンでは、movlhps64ビットチャンクをコピーするためにカップルを行うことができます(多くの場合、水平方向の合計の低い要素のみを考慮するため)。256ビットSIMDベクトルの水平方向の合計はまた、余分に必要とvextractf128対、高の半分を取得するために、ゆっくりではあるが、小さな2倍のhaddpsフロートのための戦略。のdoubleバージョンには、2バイトの4バイトではなく、2バイトの8バイト定数も必要です。全体的には、floatバージョンのサイズの4/3近くになります。

mean(a,b) = mean(a,a,b,b)これらの4つの手段すべてについて、入力を4つの要素まで単純に複製でき、length = 2を実装する必要はありません。したがって、幾何平均を4th-root = sqrt(sqrt)などとしてハードコーディングできます。そして、必要なFP定数は1つだけです4.0

4つすべての単一のSIMDベクトルがあります[a_i, b_i, c_i, d_i]。それから、4つの平均を別々のレジスタのスカラーとして計算し、次の反復のためにそれらを一緒にシャッフルします。 (SIMDベクトルの水平方向の操作は不便ですが、バランスをとるのに十分な場合には4つの要素すべてに対して同じことを行う必要があります。

のループ終了条件}while(quadratic - harmonic > 4e-5)(またはのより小さい定数doubleは、@ RobinRyderのR回答、およびKevin CruijssenのJava回答に基づいています:二次平均は常に最大の大きさであり、調和平均は常に最小です(丸め誤差を無視)。したがって、これら2つの間のデルタをチェックして、収束を検出できます。算術平均をスカラー結果として返します。通常はこの2つの間にあり、おそらく丸め誤差の影響を受けにくいものです。

浮動バージョンfloat meanmean_float_avx(__m128);argと同様に呼び出し可能で、xmm0の値を返します。(つまり、x86-64 System V、またはWindows x64 vectorcallで、x64 fastcallではありません。)または、return-typeを宣言して、__m128テスト用に2次平均と調和平均を取得できるようにします。

これfloatにxmm0とxmm1の2つの個別の引数を使用すると、1バイト余分にコストがかかります。一緒にシャッフルして2つの入力を複製するshufpsには、(ちょうどの代わりにunpcklps xmm0,xmm0)imm8 が必要です。

    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

(で作成されたNASMリスト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バージョンでは、不必要なループ搬送depチェーン、および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).

これらの問題はいずれもで発生しませんdoubleprintf各テスト の前にコメントアウトして、出力が空であることを確認します(if(delta too high)ブロックからは何もありません)。

TODO:単にクワッドハームとの収束方法を調べるのではなくdoublefloatバージョンのリファレンスとしてバージョンを使用します。


1

Javascript-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
15

あなたはJavaScriptでそれを書いた!
Pureferret

1
それは簡単な部分でした。ゴルフは大変でした。
アスガルント

1
TILが正しく構成されていません。TILリンクを回答に追加しました。
アスガルント


0

SNOBOL4(CSNOBOL4)、296バイト

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

オンラインでお試しください!

簡単な実装。ゴルフに関連する質問への私の答えからのトリックをもう少し使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.