计算两个数字的均值


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
MMak,bk,ck,dk
a0=M1(x1,...,xn),b0=M0(x1,...,xn),c0=M1(x1,...,xn),d0=M2(x1,...,xn),ak+1=M1(ak,bk,ck,dk),bk+1=M0(ak,bk,ck,dk),ck+1=M1(ak,bk,ck,dk),dk+1=M2(ak,bk,ck,dk)
所有四个序列收敛到相同的数字。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.
开头,b_0 = \ root \ of {1 * 2} = \ root \ of2 \ approx 1.4142,\\ c_0 = \ frac2 {\ frac1 {1} + \ frac1 {2}} = \ frac4 {3} \ approx 1.3333,d_0 = \ root \ of {\ frac {1 ^ 2 + 2 ^ 2} 2} = \ root \ of {\ frac5 {2}} \约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的相同数字。

挑战

给定两个正实数aba<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,

Answers:


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] 通过@Robin Ryder(节省3个字节)
GeometricMean-> E^Mean@Log@x通过@A。雷克斯(保存2个字节)
RootMeanSquare-> Mean[x^2]^.5通过@A。雷克斯(保存4个字节)

最后,我们可以分配MeanM(由@ovs提议)并保存另外5个字节


通过重新编码GeometricMean 节省2个字节
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字节

像其他一些答案一样,它使用几何平均值的表达作为对数刻度上的算术平均值(此处为基数2):

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

它还使用事实即,即。因此条件等于,这是我在while循环中使用的条件;这是通过滥用语法来实现的,该语法仅在条件为向量时才考虑第一个元素,因此不存储均值的顺序。(请注意,我们也可以使用因为它是这四个中的最小值,但是在这种情况下我们不能使用或。)k,dkakbkckdk=max(ak,bk,ck,dk)ak=bk=ck=dkdk=M1(ak,bk,ck,dk)whileckakbk

当我们退出while循环时,它x是一个常数向量。最终?x将计算其均值以将其减少为标量。


1
它不是而不是吗? l o g x nlnxnlogxn
陶尔

@Tau是的,我用表示自然对,这是R中的默认值。无论如何,我现在将其更改为以-2字节为基数的2对数。log
罗宾·赖德

6

J,34个字节

(31作为不分配变量的表达式f

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

在线尝试!

对于函数aba &.: b(“ a under b”(相关挑战))等效于(b inv) a b-应用b,然后应用a,然后求b的逆。在这种情况下,几何/谐波/二次平均分别是对数,反演和平方的“低于”算术平均值。


5

TI-BASIC,42 35 34字节

-1字节感谢@SolomonUcko

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

输入是中的两个整数的列表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之所以选择编写,是因为令牌看起来像ֿ¹在代码块中一样。

√(这个一字节的令牌。

ΔList(两个字节的令牌。


我认为您可以通过将几何平均值放在最后来节省括号。
所罗门·乌科

@SolomonUcko啊,感谢大家的注意!以前没有考虑过。

max(DeltaList(Ans-> variance(Ans
lirtosiast

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

-5个字节,感谢@PeterCordes
由于@PeterCordes的启发,多了-15个字节,这受@RobinRyder的R答案的启发。
+4个字节,因为我假设输入已预先排序。
-27个字节,感谢@OlivierGrégoire

在线尝试。

说明:

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时间的两倍),必须进行更多的迭代才能d-D使实际尺寸减少4倍。使用1e-7,它的速度与1e-8相同。使用1e-6,某些情况下的某些尾随数字会有所不同。
Peter Cordes

1
@RobinRyder的答案指出,二次均值始终是最大的,谐波总是最小的,因此也许您可以f完全放弃并且仅检查a[3]-a[2]<4e-9
Peter Cordes

1
@PeterCordes l==2||你的意思(golfed到l<3|)。是的,很好。我加了 :)
Kevin Cruijssen

2
通过聚集可聚合的reducer的180字节
OlivierGrégoire

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

在线尝试查看所有测试用例的步骤

-1个字节感谢@Grimy

几何均值的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)}н
Grimmy

@肮脏的谢谢!不敢相信我没有考虑使用长度而不是Y2/4。:)
Kevin Cruijssen

1
更好的另一个23显示了几何均值与其他均值的相似性:Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н。不幸的是,看起来我们无法重构所有这些ÅAs。
Grimmy

@肮脏的哦,我喜欢第二版。:)编辑:糟糕..谢谢您注意到我在解释中的错误。
>>>

我对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

我对Jelly的表现很不好,但是可以用类似的方法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 Systemusing 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]

在线尝试!

执行操作直到结果停止更改。

万岁为有限的精度浮点!


1

Pyth,32个字节

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

在线试用这里,或一次验证所有的测试用例(栏中有两个,见下面注)在这里。接受输入作为列表。

四舍五入似乎存在一些问题,因为某些输入本来不应该正确收敛。特别是,测试用例0.01 100卡在值上[6.748305820749738, 6.748305820749738, 6.748305820749739, 6.748305820749738],而测试用例卡在值1.61 2.41[1.9896543776640825, 1.9896543776640825, 1.9896543776640827, 1.9896543776640825]-请注意,在两种情况下,第三个均值(谐波均值)均与其他均值不同。

我不确定这个问题是否会使我的输入无效,但是无论如何我都会发布它,因为它应该可以正常工作。如果这是不可接受的,则可以通过.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{H使用u了-4字节(和更改ZG
ar4093


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由于浮点精度,最后两个测试用例给出了a 。相反,c==g[0]您可以做类似的事情Math.Abs(c-g[0])<1e-9在线尝试。
凯文·克鲁伊森

@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个字节。
在double版本中,我们仍然可以做一些movlhps复制64位块的操作(因为通常我们只关心水平和的低位元素)。256位SIMD向量的水平和还需要额外的费用vextractf128才能获得高一半,而对于float则需要缓慢但较小的2x haddps策略。的double版本还需要2x 8字节常量,而不是2x 4字节常量。总体来说,它的大小接近该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答案Kevin Cruijssen的Java答案:二次均值始终是最大量级,谐波均值始终是最小量(忽略舍入误差)。因此,我们可以检查这两者之间的差异以检测收敛。我们将算术平均值作为标量结果返回。它通常在这两者之间,并且最不容易出现舍入错误。

浮动版本float meanmean_float_avx(__m128);与arg 一样可调用,并在xmm0中返回值。(因此x86-64 System V或Windows x64 vectorcall,而不是x64 fastcall。)或声明return类型,__m128以便可以得到二次和谐波均值进行测试。

float让它在xmm0和xmm1中使用2个单独的args会多花1个字节:我们需要一个shufps带有imm8(而不是unpcklps xmm0,xmm0)的shuffle在一起并复制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

(使用创建的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来减小指数幅度并可能有助于数值精度。日志仅floor(log2(x))通过AVX512 不可用vgetexpps/pd。可以通过AVX512ER(仅至强融核)使用Exp,但精度仅为2 ^ -23。

混合使用128位AVX指令和旧版SSE并不是性能问题。可以在Haswell上混合使用256位AVX和旧版SSE,但是在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错误足以使某些输入的四次伤害小于零。

或未加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

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
令人震惊的

您是用JavaScript编写的!
Pureferret

1
那是容易的部分。打高尔夫球很难。
asgallant

1
TIL配置不正确。我在答案中添加了TIL链接。
asgallant


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

在线尝试!

简单实施。在我对一个相关问题的回答中运用技巧来打更多的高尔夫球。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.