अकब सही है कि इन वेक्टर आधारित समाधानों को कोणों का सही औसत नहीं माना जा सकता है, वे केवल यूनिट वेक्टर समकक्षों का एक औसत हैं। हालाँकि, ackb का सुझाया गया समाधान गणितीय रूप से ध्वनि के लिए प्रकट नहीं होता है।
निम्नलिखित एक ऐसा समाधान है जो गणितीय रूप से न्यूनतम (कोण [i] - avgAngle) ^ 2 के लक्ष्य से लिया गया है (2 जहां अंतर आवश्यक होने पर सही किया जाता है), जो इसे कोणों का एक वास्तविक अंकगणितीय माध्य बनाता है।
सबसे पहले, हमें यह देखने की जरूरत है कि कोणों के बीच अंतर उनके सामान्य संख्या समकक्षों के बीच अंतर के लिए अलग है। कोण x और y पर विचार करें, यदि y> = x - 180 और y <= x + 180 है, तो हम अंतर का उपयोग कर सकते हैं (xy)। अन्यथा, यदि पहली शर्त पूरी नहीं होती है, तो हमें y के बजाय गणना में (y + 360) का उपयोग करना चाहिए। इसके विपरीत, यदि दूसरी शर्त पूरी नहीं होती है, तो हमें y के बजाय (y-360) का उपयोग करना चाहिए। वक्र के समीकरण के बाद से हम केवल उन बिंदुओं पर परिवर्तन को कम कर रहे हैं जहां ये असमानताएं सही से गलत या इसके विपरीत बदल जाती हैं, हम पूर्ण [0,360) रेंज को इन बिंदुओं से अलग करके खंडों के एक समूह में बदल सकते हैं। फिर, हमें केवल इनमें से प्रत्येक सेगमेंट का न्यूनतम पता लगाना है, और फिर प्रत्येक सेगमेंट का न्यूनतम, जो कि औसत है।
यहां एक छवि प्रदर्शित की गई है जहां कोण अंतर की गणना में समस्याएं आती हैं। यदि x ग्रे क्षेत्र में स्थित है तो समस्या होगी।
एक चर को कम करने के लिए, वक्र के आधार पर, हम व्युत्पन्न कर सकते हैं कि हम क्या कम करना चाहते हैं और फिर हम मोड़ पाते हैं (जो कि व्युत्पन्न = 0 है)।
यहाँ हम सामान्य अंकगणितीय माध्य सूत्र को प्राप्त करने के लिए वर्ग अंतर को कम करने के विचार को लागू करेंगे: योग (a [i] / n। वक्र y = योग (([[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 = जहाँ सही (कोणीय) अंतर का उपसमूह [i] -xc = जहाँ सही (कोणीय) अंतर का उपसमूह ([i] -360) -x cn = cd का आकार = जहाँ एक का सबसेट सही (कोणीय) अंतर ([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 मोड़ होता है) और यदि न्यूनतम हमारे सेगमेंट के ऊपरी बाउंड से अधिक है तो सेगमेंट की न्यूनतम सीमा पर है ऊपरी सीमा। हमारे पास प्रत्येक सेगमेंट के लिए न्यूनतम होने के बाद, हम बस वही पाते हैं जिसका न्यूनतम मूल्य है जो हम कम से कम कर रहे हैं (राशि (b [i] -x) ^ 2) + योग ((c [i] -360) ) -b) ^ 2) + योग ((d [i] +360) -c) ^ 2))।
यहां वक्र के लिए एक छवि है, जो दिखाता है कि यह उन बिंदुओं पर कैसे बदलता है जहां x = ([[i] +180)% 360 है। डेटा सेट विचाराधीन है {65,92,230,320,250}।
यहां जावा में एल्गोरिथ्म का कार्यान्वयन है, जिसमें कुछ अनुकूलन भी शामिल हैं, इसकी जटिलता हे (nlogn) है। इसे घटाकर O (n) किया जा सकता है यदि आप तुलना आधारित प्रकार को गैर-तुलना आधारित सॉर्ट के साथ प्रतिस्थापित करते हैं, जैसे कि मूलांक सॉर्ट।
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 है, हालांकि यह सर्वविदित है कि अंकगणित माध्य धार मूल्यों से अत्यधिक प्रभावित है। आपको यह भी याद रखना चाहिए कि कोण वैक्टर नहीं हैं, जैसा कि अपील करते हैं कि कभी-कभी कोणों के साथ काम करते समय लग सकता है।
यह एल्गोरिथ्म निश्चित रूप से उन सभी मात्राओं पर भी लागू होता है जो मॉड्यूलर अंकगणित (न्यूनतम समायोजन के साथ) का पालन करते हैं, जैसे कि दिन का समय।
मैं यह भी कहना चाहूंगा कि भले ही यह वेक्टर समाधानों के विपरीत कोणों का एक सही औसत है, लेकिन इसका मतलब यह नहीं है कि यह वह समाधान है जिसका आपको उपयोग करना चाहिए, इसी यूनिट वैक्टर का औसत वास्तव में आपके लिए मूल्य हो सकता है का उपयोग करना चाहिए।