Обчисліть середнє середнє значення двох чисел


41

відмова від відповідальності: середнє значення складено мною

Визначте середнє арифметичне чисел як Визначте середнє геометричне значення чисел як Визначте гармонічне середнє число чисел як Визначте квадратичне середнє число чисел як M_2 (x_1, ..., x_n) = \ root \ of {\ frac {x_1 ^ 2 + x_2 ^ 2 + ... + x_n ^ 2} {n}} Середнє середнє значення ( M_M ) визначається так: Визначте чотири послідовності ( a_k, b_k, c_k, d_k ) як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до,Ьдо,здо,dKз0=М1(х1,...,хп),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 обчислюється так: починаємо з

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 .

Виклик

Давши два додатних дійсних числа, a і b ( 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
Чи можемо ми припустити, що перший вхід завжди менший, ніж другий, як у всіх ваших тестових випадках? (Якщо ні, я
відкажу

Відповіді:


14

Мова Вольфрама (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 байт


Збережіть 2 байти , перекодувавши GeometricMean
Робін Райдер

@RobinRyder Я вважаю, ти маєш на увазі гармонійне .. приємно!
J42161217

1
Збережіть ще 8 байт :#//.x_:>N@{Mean@x,E^Mean@Log@x,1/Mean[1/x],Mean[x^2]^.5}&
А. Рекс

@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 Так, я позначав природний логарифм , що є за замовчуванням у Р. Як би там не було, я тепер змінив його на 2 логарифми на -2 байти. log
Робін Райдер

6

J , 34 байти

(31 як вираз без присвоєння змінній f)

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

Спробуйте в Інтернеті!

Для функцій aі b, a &.: b("a під 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і автоматично роздруковується після завершення програми.

Формули, які використовуються для геометричних, гармонічних та квадратичних засобів, виходячи з пояснення користувача 202729 .

Приклад:

{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 .
-15 ще байт завдяки @PeterCordes , натхнених @RobinRyder R відповіді «s .
+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цим він працює повільніше (приблизно вдвічі більше часу процесора), виконуючи більше ітерацій, щоб отримати 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

05AB1E , 26 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)}н
Гримі

@Grimy Дякую! Не можу повірити, що я не думав про використання довжини замість Y2/4. :)
Кевін Круїйсен

1
Ще 23 , що вищі показує схожість середнього геометричного до інших з них: Δ©ÅA®.²ÅAo®zÅAz®nÅAt)}н. На жаль, це не виглядає так, що ми можемо переробити всі ці ÅA.
Grimmy

@Grimy О, мені подобається ця друга версія. :) EDIT: На жаль .. дякую, що помітили мою помилку в поясненні ..>.>
Кевін Круїйсен

Я не програмую на 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працювати для геометричного значення?
хтось

@ хтось це повинен бути, 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 десяткових знаків:

Пітон 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]

Спробуйте в Інтернеті!

Виконує операцію, поки результат не перестане змінюватися.

Ура для плаваючої точки з обмеженою точністю!


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перед значком [, щоб округлити кожне із засобів до 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{Hз uна -4 байт (і зміна Zдо G)
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 за те, що вказав, що використання точності з плаваючою комою спричиняло проблеми! Було б 163 байти, якби парні були ідеально точними.

Спробуйте в Інтернеті!


Два останні тестові випадки StackOverflowExceptionобумовлені точністю з плаваючою точкою. Замість c==g[0]вас можна було б зробити щось на кшталт Math.Abs(c-g[0])<1e-9. Спробуйте в Інтернеті.
Кевін Круїссен

@KevinCruijssen Спасибі, такий біль стосується цифр із плаваючою комою
Втілення Невігластва

1

машинний код x86 (SIMD 4x float з використанням 128-бітних SSE1 та AVX) 94 байт

машинний код x86 (SIMD 4x подвійний з використанням 256-бітного AVX) 123 байтів

floatпередає тестові випадки у запитанні, але з порогом виходу з циклу, достатньо малим для того, щоб це відбулося, його легко застрягти у нескінченному циклі зі випадковими введеннями.

Інструкції з упаковкою SSE1 з одноточністю мають 3 байти, а SSE2 та прості інструкції AVX - 4 байти. (Скалярні інструкції на зразок sqrtssтакож мають 4 байти, тому я використовую, sqrtpsхоча я дбаю лише про низький елемент. Це навіть не повільніше, ніж sqrtss на сучасному обладнанні). Я використовував AVX для неруйнівного призначення, щоб зберегти 2 байти проти movaps + op.
У подвійній версії ми все ще можемо зробити пару, movlhpsщоб скопіювати 64-бітні шматки (адже часто нас хвилює лише низький елемент горизонтальної суми). Горизонтальна сума 256-бітного SIMD-вектора також потребує додаткової, vextractf128щоб отримати високу половину, порівняно з повільною, але невеликою haddpsстратегією 2x для плавання . ThedoubleВерсія також потребує 2x 8-байтових констант замість 2x 4-байтових. В цілому за розміром floatверсії він виходить майже на 4/3 .

mean(a,b) = mean(a,a,b,b)для всіх 4 цих засобів , тому ми можемо просто дублювати вхід до 4 елементів і ніколи не потрібно реалізовувати length = 2. Таким чином, ми можемо твердо кодувати середнє геометричне, наприклад, четвертий-root = sqrt (sqrt). І нам потрібно тільки одну константи FP, 4.0.

У нас є один SIMD-вектор з усіх 4 [a_i, b_i, c_i, d_i]. Виходячи з цього, ми обчислюємо 4 засоби як скаляри в окремі регістри та переміщуємо їх назад для наступної ітерації. (Горизонтальні операції на векторах SIMD незручні, але нам потрібно зробити те ж саме для всіх 4 елементів у достатній кількості випадків, коли він врівноважується. Я почав на версії x87 цього, але він ставав дуже довгим і не веселим.)

Умова виходу з циклу }while(quadratic - harmonic > 4e-5)(або менша константа для double) базується на R-відповіді @ RobinRyder , і відповіді Java Кевіна Круїссена : квадратичне середнє значення завжди є найбільшою величиною, а середнє гармонічне значення завжди найменше (ігноруючи помилки округлення). Тож ми можемо перевірити дельту між цими двома, щоб виявити конвергенцію. Повертаємо середнє арифметичне як скалярний результат. Зазвичай це між цими двома і, мабуть, найменш схильний до помилок округлення.

Версіяfloat meanmean_float_avx(__m128); з плаваючою можливістю: викликається як з аргументом і значенням повернення у xmm0. (Отже, x86-64 System V, або Windows x64 vectorcall, але не x64 fastcall.) Або оголосити тип повернення, __m128щоб ви могли отримати квадратичну та гармонійну середню для тестування.

Якщо це займе 2 окремих floatаргументи в xmm0 та xmm1, це коштуватиме 1 додатковий байт: нам знадобиться a 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

(Список 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 (лише для 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 або емулятор, як 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версії, а не просто дивіться на те, як вони конвергуються з quad-шкодою.


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
asgallant

ви написали це в JavaScript!
Pureferret

1
Це була легка частина. Гольфувати це було важко.
асгалант

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

Спробуйте в Інтернеті!

Безпосередня реалізація. Трохи більше використовує хитрість з моєї відповіді на пов'язане з гольфом питання .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.