131

Tôi muốn tính trung bình của một tập hợp dữ liệu vòng tròn. Ví dụ, tôi có thể có một vài mẫu từ việc đọc la bàn. Vấn đề tất nhiên là làm thế nào để đối phó với sự bao bọc. Thuật toán tương tự có thể hữu ích cho một mặt đồng hồ.

Câu hỏi thực tế phức tạp hơn - thống kê có ý nghĩa gì trên một hình cầu hoặc trong một không gian đại số "bao bọc xung quanh", ví dụ như nhóm phụ gia mod n. Câu trả lời có thể không phải là duy nhất, ví dụ trung bình là 359 độ và 1 độ có thể là 0 độ hoặc 180, nhưng về mặt thống kê 0 có vẻ tốt hơn.

Đây là một vấn đề lập trình thực sự đối với tôi và tôi đang cố gắng làm cho nó không giống như một bài toán.

|
84

Tính toán các vectơ đơn vị từ các góc và lấy góc trung bình của chúng.

|
  • 1

    Điều đó không hoạt động nếu các vectơ triệt tiêu lẫn nhau. Trung bình vẫn có thể có ý nghĩa trong trường hợp này, tùy thuộc vào định nghĩa chính xác của nó.

    – Hoàng Vinh Quang 14:27:03 29/01/2009
  • 1

    @David, hướng trung bình của hai vòng bi 180 độ không xác định. Điều này không làm cho câu trả lời của starblue sai, đó chỉ là một trường hợp đặc biệt, như xảy ra trong nhiều vấn đề về địa mạo.

    – Bùi Thu Hạ 14:31:57 29/01/2009
  • 1

    @smacl: Tôi đồng ý, nếu các góc đại diện cho các hướng. Nhưng nếu bạn nghĩ về các số phức, chẳng hạn, và định nghĩa trung bình là "đối số của c là gì, sao cho c c == a b", trong đó a và b có mô đun là 1, thì trung bình là 0 và 180 là 90.

    – Vũ Thanh Mẫn 14:42:43 29/01/2009
  • 1
  • 1

    @PierreBdR: Nếu tôi thực hiện hai bước theo hướng 0deg và một bước theo hướng 90deg, tôi sẽ di chuyển theo hướng 26,56 độ so với nơi tôi bắt đầu. Theo nghĩa này, 26,56 có ý nghĩa hơn nhiều khi hướng trung bình của {0,0,90} độ hơn 30 độ. Trung bình đại số chỉ là một trong nhiều mức trung bình có thể có (xem en.wikipedia.org/wiki/Mean ) - và có vẻ như không liên quan cho mục đích lấy trung bình các hướng (giống như đối với nhiều người khác).

    – Đặng Trọng Tường 03:52:31 04/03/2011
52

Câu hỏi này được kiểm tra chi tiết trong cuốn sách: "Statistics On Spheres", Geoffrey S. Watson, Bài giảng của Đại học Arkansas trong Khoa học toán học, 1983 John Wiley & Sons, Inc. như đã đề cập tại http: //catless.ncl. ac.uk/Risks/7.44.html#subj4 của Bruce Karsh.

Một cách tốt để ước tính góc trung bình, A, từ tập hợp các số đo góc a [i] 0 <= i

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

Phương pháp được đưa ra bởi starblue là tương đương về mặt tính toán, nhưng lý do của anh ta rõ ràng hơn và có thể hiệu quả hơn về mặt lập trình, và cũng hoạt động tốt trong trường hợp không, vì vậy, với anh ta.

Chủ đề hiện được khám phá chi tiết hơn trên Wikipedia và với các mục đích sử dụng khác, như các phần phân số.

|
  • 1

    cũng giống như thuật toán tôi đã đăng cùng lúc với bạn. Tuy nhiên, bạn sẽ cần sử dụng atan2 thay vì atan đơn giản, vì nếu không, bạn không thể biết câu trả lời nằm ở góc phần tư nào.

    – Vũ Quang Tài 15:06:02 29/01/2009
  • 1

    Bạn vẫn có thể kết thúc với một số câu trả lời không xác định. Giống như trong mẫu 0, 180. Vì vậy, bạn vẫn phải kiểm tra các trường hợp cạnh. Ngoài ra, thường có một chức năng atan2 có thể nhanh hơn trong trường hợp của bạn.

    – Lý Kỳ Công 15:24:01 29/01/2009
47

Tôi thấy vấn đề - ví dụ: nếu bạn có góc 45 'và góc 315', trung bình "tự nhiên" sẽ là 180 ', nhưng giá trị bạn muốn thực sự là 0'.

Tôi nghĩ Starblue là một cái gì đó. Chỉ cần tính toán tọa độ cartesian (x, y) cho mỗi góc và cộng các vectơ kết quả đó lại với nhau. Giá trị bù góc của vectơ cuối cùng phải là kết quả bắt buộc của bạn.

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

Bây giờ tôi đang bỏ qua rằng một tiêu đề la bàn bắt đầu ở phía bắc và đi theo chiều kim đồng hồ, trong khi tọa độ cartesian "bình thường" bắt đầu bằng 0 dọc theo trục X, và sau đó đi ngược chiều kim đồng hồ. Các toán học nên làm việc theo cùng một cách bất kể.

|
  • 1

    Thư viện toán học của bạn có thể sử dụng Radian cho các góc. Nhớ chuyển đổi.

    – Vũ Quang Tài 23:51:16 27/11/2009
  • 1

    Có thể đã quá muộn vào ban đêm, nhưng bằng cách sử dụng logic này, tôi có được góc trung bình 341,8947 ... thay vì 342 cho các góc [320, 330, 340, 350, 10,]. Có ai thấy lỗi đánh máy của tôi không?

    – Lý Kỳ Công 12:15:10 03/07/2014
  • 1

    @AlexRobinson không phải là một lỗi đánh máy, bởi vì góc cuối cùng chỉ đơn giản là góc cuối cùng có được bằng cách thực hiện một bộ các bước của từng góc đó.

    – Đặng Gia Bảo 12:30:34 03/07/2014
  • 1

    @AlexRobinson, để cụ thể hơn : cos(), sin()atan2()đưa ra các xấp xỉ (tốt, nhưng vẫn giảm 1 hoặc 2 ulps) vì vậy bạn càng trung bình, bạn càng bao gồm nhiều lỗi.

    – Đỗ Hương Trang 10:46:48 17/03/2016
19

ĐỐI VỚI TRƯỜNG HỢP ĐẶC BIỆT CỦA HAI ÁO:

Câu trả lời ((a + b) mod 360) / 2SAI . Đối với các góc 350 và 2, điểm gần nhất là 356, không phải 176.

Các giải pháp vectơ và trig đơn vị có thể quá đắt.

Những gì tôi đã nhận được từ một chút mày mò là:

diff = ( ( a - b + 180 + 360 ) mod 360 ) - 180
angle = (360 + b + ( diff / 2 ) ) mod 360
  • 0, 180 -> 90 (hai câu trả lời cho điều này: phương trình này lấy câu trả lời theo chiều kim đồng hồ từ a)
  • 180, 0 -> 270 (xem bên trên)
  • 180, 1 -> 90,5
  • 1, 180 -> 90,5
  • 20, 350 -> 5
  • 350, 20 -> 5 (tất cả các ví dụ sau cũng đảo ngược đúng cách)
  • 10, 20 -> 15
  • 350, 2 -> 356
  • 359, 0 -> 359,5
  • 180, 180 -> 180
|
14

ackb đúng rằng các giải pháp dựa trên vectơ này không thể được coi là trung bình thực của các góc, chúng chỉ là trung bình của các đối tác vectơ đơn vị. Tuy nhiên, giải pháp được đề xuất của ackb không xuất hiện với âm thanh toán học.

Sau đây là một giải pháp có nguồn gốc toán học từ mục tiêu giảm thiểu (góc [i] - avgAngle) ^ 2 (trong đó sự khác biệt được sửa nếu cần thiết), làm cho nó trở thành trung bình số học thực sự của các góc.

Đầu tiên, chúng ta cần xem xét chính xác trường hợp nào sự khác biệt giữa các góc khác với sự khác biệt giữa các đối tác số bình thường của chúng. Xét các góc x và y, nếu y> = x - 180 và y <= x + 180, thì chúng ta có thể sử dụng trực tiếp sự khác biệt (xy). Mặt khác, nếu điều kiện đầu tiên không được đáp ứng thì chúng ta phải sử dụng (y + 360) trong phép tính thay vì y. Tương ứng, nếu điều kiện thứ hai không được đáp ứng thì chúng ta phải sử dụng (y-360) thay vì y. Do phương trình của đường cong, chúng tôi chỉ giảm thiểu các thay đổi tại các điểm mà các bất đẳng thức này thay đổi từ đúng thành sai hoặc ngược lại, chúng tôi có thể tách phạm vi [0,360) đầy đủ thành một tập hợp các phân đoạn, cách nhau bởi các điểm này. Sau đó, chúng ta chỉ cần tìm mức tối thiểu của mỗi phân khúc này và sau đó là mức tối thiểu của mỗi phân khúc, là mức trung bình.

Đây là một hình ảnh chứng minh nơi các vấn đề xảy ra trong việc tính toán sự khác biệt góc. Nếu x nằm trong vùng màu xám thì sẽ có vấn đề.

Để giảm thiểu một biến, tùy thuộc vào đường cong, chúng ta có thể lấy đạo hàm của những gì chúng ta muốn giảm thiểu và sau đó chúng ta tìm thấy bước ngoặt (đó là nơi đạo hàm = 0).

Ở đây chúng tôi sẽ áp dụng ý tưởng giảm thiểu chênh lệch bình phương để rút ra công thức trung bình số học phổ biến: sum (a [i]) / n. Đường cong y = sum ((a [i] -x) ^ 2) có thể được thu nhỏ theo cách này:

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ây giờ áp dụng nó cho các đường cong với sự khác biệt được điều chỉnh của chúng tôi:

b = tập con của a trong đó chênh lệch (góc) chính xác a [i] -xc = tập con của a trong đó chênh lệch (góc) đúng (a [i] -360) -x cn = size của cd = tập con của a trong đó đúng (góc) khác biệt (a [i] +360) -x dn = kích thước của 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

Chỉ riêng điều này là không đủ để có được mức tối thiểu, trong khi nó hoạt động với các giá trị bình thường, có một tập hợp không giới hạn, do đó, kết quả chắc chắn sẽ nằm trong phạm vi của tập hợp và do đó là hợp lệ. Chúng tôi cần tối thiểu trong một phạm vi (được xác định bởi phân khúc). Nếu mức tối thiểu nhỏ hơn giới hạn dưới của phân khúc của chúng tôi thì mức tối thiểu của phân khúc đó phải ở giới hạn dưới (vì các đường cong bậc hai chỉ có 1 điểm quay đầu) và nếu mức tối thiểu lớn hơn giới hạn trên của phân khúc của chúng tôi thì mức tối thiểu của phân khúc là giới hạn trên. Sau khi chúng tôi có mức tối thiểu cho mỗi phân đoạn, chúng tôi chỉ cần tìm một phân đoạn có giá trị thấp nhất cho những gì chúng tôi thu nhỏ (sum ((b [i] -x) ^ 2) + sum (((c [i] -360 ) -b) ^ 2) + tổng (((d [i] +360) -c) ^ 2)).

Đây là hình ảnh cho đường cong, cho thấy nó thay đổi như thế nào tại các điểm có x = (a [i] +180)% 360. Tập dữ liệu được đề cập là {65,92,230,320,250}.

Đây là một triển khai của thuật toán trong Java, bao gồm một số tối ưu hóa, độ phức tạp của nó là O (nlogn). Nó có thể được giảm xuống O (n) nếu bạn thay thế sắp xếp dựa trên so sánh bằng một loại không dựa trên so sánh, chẳng hạn như sắp xếp cơ số.

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

Giá trị trung bình số học của một tập hợp các góc có thể không phù hợp với ý tưởng trực quan của bạn về mức trung bình nên là bao nhiêu. Ví dụ: giá trị trung bình số học của tập {179,179,0,181,181} là 216 (và 144). Câu trả lời bạn nghĩ ngay đến có lẽ là 180, tuy nhiên ai cũng biết rằng trung bình số học bị ảnh hưởng nặng nề bởi các giá trị cạnh. Bạn cũng nên nhớ rằng các góc không phải là vectơ, hấp dẫn như đôi khi có thể xử lý các góc.

Thuật toán này tất nhiên cũng áp dụng cho tất cả các đại lượng tuân theo số học mô-đun (với sự điều chỉnh tối thiểu), chẳng hạn như thời gian trong ngày.

Tôi cũng muốn nhấn mạnh rằng mặc dù đây là trung bình thực của các góc, không giống như các giải pháp vectơ, điều đó không nhất thiết có nghĩa là đó là giải pháp bạn nên sử dụng, trung bình của các vectơ đơn vị tương ứng có thể là giá trị bạn thực sự nên được sử dụng.

|
  • 1

    Phương pháp Mitsuta thực sự cho góc bắt đầu + trung bình của các phép quay từ góc bắt đầu. Vì vậy, để có được một phương pháp tương tự, tính toán sai số đo thì bạn cần xem xét các phép quay xảy ra và ước tính sai số cho các phương pháp đó. Tôi nghĩ rằng bạn sẽ cần một bản phân phối cho các phép quay để ước tính lỗi cho chúng.

    – Hồ Hải Ân 09:30:44 08/09/2010
6

Bạn phải xác định trung bình chính xác hơn. Đối với trường hợp cụ thể của hai góc, tôi có thể nghĩ về hai kịch bản khác nhau:

  1. Trung bình "đúng", nghĩa là (a + b) / 2% 360.
  2. Góc chỉ "giữa" hai góc còn lại trong cùng một hình bán nguyệt, ví dụ cho 355 và 5, đây sẽ là 0, không phải 180. Để làm điều này, bạn cần kiểm tra xem chênh lệch giữa hai góc có lớn hơn 180 không hay không. Nếu vậy, hãy tăng góc nhỏ hơn 360 trước khi sử dụng công thức trên.

Tuy nhiên, tôi không thấy cách thay thế thứ hai có thể được khái quát cho trường hợp có nhiều hơn hai góc.

|
  • 1

    Trong khi câu hỏi đề cập đến các góc, nó được coi là hướng trung bình tốt hơn và là một vấn đề điều hướng phổ biến.

    – Dương Ngọc Mai 14:33:55 29/01/2009
  • 1

    Điểm tốt, David. Chẳng hạn, trung bình của góc 180º và góc 540º là gì? Là 360 độ hay 180 độ?

    – Tạ Hải Nam 14:35:13 29/01/2009
  • 1

    @Baltimark, tôi đoán nó phụ thuộc vào những gì bạn đang làm. Nếu điều hướng của nó, có lẽ là sau này. Nếu đó là một cú nhảy trượt tuyết lạ mắt, có thể là trước đây;)

    – Hoàng Kim Châu 14:39:50 29/01/2009
  • 1

    Vậy trung bình "thực" của 1 và 359 là (360/2)% 360 = 180 ?? Tôi nghĩ là không.

    – Đặng Tiến Hoạt 18:00:56 01/02/2009
  • 1

    @Die in Sente: nói số, chắc chắn. Ví dụ: nếu các góc đại diện cho các ngã rẽ, không phải các hướng, thì trung bình của 359 và 1 chắc chắn là 180. Tất cả chỉ là vấn đề giải thích.

    – Đỗ Ðức Trung 18:57:57 01/02/2009
4

Giống như tất cả các mức trung bình, câu trả lời phụ thuộc vào sự lựa chọn số liệu. Đối với một số liệu M đã cho, trung bình của một số góc a_k trong [-pi, pi] cho k trong [1, N] là góc a_M giúp giảm thiểu tổng khoảng cách bình phương d ^ 2_M (a_M, a_k). Đối với một trung bình có trọng số, người ta chỉ cần bao gồm tổng các trọng số w_k (sao cho sum_k w_k = 1). Đó là,

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

Hai lựa chọn phổ biến về số liệu là số liệu Frobenius và Riemann. Đối với số liệu Frobenius, tồn tại một công thức trực tiếp tương ứng với khái niệm thông thường về mang trung bình trong thống kê vòng tròn. Xem "Phương tiện và tính trung bình trong nhóm các phép quay", Maher Moakher, Tạp chí SIAM về phân tích và ứng dụng ma trận, Tập 24, Số 1, 2002, để biết chi tiết.
http://link.aip.org/link/?SJMAEL/24/1/1

Đây là một chức năng cho GNU Octave 3.2.4 thực hiện tính toán:

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/'.
|
3

Tôi có một phương pháp khác với @Starblue đưa ra câu trả lời "chính xác" cho một số góc được đưa ra ở trên. Ví dụ:

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

Nó sử dụng một tổng số trên sự khác biệt giữa các góc liên tiếp. Mã (trong 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

    Mã của bạn trả về các câu trả lời khác nhau cho [-90,90,40][90,-90,40]; Tôi không nghĩ rằng trung bình không giao hoán là một mức rất hữu ích.

    – Lý Giang Thiên 20:24:56 04/03/2013
3

Đây là giải pháp đầy đủ: (đầu vào là một mảng mang theo độ (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;
}
|
  • 1

    Vấn đề này đã gây trở ngại cho tôi trong một thời gian, giải pháp của bạn hoạt động (sử dụng Arduino, do đó, một vài thay đổi đối với mã của bạn nhưng không có gì nhiều), tôi đang hiển thị đọc la bàn và đọc số đọc sau mỗi 50ms và lưu trữ vào mảng đọc 16 lần trong chức năng của bạn ở trên, vấn đề 0-360 bao quanh giải quyết! cảm ơn :)

    – Dương Thăng Long 14:56:14 11/11/2015
3

Tôi muốn chia sẻ một phương pháp tôi đã sử dụng với một vi điều khiển không có khả năng dấu phẩy động hoặc lượng giác. Tôi vẫn cần "trung bình" 10 bài đọc mang thô để làm mịn các biến thể.

  1. Kiểm tra xem ổ đỡ đầu tiên có nằm trong khoảng 270-360 hoặc 0-90 độ (hai góc phần tư phía bắc)
  2. Nếu có, hãy xoay giá trị này và tất cả các lần đọc tiếp theo 180 độ, giữ tất cả các giá trị trong phạm vi 0 <= mang <360. Nếu không, hãy đọc các giá trị đọc khi chúng đến.
  3. Sau khi đọc 10 lần, hãy tính trung bình bằng số giả sử rằng không có kết quả
  4. Nếu xoay 180 độ đã có hiệu lực thì hãy xoay trung bình tính toán 180 độ để trở về ổ đỡ "thật".

Nó không lý tưởng; nó có thể vỡ Tôi đã thoát khỏi nó trong trường hợp này vì thiết bị chỉ quay rất chậm. Tôi sẽ đưa nó ra khỏi đó trong trường hợp bất cứ ai khác thấy mình làm việc dưới những hạn chế tương tự.

|
2

Trong trăn, với các góc giữa [-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)

Chi tiết:

Đối với trung bình của hai góc có hai trung bình cách nhau 180 °, nhưng chúng ta có thể muốn trung bình gần hơn.

Trực quan, trung bình của màu xanh lam ( b ) và màu xanh lá cây ( a ) mang lại điểm teal:

Các góc 'bao quanh' (ví dụ 355 + 10 = 5), nhưng số học tiêu chuẩn sẽ bỏ qua điểm nhánh này. Tuy nhiên, nếu góc b đối diện với điểm nhánh, thì ( b + g ) / 2 cho điểm trung bình gần nhất: điểm teal.

Đối với hai góc bất kỳ, chúng ta có thể xoay vấn đề để một trong các góc đối diện với điểm nhánh, thực hiện tính trung bình tiêu chuẩn, sau đó xoay trở lại.

|
2

Đây là một giải pháp C ++ hoàn chỉnh:

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

Nó lấy các góc dưới dạng một vectơ nhân đôi và trả về trung bình đơn giản là gấp đôi. Các góc phải được tính theo độ, và tất nhiên trung bình cũng tính theo độ.

|
  • 1

    avgCoslà trung bình của các thành phần x và avgSinlà trung bình của các thành phần y. Các tham số cho hàm arctangent là atan2( y, x ). Vì vậy, không nên mã của bạn thay vào đó là: atan2( avgSin, avgCos ) ??

    – Hoàng Duy Uyên 14:27:29 28/09/2017
  • 1

    Tôi đã nhận được thuật toán này từ một nơi nào đó, tôi đã không tự mình nghĩ ra nó, vì vậy tôi cho rằng nó đúng theo cách của nó. Thêm vào đó nó cho kết quả chính xác là tốt.

    – Tạ Cao Thọ 20:59:01 28/09/2017
2

Tôi sẽ đi theo cách vector sử dụng số phức. Ví dụ của tôi là trong Python, có số phức tích hợp:

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)

Lưu ý rằng Python không cần xây dựng một danh sách các vectơ mới tạm thời, tất cả những điều trên có thể được thực hiện trong một bước; Tôi chỉ chọn cách này để xấp xỉ mã giả áp dụng cho các ngôn ngữ khác.

|
1

Chà, tôi cực kỳ trễ bữa tiệc nhưng nghĩ rằng tôi sẽ thêm 2 xu của mình vì tôi thực sự không thể tìm thấy bất kỳ câu trả lời dứt khoát nào. Cuối cùng, tôi đã triển khai phiên bản Java sau đây của phương thức Mitsuta, mà tôi hy vọng, cung cấp một giải pháp đơn giản và mạnh mẽ. Cụ thể là Độ lệch chuẩn cung cấp cả độ phân tán của thước đo và, nếu sd == 90, chỉ ra rằng các góc đầu vào dẫn đến giá trị trung bình không rõ ràng.

EDIT: Trên thực tế tôi nhận ra rằng việc triển khai ban đầu của tôi có thể được đơn giản hóa hơn nữa, trên thực tế đơn giản đáng lo ngại khi xem xét tất cả các cuộc hội thoại và lượng giác đang diễn ra trong các câu trả lời khác.

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

... Và đối với tất cả các bạn (Java) chuyên viên máy tính ngoài kia, bạn có thể sử dụng cách tiếp cận ở trên để có được góc trung bình trong một dòng.

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

Dưới đây là một giải pháp số học hoàn toàn sử dụng các đường trung bình di động và chú ý để bình thường hóa các giá trị. Nó nhanh và cung cấp câu trả lời chính xác nếu tất cả các góc nằm ở một bên của vòng tròn (trong phạm vi 180 ° của nhau).

Nó tương đương về mặt toán học với việc thêm phần bù giúp dịch chuyển các giá trị vào phạm vi (0, 180), tính toán giá trị trung bình và sau đó trừ đi phần bù.

Các ý kiến ​​mô tả phạm vi mà một giá trị cụ thể có thể đảm nhận tại bất kỳ thời điểm nào

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

Câu trả lời của bạn (> 20 ký tự)

Bằng cách click "Đăng trả lời", bạn đồng ý với Điều khoản dịch vụ, Chính sách bảo mật and Chính sách cookie của chúng tôi.

Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ hoặc hỏi câu hỏi của bạn.