Як обчислюють середнє значення набору кругових даних?


147

Я хочу обчислити середнє значення набору кругових даних. Наприклад, у мене може бути кілька зразків з читання компаса. Проблема, звичайно, полягає в тому, як боротися з обгорткою. Цей же алгоритм може бути корисним для годинника.

Справжнє питання є складнішим - що означає статистика щодо сфери або в алгебраїчному просторі, який "обгортається", наприклад, група добавок mod n. Відповідь може бути не однозначною, наприклад, середнє значення 359 градусів і 1 градус може бути 0 градусів або 180, але статистично 0 виглядає краще.

Це справжня проблема програмування для мене, і я намагаюся зробити так, щоб це не виглядало просто як математична проблема.


1
За середнім кутом, я припускаю, що ви хочете середнього опору Кут існує між двома лініями, підшипник - це напрямок однієї лінії. У цьому випадку Starblue має право.
SmacL

@Nick Fortescue: чи можете ви оновити своє запитання, щоб бути більш конкретними: ви маєте на увазі кути чи підшипник?
Мітч Пшеничний

1
Я насправді хотів чогось більш складного (але аналогічний підшипникам) і намагався спростити, щоб полегшити питання, і, як завжди, це ускладнювало. Я знайшов відповідь, яку хотів, на сайті catless.ncl.ac.uk/Risks/7.44.html#subj4 . Я відредагую qn.
Nick Fortescue

Відповідь на ризики - це в основному те, що я пропоную, за винятком того, що може виникнути проблеми, коли знаменник дорівнює 0.
starblue

Цікава стаття про значення кутів: twistedoakstudios.com/blog/?p=938
starblue

Відповіді:


99

Обчисліть одиничні вектори з кутів і візьміть їх середній кут.


8
Це не працює, якщо вектори скасовують один одного. Середнє значення все ж може бути значущим у цьому випадку залежно від його точного визначення.
Девід Ханак

21
@ Давид, середній напрямок двох підшипників на 180 градусів не визначений. Це не робить відповідь starblue неправильним, це лише винятковий випадок, як це трапляється у багатьох геометричних проблемах.
SmacL

5
@smacl: Я згоден, якщо кути представляють напрямки. Але якщо ви думаєте, наприклад, про складні числа, і визначаєте середнє значення "що є аргументом c, таким, що c c == a b", де a і b мають модуль 1, то середнє значення 0 і 180 - 90.
Давід Ханак

3
Дивіться також math.stackexchange.com/questions/14530/…
starblue

5
@PierreBdR: Якщо я зроблю два кроки у напрямку 0deg та один у напрямку 90deg, я рухався б у напрямку 26,56 град відносно того, з чого я почав. У цьому сенсі 26,56 має набагато більше сенсу, оскільки середній напрям {0,0,90} град, ніж 30 град. Середнє значення алгебраїки - це лише одне з багатьох можливих середніх показників (див. En.wikipedia.org/wiki/Mean ) - і це здається зовсім несуттєвим для цілей усереднення напрямків (так, як це робиться для багатьох інших).
Янусь

60

Це питання детально розглянуто в книзі: «Статистика Spheres», Джеффрі С. Уотсон, Університет Арканзасу Lecture Notes в математичних наук, 1983 John Wiley & Sons, Inc. , як зазначено в HTTP: //catless.ncl. ac.uk/Risks/7.44.html#subj4 Брюса Карша.

Хороший спосіб оцінити середній кут A за набором вимірювань кута a [i] 0 <= i

                   sum_i_from_1_to_N sin(a[i])
a = arctangent ---------------------------
                   sum_i_from_1_to_N cos(a[i])

Метод, який надає starblue, обчислювально еквівалентний, але його причини ясніші та, ймовірно, програмно ефективніші, а також добре працюють у нульовому випадку, тому кудо йому.

Тема тепер детальніше досліджується у Вікіпедії та з іншими напрямами, як дробові частини.


8
що також є аналогічним алгоритму, який я розмістив одночасно з вами. Вам потрібно використовувати atan2, а не простий атан, оскільки в іншому випадку ви не можете сказати, на який квадрант відповідь.
Alnitak

Ви все ще можете отримати невизначені відповіді. Як у зразку 0, 180. Тож вам все одно доведеться перевірити наявність крайових справ. Крім того, зазвичай є доступна функція atan2, яка може бути швидшою у вашому випадку.
Локі

50

Я бачу проблему - наприклад, якщо у вас кут 45 'та кут 315', "природний" середній буде 180 ", але бажане значення насправді 0".

Я думаю, що Starblue має на чомусь. Просто обчисліть (x, y) декартові координати для кожного кута і додайте ці отримані вектори разом. Кутове зміщення кінцевого вектора повинно бути вашим необхідним результатом.

x = y = 0
foreach angle {
    x += cos(angle)
    y += sin(angle)
}
average_angle = atan2(y, x)

Я зараз ігнорую, що заголовок компаса починається на північ і йде за годинниковою стрілкою, тоді як "звичайні" декартові координати починаються з нуля вздовж осі X, а потім йдуть проти годинникової стрілки. Математика повинна працювати однаково незалежно.


13
Ваша математична бібліотека, ймовірно, використовує Радіан для кутів. Не забудьте конвертувати.
Мартін Бекетт

2
Можливо, вночі пізно, але використовуючи цю логіку, я отримую середній кут 341.8947 ... замість 342 для кутів [320, 330, 340, 350, 10,]. Хтось бачив мою друк?
Алекс Робінсон

1
@AlexRobinson - це не помилка друку, це тому, що остаточний кут - це просто можливий кут, отриманий шляхом набору кроків кожного з цих кутів окремо.
Альнітак

1
@AlexRobinson, щоб бути більш точним: cos(), sin()і atan2()дає наближенню (гарне, але все одно від 1 або 2 ulps) , тому чим більше ви в середньому, тим більше помилок ви включите.
Матьє

23

ДЛЯ СПЕЦІАЛЬНОГО СПРАВИ ДВОХ АНГЛІВ:

Відповідь ((a + b) мод 360) / 2 - НЕПРАВНИЙ . Для кутів 350 і 2 найближча точка - 356, а не 176.

Рішення одиничного вектора та тригенів можуть бути надто дорогими.

Що я отримав від маленького майстерності:

diff = ( ( a - b + 180 + 360 ) mod 360 ) - 180
angle = (360 + b + ( diff / 2 ) ) mod 360
  • 0, 180 -> 90 (дві відповіді на це: це рівняння бере відповідь за годинниковою стрілкою з а)
  • 180, 0 -> 270 (див. Вище)
  • 180, 1 -> 90,5
  • 1, 180 -> 90,5
  • 20, 350 -> 5
  • 350, 20 -> 5 (усі наведені нижче приклади також обертаються належним чином)
  • 10, 20 -> 15
  • 350, 2 -> 356
  • 359, 0 -> 359,5
  • 180, 180 -> 180

Це може бути додатково оптимізована за рахунок використання бамс: stackoverflow.com/questions/1048945 / ...
Darron

Непогано. Перший рядок обчислює відносний кут a відносно b в діапазоні [-180, 179], другий обчислює середній кут від цього. Я б використав b + diff / 2 замість a - diff / 2 для наочності.
starblue

1
Я щось пропускаю? Я DO отримати 295.
Darron

Ах .. я розумію. Мод-оператор Matlab завершує від -10 до 350. Я зміню код. Це простий додатковий 360.
darron

Ще одна приємна особливість цього методу полягає в тому, що легко реалізувати середньозважене серед двох кутів. У другому рядку помножте різницю на вагу першого кута і замініть 2 у знаменнику на суму ваг. angle = (360 + b + (WEIGHT [a] * diff / (WEIGHT [a] + WEIGHT [b]))) mod 360
oosterwal

14

Ackb правильно, що ці векторні рішення не можна вважати справжніми середніми кутами, вони є лише середнім рівнем одиничних векторів. Однак запропоноване рішення Ackb математично не здається.

Далі йде рішення, яке математично виводиться з мети мінімізації (кут [i] - avgAngle) ^ 2 (де різниця коригується при необхідності), що робить його справжнім середнім арифметичним кутом.

Спочатку нам потрібно розібратися, у яких випадках різниця між кутами відрізняється від різниці між їх звичайними числами. Розглянемо кути x і y, якщо y> = x - 180 і y <= x + 180, то ми можемо використовувати різницю (xy) безпосередньо. В іншому випадку, якщо перша умова не виконується, тоді ми повинні використовувати (y + 360) в обчисленні замість y. Відповідно, якщо друга умова не виконується, тоді ми повинні використовувати (y-360) замість y. Оскільки рівняння кривої ми мінімізуємо лише зміни в точках, де ці нерівності змінюються від істинних до хибних або навпаки, ми можемо розділити повний [0,360) діапазон на набір відрізків, розділених цими точками. Тоді нам потрібно лише знайти мінімум кожного з цих сегментів, а потім мінімум мінімуму кожного сегмента, який є середнім.

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

Кутові порівняння

Щоб мінімізувати змінну, залежно від кривої, ми можемо взяти похідну, яку ми хочемо мінімізувати, і тоді ми знайдемо точку повороту (де де похідна = 0).

Тут ми застосуємо ідею мінімізації різниці у квадраті для отримання загальної середньої арифметичної формули: sum (a [i]) / n. Криву y = sum ((a [i] -x) ^ 2) можна мінімізувати таким чином:

y = sum((a[i]-x)^2)
= sum(a[i]^2 - 2*a[i]*x + x^2)
= sum(a[i]^2) - 2*x*sum(a[i]) + n*x^2

dy\dx = -2*sum(a[i]) + 2*n*x

for dy/dx = 0:
-2*sum(a[i]) + 2*n*x = 0
-> n*x = sum(a[i])
-> x = sum(a[i])/n

Тепер застосуємо його до кривих з нашими відрегульованими відмінностями:

b = підмножина a, де правильна (кутова) різниця a [i] -xc = підмножина a, де правильна (кутова) різниця (a [i] -360) -x cn = розмір cd = підмножина a, де правильна (кутова) різниця (a [i] +360) -x dn = розмір d

y = sum((b[i]-x)^2) + sum(((c[i]-360)-b)^2) + sum(((d[i]+360)-c)^2)
= sum(b[i]^2 - 2*b[i]*x + x^2)
  + sum((c[i]-360)^2 - 2*(c[i]-360)*x + x^2)
  + sum((d[i]+360)^2 - 2*(d[i]+360)*x + x^2)
= sum(b[i]^2) - 2*x*sum(b[i])
  + sum((c[i]-360)^2) - 2*x*(sum(c[i]) - 360*cn)
  + sum((d[i]+360)^2) - 2*x*(sum(d[i]) + 360*dn)
  + n*x^2
= sum(b[i]^2) + sum((c[i]-360)^2) + sum((d[i]+360)^2)
  - 2*x*(sum(b[i]) + sum(c[i]) + sum(d[i]))
  - 2*x*(360*dn - 360*cn)
  + n*x^2
= sum(b[i]^2) + sum((c[i]-360)^2) + sum((d[i]+360)^2)
  - 2*x*sum(x[i])
  - 2*x*360*(dn - cn)
  + n*x^2

dy/dx = 2*n*x - 2*sum(x[i]) - 2*360*(dn - cn)

for dy/dx = 0:
2*n*x - 2*sum(x[i]) - 2*360*(dn - cn) = 0
n*x = sum(x[i]) + 360*(dn - cn)
x = (sum(x[i]) + 360*(dn - cn))/n

Одного лише цього недостатньо, щоб отримати мінімум, хоча він працює для нормальних значень, який має необмежений набір, тому результат, безумовно, лежить у межах заданого діапазону і тому є дійсним. Нам потрібен мінімум у межах діапазону (визначеного сегментом). Якщо мінімум менший від нижньої межі нашого сегмента, то мінімум цього сегмента повинен бути на нижній межі (оскільки квадратичні криві мають лише 1 поворот), а якщо мінімум перевищує верхню межу нашого сегмента, то мінімум сегмента знаходиться на верхня межа. Після того, як у нас є мінімум для кожного сегмента, ми просто знаходимо те, що має найменше значення для того, що ми мінімізуємо (sum ((b [i] -x) ^ 2) + sum ((((c [i] -360) ) -b) ^ 2) + сума (((d [i] +360) -c) ^ 2)).

Ось зображення кривої, яке показує, як вона змінюється в точках, де x = (a [i] +180)% 360. Набір даних, про який йдеться, дорівнює {65,92,230,320,250}.

Крива

Ось реалізація алгоритму на Java, включаючи деякі оптимізації, його складність становить O (nlogn). Він може бути зменшений до O (n), якщо ви заміните сортування на основі порівняння на сортування, яке не базується на порівнянні, наприклад, сортування radix.

static double varnc(double _mean, int _n, double _sumX, double _sumSqrX)
{
    return _mean*(_n*_mean - 2*_sumX) + _sumSqrX;
}
//with lower correction
static double varlc(double _mean, int _n, double _sumX, double _sumSqrX, int _nc, double _sumC)
{
    return _mean*(_n*_mean - 2*_sumX) + _sumSqrX
            + 2*360*_sumC + _nc*(-2*360*_mean + 360*360);
}
//with upper correction
static double varuc(double _mean, int _n, double _sumX, double _sumSqrX, int _nc, double _sumC)
{
    return _mean*(_n*_mean - 2*_sumX) + _sumSqrX
            - 2*360*_sumC + _nc*(2*360*_mean + 360*360);
}

static double[] averageAngles(double[] _angles)
{
    double sumAngles;
    double sumSqrAngles;

    double[] lowerAngles;
    double[] upperAngles;

    {
        List<Double> lowerAngles_ = new LinkedList<Double>();
        List<Double> upperAngles_ = new LinkedList<Double>();

        sumAngles = 0;
        sumSqrAngles = 0;
        for(double angle : _angles)
        {
            sumAngles += angle;
            sumSqrAngles += angle*angle;
            if(angle < 180)
                lowerAngles_.add(angle);
            else if(angle > 180)
                upperAngles_.add(angle);
        }


        Collections.sort(lowerAngles_);
        Collections.sort(upperAngles_,Collections.reverseOrder());


        lowerAngles = new double[lowerAngles_.size()];
        Iterator<Double> lowerAnglesIter = lowerAngles_.iterator();
        for(int i = 0; i < lowerAngles_.size(); i++)
            lowerAngles[i] = lowerAnglesIter.next();

        upperAngles = new double[upperAngles_.size()];
        Iterator<Double> upperAnglesIter = upperAngles_.iterator();
        for(int i = 0; i < upperAngles_.size(); i++)
            upperAngles[i] = upperAnglesIter.next();
    }

    List<Double> averageAngles = new LinkedList<Double>();
    averageAngles.add(180d);
    double variance = varnc(180,_angles.length,sumAngles,sumSqrAngles);

    double lowerBound = 180;
    double sumLC = 0;
    for(int i = 0; i < lowerAngles.length; i++)
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles + 360*i)/_angles.length;
        //minimum is outside segment range (therefore not directly relevant)
        //since it is greater than lowerAngles[i], the minimum for the segment
        //must lie on the boundary lowerAngles[i]
        if(testAverageAngle > lowerAngles[i]+180)
            testAverageAngle = lowerAngles[i];

        if(testAverageAngle > lowerBound)
        {
            double testVariance = varlc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,i,sumLC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }

        lowerBound = lowerAngles[i];
        sumLC += lowerAngles[i];
    }
    //Test last segment
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles + 360*lowerAngles.length)/_angles.length;
        //minimum is inside segment range
        //we will test average 0 (360) later
        if(testAverageAngle < 360 && testAverageAngle > lowerBound)
        {
            double testVariance = varlc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,lowerAngles.length,sumLC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }
    }


    double upperBound = 180;
    double sumUC = 0;
    for(int i = 0; i < upperAngles.length; i++)
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles - 360*i)/_angles.length;
        //minimum is outside segment range (therefore not directly relevant)
        //since it is greater than lowerAngles[i], the minimum for the segment
        //must lie on the boundary lowerAngles[i]
        if(testAverageAngle < upperAngles[i]-180)
            testAverageAngle = upperAngles[i];

        if(testAverageAngle < upperBound)
        {
            double testVariance = varuc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,i,sumUC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }

        upperBound = upperAngles[i];
        sumUC += upperBound;
    }
    //Test last segment
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles - 360*upperAngles.length)/_angles.length;
        //minimum is inside segment range
        //we test average 0 (360) now           
        if(testAverageAngle < 0)
            testAverageAngle = 0;

        if(testAverageAngle < upperBound)
        {
            double testVariance = varuc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,upperAngles.length,sumUC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }
    }


    double[] averageAngles_ = new double[averageAngles.size()];
    Iterator<Double> averageAnglesIter = averageAngles.iterator();
    for(int i = 0; i < averageAngles_.length; i++)
        averageAngles_[i] = averageAnglesIter.next();


    return averageAngles_;
}

Середнє арифметичне набору кутів може не узгоджуватися з вашим інтуїтивним уявленням про те, яким повинен бути середній показник. Наприклад, середнє арифметичне множини {179,179,0,181,181} дорівнює 216 (і 144). Відповідь, яку ви відразу ж придумаєте, - це, ймовірно, 180, проте добре відомо, що середнє арифметичне сильно впливає на крайові значення. Ви також повинні пам’ятати, що кути не є векторами, настільки привабливими, як це може здатися при роботі з кутами іноді.

Цей алгоритм, звичайно, також застосовується до всіх величин, які підкоряються модульній арифметиці (з мінімальним регулюванням), наприклад, час доби.

Я також хотів би підкреслити, що, хоча це справжнє середнє значення кутів, на відміну від векторних рішень, це не обов’язково означає, що це рішення, яке ви повинні використовувати, середнє значення відповідних одиничних векторів цілком може бути значенням, яке ви насправді слід використовувати.


Метод Мітсута фактично дає початковий кут + середнє значення обертів від початкового кута. Тож, щоб отримати подібний метод, враховуючи помилку вимірювання, тоді вам потрібно буде дивитися на обертання, що відбуваються, і оцінювати помилку для них. Я думаю, вам знадобиться розподіл для обертів, щоб оцінити помилку для них.
Шумний

6

Ви повинні визначити середнє значення точніше. Для конкретного випадку двох кутів я можу придумати два різні сценарії:

  1. "Істинне" середнє, тобто (a + b) / 2% 360.
  2. Кут, який вказує "між" двома іншими, залишаючись в одному півколі, наприклад, для 355 і 5, це було б 0, а не 180. Для цього потрібно перевірити, чи різниця між двома кутами більша за 180 чи ні. Якщо так, збільште менший кут на 360, перш ніж використовувати вищезазначену формулу.

Я не бачу, як другу альтернативу можна узагальнити для випадку більш ніж двох кутів.


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

Гарні бали, Девід. Наприклад, яке середнє значення кута 180º та кута 540º? Це 360º чи 180º?
Балтимарк

3
@ Baltimark, я думаю, це залежить від того, що ти робиш. Якщо його навігація, ймовірно, остання. Якщо це фантастичний стрибок на сноуборді, можливо, колишній;)
SmacL

Отже, "справжнє" середнє значення 1 і 359 становить (360/2)% 360 = 180 ?? Я думаю, що не.
Помер у Сенте

1
@Die in Sente: чисельно кажучи, безумовно. Наприклад, якщо кути представляють повороти, а не напрямки, то середнє значення 359 і 1 - це напевно 180. Це все питання інтерпретації.
Девід Ханак

4

Як і всі середні значення, відповідь залежить від вибору метрики. Для даної метрики M середнє значення деяких кутів a_k в [-pi, pi] при k в [1, N] - це кут a_M, який мінімізує суму квадратних відстаней d ^ 2_M (a_M, a_k). Для середньозваженого середнього значення просто включають у суму ваги w_k (такі, що sum_k w_k = 1). Це є,

a_M = arg min_x sum_k w_k d ^ 2_M (x, a_k)

Двома загальними варіантами метрики є метрика Фробеніуса та Рімана. Для метрики Фробеніуса існує пряма формула, яка відповідає звичайному поняттю середнього значення в круговій статистиці. Докладніше див. "Засоби та усереднення у групі обертів", Maher Moakher, журнал SIAM щодо матричного аналізу та застосувань, том 24, випуск 1, 2002, для деталей.
http://link.aip.org/link/?SJMAEL/24/1/1

Ось функція для GNU Octave 3.2.4, яка виконує обчислення:

function ma=meanangleoct(a,w,hp,ntype)
%   ma=meanangleoct(a,w,hp,ntype) returns the average of angles a
%   given weights w and half-period hp using norm type ntype
%   Ref: "Means and Averaging in the Group of Rotations",
%   Maher Moakher, SIAM Journal on Matrix Analysis and Applications,
%   Volume 24, Issue 1, 2002.

if (nargin<1) | (nargin>4), help meanangleoct, return, end 
if isempty(a), error('no measurement angles'), end
la=length(a); sa=size(a); 
if prod(sa)~=la, error('a must be a vector'); end
if (nargin<4) || isempty(ntype), ntype='F'; end
if ~sum(ntype==['F' 'R']), error('ntype must be F or R'), end
if (nargin<3) || isempty(hp), hp=pi; end
if (nargin<2) || isempty(w), w=1/la+0*a; end
lw=length(w); sw=size(w); 
if prod(sw)~=lw, error('w must be a vector'); end
if lw~=la, error('length of w must equal length of a'), end
if sum(w)~=1, warning('resumming weights to unity'), w=w/sum(w); end

a=a(:);     % make column vector
w=w(:);     % make column vector
a=mod(a+hp,2*hp)-hp;    % reduce to central period
a=a/hp*pi;              % scale to half period pi
z=exp(i*a); % U(1) elements

% % NOTA BENE:
% % fminbnd can get hung up near the boundaries.
% % If that happens, shift the input angles a
% % forward by one half period, then shift the
% % resulting mean ma back by one half period.
% X=fminbnd(@meritfcn,-pi,pi,[],z,w,ntype);

% % seems to work better
x0=imag(log(sum(w.*z)));
X=fminbnd(@meritfcn,x0-pi,x0+pi,[],z,w,ntype);

% X=real(X);              % truncate some roundoff
X=mod(X+pi,2*pi)-pi;    % reduce to central period
ma=X*hp/pi;             % scale to half period hp

return
%%%%%%

function d2=meritfcn(x,z,w,ntype)
x=exp(i*x);
if ntype=='F'
    y=x-z;
else % ntype=='R'
    y=log(x'*z);
end
d2=y'*diag(w)*y;
return
%%%%%%

% %   test script
% % 
% % NOTA BENE: meanangleoct(a,[],[],'R') will equal mean(a) 
% % when all abs(a-b) < pi/2 for some value b
% % 
% na=3, a=sort(mod(randn(1,na)+1,2)-1)*pi;
% da=diff([a a(1)+2*pi]); [mda,ndx]=min(da);
% a=circshift(a,[0 2-ndx])    % so that diff(a(2:3)) is smallest
% A=exp(i*a), B1=expm(a(1)*[0 -1; 1 0]), 
% B2=expm(a(2)*[0 -1; 1 0]), B3=expm(a(3)*[0 -1; 1 0]),
% masimpl=[angle(mean(exp(i*a))) mean(a)]
% Bsum=B1+B2+B3; BmeanF=Bsum/sqrt(det(Bsum)); 
% % this expression for BmeanR should be correct for ordering of a above
% BmeanR=B1*(B1'*B2*(B2'*B3)^(1/2))^(2/3);
% mamtrx=real([[0 1]*logm(BmeanF)*[1 0]' [0 1]*logm(BmeanR)*[1 0]'])
% manorm=[meanangleoct(a,[],[],'F') meanangleoct(a,[],[],'R')]
% polar(a,1+0*a,'b*'), axis square, hold on
% polar(manorm(1),1,'rs'), polar(manorm(2),1,'gd'), hold off

%     Meanangleoct Version 1.0
%     Copyright (C) 2011 Alphawave Research, robjohnson@alphawaveresearch.com
%     Released under GNU GPLv3 -- see file COPYING for more info.
%
%     Meanangle is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or (at
%     your option) any later version.
%
%     Meanangle is distributed in the hope that it will be useful, but
%     WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%     General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see `http://www.gnu.org/licenses/'.

4

Я хотів би поділитися методом, який я використовував, з мікроконтролером, який не мав можливості з плаваючою точкою або тригонометрією. Мені все ж потрібно було "середньостатистично" 10 показань підшипника, щоб згладити зміни.

  1. Перевірте, чи має перший підшипник діапазон 270-360 або 0-90 градусів (два північні квадранти)
  2. Якщо це так, поверніть це та всі наступні показання на 180 градусів, зберігаючи всі значення в діапазоні 0 <= несучий <360. В іншому випадку приймайте показання в міру їх надходження.
  3. Після того, як було проведено 10 показань, обчислюють середнє число, припускаючи, що не було обгортання
  4. Якщо обертання на 180 градусів було дійсним, то поверніть обчислене середнє значення на 180 градусів, щоб повернутися до "справжнього" підшипника.

Це не ідеально; він може зламатися. Я в цьому випадку відійшов від цього, оскільки пристрій обертається лише дуже повільно. Я викладу це на випадок, якщо хтось інший виявить, що працює за подібними обмеженнями.


3

Англійською:

  1. Зробіть другий набір даних зі всіма кутами, зміщеними на 180.
  2. Візьміть дисперсію обох наборів даних.
  3. Візьміть середнє набір даних із найменшою дисперсією.
  4. Якщо це середнє значення зі зміщеного набору, то відповідь перенесіть ще раз на 180.

У пітоні:

Масив # купових NX1 кутів

if np.var(A) < np.var((A-180)%360):
    average = np.average(A)

else:
    average = (np.average((A-180)%360)+180)%360

Це чудовий спосіб досягти кінцевого результату без триггерних функцій, це просто і легко здійснити.
Ян Мерсер

це працює для будь-якого діапазону кругових даних; просто зрушити на половину кругового діапазону; чудова відповідь!
Капітан Фантастичний

3

Ось повне рішення: (вхід - це масив підшипників у градусах (0-360)

public static int getAvarageBearing(int[] arr)
{
    double sunSin = 0;
    double sunCos = 0;
    int counter = 0;

    for (double bearing : arr)
    {
        bearing *= Math.PI/180;

        sunSin += Math.sin(bearing);
        sunCos += Math.cos(bearing);
        counter++; 
    }

    int avBearing = INVALID_ANGLE_VALUE;
    if (counter > 0)
    {
        double bearingInRad = Math.atan2(sunSin/counter, sunCos/counter);
        avBearing = (int) (bearingInRad*180f/Math.PI);
        if (avBearing<0)
            avBearing += 360;
    }

    return avBearing;
}

Ця проблема мене на деякий час бентежила, ваше рішення працює (використовуючи Arduino, тому кілька змін у вашому коді, але нічого не дуже), я показую компас читання і беручи читання кожні 50 мс і зберігаю в 16-кратному масиві читання, який я потім використовую у вашій функції вище, випустіть 0-360 обгортань вирішено! дякую :)
Andology

3

У пітоні з кутами між [-180, 180)

def add_angles(a, b):
  return (a + b + 180) % 360 - 180

def average_angles(a, b):
  return add_angles(a, add_angles(-a, b)/2)

Деталі:

У середньому на два кути є два середні на 180 ° один від одного, але ми можемо захотіти ближчого середнього.

Візуально середнє значення синього ( b ) та зеленого ( a ) дає точку зору:

Оригінал

Кути "обертаються навколо" (наприклад, 355 + 10 = 5), але стандартна арифметика ігнорує цю точку гілки. Однак якщо кут b протилежний точці розгалуження, то ( b + g ) / 2 дає найбільш близьке середнє: точку зрізу.

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

обертаєтьсяповернувся


2

Я б пішов векторним шляхом, використовуючи складні числа. Мій приклад - в Python, який має вбудовані складні числа:

import cmath # complex math

def average_angle(list_of_angles):

    # make a new list of vectors
    vectors= [cmath.rect(1, angle) # length 1 for each vector
        for angle in list_of_angles]

    vector_sum= sum(vectors)

    # no need to average, we don't care for the modulus
    return cmath.phase(vector_sum)

Зауважте, що Python не потребує створення тимчасового нового списку векторів, все вищезазначене можна зробити за один крок; Я просто вибрав такий спосіб, щоб наблизити псевдо-код, застосовний і до інших мов.


2

Ось повне рішення C ++:

#include <vector>
#include <cmath>

double dAngleAvg(const vector<double>& angles) {
    auto avgSin = double{ 0.0 };
    auto avgCos = double{ 0.0 };
    static const auto conv      = double{ 0.01745329251994 }; // PI / 180
    static const auto i_conv    = double{ 57.2957795130823 }; // 180 / PI
    for (const auto& theta : angles) {
        avgSin += sin(theta*conv);
        avgCos += cos(theta*conv);
    }
    avgSin /= (double)angles.size();
    avgCos /= (double)angles.size();
    auto ret = double{ 90.0 - atan2(avgCos, avgSin) * i_conv };
    if (ret<0.0) ret += 360.0;
    return fmod(ret, 360.0);
}

Він приймає кути у вигляді вектора подвійних і повертає середнє просто як подвійне. Кути повинні бути в градусах, і звичайно середнє також в градусах.


avgCosє середнім значенням x компонентів і avgSinє середнім показником y компонентів. Параметри функції арктангента є atan2( y, x ). Отже, чи не повинен бути ваш код натомість: atan2( avgSin, avgCos ) ??
Майк Фінч

Я взяв цей алгоритм звідкись, я сам його не придумав, тому припускаю, що він правильний таким, яким він є. Плюс це також дає правильні результати.
adam10603

2

Виходячи з відповіді Альнітака , я написав метод Java для обчислення середнього значення з декількох кутів:

Якщо ваші кути в радіанах:

public static double averageAngleRadians(double... angles) {
    double x = 0;
    double y = 0;
    for (double a : angles) {
        x += Math.cos(a);
        y += Math.sin(a);
    }

    return Math.atan2(y, x);
}

Якщо ваші кути в градусах:

public static double averageAngleDegrees(double... angles) {
    double x = 0;
    double y = 0;
    for (double a : angles) {
        x += Math.cos(Math.toRadians(a));
        y += Math.sin(Math.toRadians(a));
    }

    return Math.toDegrees(Math.atan2(y, x));
}

1

Ось ідея: побудуйте середнє ітеративно, завжди обчислюючи середнє значення кутів, найближчих разом, зберігаючи вагу.

Ще одна ідея: знайти найбільший зазор між заданими кутами. Знайдіть точку, яка поділяє її, а потім виберіть протилежну точку на колі як опорний нуль, щоб обчислити середнє значення.


Я не рекомендую своєї відповіді, але натомість відповідь starblue - високо оцінений. Ключовим спостереженням є уявлення про центр компаса як про 0,0 бала.
Джон з вафлею

1

Зобразимо ці кути з точками на окружність кола.

Чи можна припустити, що всі ці точки припадають на одну і ту ж половину кола? (В іншому випадку немає очевидного способу визначення "середнього кута". Подумайте про дві точки діаметра, наприклад 0 град. І 180 град. - це середнє значення 90 градусів або 270 градусів? Що відбувається, коли у нас є 3 і більше рівномірно розподілити бали?)

З цим припущенням ми вибираємо довільну точку на цьому півколі як "початок" і вимірюємо заданий набір кутів відносно цього початку (називаємо це "відносним кутом"). Зауважимо, що відносний кут має абсолютне значення строго менше 180 град. Нарешті, візьмемо середнє значення цих відносних кутів, щоб отримати бажаний середній кут (щодо нашого походження, звичайно).


1

Єдиної "правильної відповіді" немає. Я рекомендую ознайомитися з книгою К. В. Мардіа та П. П. Юппом, "Спрямована статистика" (Wiley, 1999) для ретельного аналізу.


1

(Просто хочу поділитися моєю точкою зору з Теорії оцінки або статистичних висновків)

Проба Німбл полягає в отриманні оцінки MMSE ^ набору кутів, але це один із варіантів пошуку «усередненого» напрямку; можна також знайти оцінку MMAE ^ або якусь іншу оцінку як «усереднену» сторону, і це залежить від вашої метричної кількісної оцінки помилки напрямку; або, більш загально, в теорії оцінки, визначення функції витрат.

^ MMSE / MMAE відповідає мінімальній середньоквадратичній помилці.

ackb сказав: "Середній кут phi_avg повинен мати властивість того, що sum_i | phi_avg-phi_i | ^ 2 стає мінімальним ... вони щось середнє, але не кути"

---- ви кількісно оцінюєте помилки в сенсі середнього квадрату, і це один з найбільш поширених способів, однак, не єдиний спосіб. Відповідь, яку віддають переваги більшість людей тут (тобто сума одиничних векторів і отримують кут результату), насправді є одним із розумних рішень. Саме (можна довести) оцінювач ML слугує "усередненим" напрямком, який ми хочемо, якщо напрямки векторів моделюються як розподіл фон Мізеса. Цей розподіл не є фантастичним, а є лише періодично вибірковою дистрибутивою з 2D-гааски. Див. Рівняння (2.179) у книзі Бішопа «Розпізнавання образів та машинне навчання». Знову ж таки, це далеко не єдиний варіант, який представляє «середній» напрямок, однак цілком розумний той, який має як хороше теоретичне обгрунтування, так і просту реалізацію.

Німбл сказав: "Акб має рацію, що ці векторні рішення не можна вважати справжніми середніми кутами, вони є лише середнім рівнем одиничних векторних аналогів"

----це не правда. "Аналоги одиничного вектора" розкривають інформацію про напрямок вектора. Кут - це величина, не враховуючи довжину вектора, а одиничний вектор - це щось із додатковою інформацією, що довжина дорівнює 1. Ви можете визначити свій "одиничний" вектор довжиною 2, це насправді не має значення.


1

Ось повністю арифметичне рішення з використанням ковзних середніх значень та подбати про нормалізацію значень. Це швидко і дає правильні відповіді, якщо всі кути знаходяться на одній стороні кола (в межах 180 ° один від одного).

Це математично еквівалентно додаванню зміщення, яке зміщує значення в діапазон (0, 180), калібрування середнього значення і потім віднімання зміщення.

У коментарях описано, який діапазон може набути конкретна величина в будь-який момент часу

// angles have to be in the range [0, 360) and within 180° of each other.
// n >= 1
// returns the circular average of the angles int the range [0, 360).
double meanAngle(double* angles, int n)
{
    double average = angles[0];
    for (int i = 1; i<n; i++)
    {
        // average: (0, 360)
        double diff = angles[i]-average;
        // diff: (-540, 540)

        if (diff < -180)
            diff += 360;
        else if (diff >= 180)
            diff -= 360;
        // diff: (-180, 180)

        average += diff/(i+1);
        // average: (-180, 540)

        if (average < 0)
            average += 360;
        else if (average >= 360)
            average -= 360;
        // average: (0, 360)
    }
    return average;
}

1

Ну я дуже запізнююсь на вечірку, але подумав, що додам 2 копійки варті, тому що я не зміг знайти остаточної відповіді. Врешті-решт я реалізував наступну версію Java методу Mitsuta, який, я сподіваюся, пропонує просте і надійне рішення. Тим більше, що Стандартне відхилення забезпечує як дисперсію міри, так і, якщо sd == 90, вказує, що кути введення призводять до неоднозначної середньої величини.

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

/**
 * The Mitsuta method
 *
 * @param angles Angles from 0 - 360
 * @return double array containing
 * 0 - mean
 * 1 - sd: a measure of angular dispersion, in the range [0..360], similar to standard deviation.
 * Note if sd == 90 then the mean can also be its inverse, i.e. 360 == 0, 300 == 60.
 */
public static double[] getAngleStatsMitsuta(double... angles) {
    double sum = 0;
    double sumsq = 0;
    for (double angle : angles) {
        if (angle >= 180) {
            angle -= 360;
        }
        sum += angle;
        sumsq += angle * angle;
    }

    double mean = sum / angles.length;
    return new double[]{mean <= 0 ? 360 + mean: mean, Math.sqrt(sumsq / angles.length - (mean * mean))};
}

... і для всіх вас (Java) вибухів там, ви можете використовувати вищезазначений підхід, щоб отримати середній кут в одному рядку.

Arrays.stream(angles).map(angle -> angle<180 ? angle: (angle-360)).sum() / angles.length;

Я вважаю, що ви пропустили щось із методу Міцуди. Будь ласка , зверніть увагу на відповідь розмістив Ліор Коган stackoverflow.com/a/1828222/9265852
kykzk46

0

Альнітак має правильне рішення. Рішення Ніка Фортескуа функціонально однакове.

Для особливого випадку де

(sum (x_component) = 0,0 && sum (y_component) = 0,0) // напр., 2 кути 10. і 190. градусів еа.

використовуйте 0,0 градуса як суму

Обчислювально вам доведеться перевірити цей випадок, оскільки atan2 (0., 0.) не визначений і призведе до помилки.


на glibc 'atan2' визначено для (0, 0) - результат 0
Alnitak

0

Середній кут phi_avg повинен мати властивість, що sum_i | phi_avg-phi_i | ^ 2 стає мінімальною, де різниця повинна бути в [-Pi, Pi) (тому що може бути коротше, щоб піти навпаки!). Це легко досягти, нормалізуючи всі вхідні значення до [0, 2Pi), підтримуючи середній показник phi_run і вибравши нормалізацію | phi_i-phi_run | до [-Pi, Pi) (додаванням або відніманням 2Pi). Більшість запропонованих вище пропозицій роблять щось інше, що не має цього мінімального властивості, тобто вони щось середнє , але не кути.


0

Я вирішив проблему за допомогою відповіді від @David_Hanak. За його словами:

Кут, який вказує "між" двома іншими, залишаючись в одному півколі, наприклад, для 355 і 5, це було б 0, а не 180. Для цього потрібно перевірити, чи різниця між двома кутами більша за 180 чи ні. Якщо так, збільште менший кут на 360, перш ніж використовувати вищезазначену формулу.

Тому я зробив підрахунок середнього рівня всіх кутів. А потім усі кути, менші за це, збільшуйте їх на 360. Потім перерахуйте середнє значення, додавши їх усі і розділивши їх на їх довжину.

        float angleY = 0f;
        int count = eulerAngles.Count;

        for (byte i = 0; i < count; i++)
            angleY += eulerAngles[i].y;

        float averageAngle = angleY / count;

        angleY = 0f;
        for (byte i = 0; i < count; i++)
        {
            float angle = eulerAngles[i].y;
            if (angle < averageAngle)
                angle += 360f;
            angleY += angle;
        }

        angleY = angleY / count;

Працює чудово.


0

Функція Python:

from math import sin,cos,atan2,pi
import numpy as np
def meanangle(angles,weights=0,setting='degrees'):
    '''computes the mean angle'''
    if weights==0:
         weights=np.ones(len(angles))
    sumsin=0
    sumcos=0
    if setting=='degrees':
        angles=np.array(angles)*pi/180
    for i in range(len(angles)):
        sumsin+=weights[i]/sum(weights)*sin(angles[i])
        sumcos+=weights[i]/sum(weights)*cos(angles[i])
    average=atan2(sumsin,sumcos)
    if setting=='degrees':
        average=average*180/pi
    return average

0

Ви можете використовувати цю функцію в Matlab:

function retVal=DegreeAngleMean(x) 

len=length(x);

sum1=0; 
sum2=0; 

count1=0;
count2=0; 

for i=1:len 
   if x(i)<180 
       sum1=sum1+x(i); 
       count1=count1+1; 
   else 
       sum2=sum2+x(i); 
       count2=count2+1; 
   end 
end 

if (count1>0) 
     k1=sum1/count1; 
end 

if (count2>0) 
     k2=sum2/count2; 
end 

if count1>0 && count2>0 
   if(k2-k1 >= 180) 
       retVal = ((sum1+sum2)-count2*360)/len; 
   else 
       retVal = (sum1+sum2)/len; 
   end 
elseif count1>0 
    retVal = k1; 
else 
    retVal = k2; 
end 

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

0

Ви можете побачити рішення та невелике пояснення за наступним посиланням для будь-якої мови програмування: https://rosettacode.org/wiki/Aasures/Mean_angle

Наприклад, рішення C ++ :

#include<math.h>
#include<stdio.h>

double
meanAngle (double *angles, int size)
{
  double y_part = 0, x_part = 0;
  int i;

  for (i = 0; i < size; i++)
    {
      x_part += cos (angles[i] * M_PI / 180);
      y_part += sin (angles[i] * M_PI / 180);
    }

  return atan2 (y_part / size, x_part / size) * 180 / M_PI;
}

int
main ()
{
  double angleSet1[] = { 350, 10 };
  double angleSet2[] = { 90, 180, 270, 360};
  double angleSet3[] = { 10, 20, 30};

  printf ("\nMean Angle for 1st set : %lf degrees", meanAngle (angleSet1, 2));
  printf ("\nMean Angle for 2nd set : %lf degrees", meanAngle (angleSet2, 4));
  printf ("\nMean Angle for 3rd set : %lf degrees\n", meanAngle (angleSet3, 3));
  return 0;
}

Вихід:

Mean Angle for 1st set : -0.000000 degrees
Mean Angle for 2nd set : -90.000000 degrees
Mean Angle for 3rd set : 20.000000 degrees

Або рішення Matlab :

function u = mean_angle(phi)
    u = angle(mean(exp(i*pi*phi/180)))*180/pi;
end

 mean_angle([350, 10])
ans = -2.7452e-14
 mean_angle([90, 180, 270, 360])
ans = -90
 mean_angle([10, 20, 30])
ans =  20.000

0

Хоча відповідь starblue дає кут середнього одиничного вектора, можна розширити поняття середнього арифметичного до кутів, якщо ви приймаєте, що може бути більше однієї відповіді в діапазоні від 0 до 2 * пі (або 0 ° до 360 °). Наприклад, середнє значення 0 ° і 180 ° може бути або 90 °, або 270 °.

Середнє арифметичне має властивість бути єдиним значенням із мінімальною сумою квадратних відстаней до вхідних значень. Відстань уздовж одиничного кола між двома одиничними векторами можна легко обчислити як обернений косинус їх крапкового добутку. Якщо ми обираємо одиничний вектор, зводячи до мінімуму суму прямокутного косинуса квадрата точкового добутку нашого вектора та кожного вхідного одиничного вектора, то ми маємо еквівалентне середнє значення. Знову ж таки, майте на увазі, що у виняткових випадках може бути два або більше мінімумів.

Цю концепцію можна було б поширити на будь-яку кількість вимірів, оскільки відстань уздовж одиничної сфери можна обчислити точно так само, як відстань уздовж одиничного кола - оберненого косинуса крапкового добутку двох одиничних векторів.

Для кіл ми могли вирішити це середнє значення кількома способами, але я пропоную такий алгоритм O (n ^ 2) (кути знаходяться в радіанах, і я уникаю обчислення одиничних векторів):

var bestAverage = -1
double minimumSquareDistance
for each a1 in input
    var sumA = 0;
    for each a2 in input
        var a = (a2 - a1) mod (2*pi) + a1
        sumA += a
    end for
    var averageHere = sumA / input.count
    var sumSqDistHere = 0
    for each a2 in input
        var dist = (a2 - averageHere + pi) mod (2*pi) - pi // keep within range of -pi to pi
        sumSqDistHere += dist * dist
    end for
    if (bestAverage < 0 OR sumSqDistHere < minimumSquareDistance) // for exceptional cases, sumSqDistHere may be equal to minimumSquareDistance at least once. In these cases we will only find one of the averages
        minimumSquareDistance = sumSqDistHere
        bestAverage = averageHere
    end if
end for
return bestAverage

Якщо всі кути знаходяться в межах 180 ° один від одного, тоді ми можемо використовувати простіший алгоритм O (n) + O (сортування) (знову ж таки, використовуючи радіани та уникаючи використання одиничних векторів):

sort(input)
var largestGapEnd = input[0]
var largestGapSize = (input[0] - input[input.count-1]) mod (2*pi)
for (int i = 1; i < input.count; ++i)
    var gapSize = (input[i] - input[i - 1]) mod (2*pi)
    if (largestGapEnd < 0 OR gapSize > largestGapSize)
        largestGapSize = gapSize
        largestGapEnd = input[i]
    end if
end for
double sum = 0
for each angle in input
    var a2 = (angle - largestGapEnd) mod (2*pi) + largestGapEnd
    sum += a2
end for
return sum / input.count

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


0

Проблема надзвичайно проста. 1. Переконайтесь, що всі кути знаходяться в межах від -180 до 180 градусів. 2. a Додайте всі негативні кути, візьміть їх середнє значення та COUNT, скільки 2. b.Додайте всі негативні кути, візьміть їх середнє значення та COUNT скільки. 3. Візьміть різницю pos_average мінус neg_average Якщо різниця більше 180, то змініть різницю на 360 мінус різницю. В іншому випадку просто змініть знак різниці. Зауважте, що різниця завжди негативна. Average_Angle дорівнює pos_average плюс різниця разів "вага", від'ємне число, поділене на суму від'ємного та додатного підрахунку


0

Ось ява-код на середні кути, я думаю, що він досить надійний.

public static double getAverageAngle(List<Double> angles)
{
    // r = right (0 to 180 degrees)

    // l = left (180 to 360 degrees)

    double rTotal = 0;
    double lTotal = 0;
    double rCtr = 0;
    double lCtr = 0;

    for (Double angle : angles)
    {
        double norm = normalize(angle);
        if (norm >= 180)
        {
            lTotal += norm;
            lCtr++;
        } else
        {
            rTotal += norm;
            rCtr++;
        }
    }

    double rAvg = rTotal / Math.max(rCtr, 1.0);
    double lAvg = lTotal / Math.max(lCtr, 1.0);

    if (rAvg > lAvg + 180)
    {
        lAvg += 360;
    }
    if (lAvg > rAvg + 180)
    {
        rAvg += 360;
    }

    double rPortion = rAvg * (rCtr / (rCtr + lCtr));
    double lPortion = lAvg * (lCtr / (lCtr + rCtr));
    return normalize(rPortion + lPortion);
}

public static double normalize(double angle)
{
    double result = angle;
    if (angle >= 360)
    {
        result = angle % 360;
    }
    if (angle < 0)
    {
        result = 360 + (angle % 360);
    }
    return result;
}

-3

У мене інший метод, ніж @Starblue, який дає "правильні" відповіді на деякі кути, наведені вище. Наприклад:

  • angle_avg ([350,10]) = 0
  • angle_avg ([- 90,90,40]) = 13,333
  • angle_avg ([350,2]) = 356

Він використовує суму над різницею між послідовними кутами. Код (в Matlab):

function [avg] = angle_avg(angles)
last = angles(1);
sum = angles(1);
for i=2:length(angles)
    diff = mod(angles(i)-angles(i-1)+ 180,360)-180
    last = last + diff;
    sum = sum + last;
end
avg = mod(sum/length(angles), 360);
end

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