कोणों की तुलना करना और अंतर का पता लगाना


27

मैं कोणों की तुलना करना चाहता हूं और उनके बीच की दूरी का अंदाजा लगाना चाहता हूं। इस एप्लिकेशन के लिए, मैं डिग्री में काम कर रहा हूं, लेकिन यह रेडियन और ग्रेड के लिए भी काम करेगा। कोणों के साथ समस्या यह है कि वे मॉड्यूलर अंकगणित पर निर्भर करते हैं, अर्थात 0-360 डिग्री।

कहते हैं कि एक कोण 15 डिग्री पर है और एक 45 पर है। अंतर 30 डिग्री है, और 45 डिग्री कोण 15 डिग्री एक से अधिक है।

लेकिन, यह टूट जाता है जब आप कहते हैं, 345 डिग्री और 30 डिग्री। हालांकि वे ठीक से तुलना करते हैं, उनके बीच का अंतर सही 45 डिग्री के बजाय 315 डिग्री है।

इसे कैसे हल किया जा सकता है? मैं एल्गोरिथम कोड लिख सकता हूं:

if(angle1 > angle2) delta_theta = 360 - angle2 - angle1;
else delta_theta = angle2 - angle1;

लेकिन मैं एक ऐसा समाधान पसंद करूंगा जो तुलना / शाखाओं से बचता है, और पूरी तरह से अंकगणित पर निर्भर करता है।


इस समस्या पर, क्या हम मान सकते हैं कि दिए गए कोण [0,360] या (-infinite, + अनंत) श्रेणी में हैं? उदाहरण के लिए, क्या एल्गोरिथ्म को 450 के साथ -130 डिग्री की तुलना पर भी काम करना चाहिए?
इगोरिया

मान लें कि कोण उस सीमा तक सामान्यीकृत हैं।
थॉमस ओ

जवाबों:


29

यहाँ मेरा सरलीकृत, शाखा रहित, तुलना-रहित, कोई न्यूनतम / अधिकतम संस्करण नहीं है:

angle = 180 - abs(abs(a1 - a2) - 180); 

मोडुलो को हटा दिया, क्योंकि इनपुट पर्याप्त रूप से विवश हैं (मार्टिन को धन्यवाद कि बाहर इंगित करने के लिए)।

दो एब्स, तीन घटाव।


आपको मोडुलो की आवश्यकता नहीं है, इनपुट मान [0,360] सीमा तक सीमित हैं (मूल प्रस्तुत करने के लिए थॉमस की टिप्पणी देखें)। काफी साफ़।
मार्टिन सोज्का

आह, हाँ, तुम सही हो। जब मैंने इसे आज़माया तो मेरे पास कम सख्त इनपुट था।
जेसन

लेकिन क्या होगा अगर आप अंतर के संकेत को संरक्षित करना चाहते हैं ताकि आप बता सकें कि बाईं तरफ कौन था?
याकूब फिलिप्स

9

हालांकि वे ठीक से तुलना करते हैं, उनके बीच का अंतर सही 45 डिग्री के बजाय 315 डिग्री है।

आपको क्या लगता है कि 315 गलत है? एक दिशा में, यह 315 डिग्री है, दूसरी दिशा में, यह 45 है। आप जो भी चुनना चाहते हैं वह 2 संभावित कोणों में सबसे छोटा है और ऐसा लगता है कि आंतरिक रूप से एक सशर्त की आवश्यकता है। आप इसे रैप-अराउंड अरिथमेटिक (यानी मॉडुलस ऑपरेटर के माध्यम से) से हल नहीं कर सकते हैं क्योंकि जब आप धीरे-धीरे एक कोण बढ़ाते हैं तो उनके बीच का कोण 180 तक बढ़ता है और फिर घटने लगता है।

मुझे लगता है कि आपको या तो दोनों कोणों की जांच करनी होगी और यह तय करना होगा कि आप किस दिशा को मापना चाहते हैं, या दोनों दिशाओं की गणना करें और तय करें कि आपको कौन सा परिणाम चाहिए।


क्षमा करें मुझे स्पष्ट करना चाहिए। यदि आपने इसे उल्टा किया है, तो 30 - 345 -315 है और एक नकारात्मक कोण बहुत मायने नहीं रखता है। मुझे लगता है कि मैं दोनों के बीच सबसे छोटे कोण की तलाश कर रहा हूं। यानी 45 डिग्री 315 से छोटा है।
थॉमस ओ

2
लेकिन कोई 'रिवर्स' नहीं है - आपके पास 2 कोण और 2 प्रकार के रोटेशन हैं जो आप एक मैच को दूसरे बनाने के लिए प्रदर्शन कर सकते हैं। एक नकारात्मक कोण सही अर्थ बनाता है - यह एक मनमाना अक्ष से रोटेशन का एक उपाय है, सब के बाद।
कियलोतन

यदि आप सबसे छोटा कोण चाहते हैं तो एब्स (a1% 180 - a2% 180) आपको वह कोण देगा। हालांकि, यह आपको दिशा नहीं बताएगा। एब्स को हटाने से आपको "ए 1" से "ए 2" तक जाने वाला सबसे छोटा कोण मिलेगा "
चेवी गंबल

2
@ काहे, हुह? 180 और 0 के बीच अंतर 0 नहीं है, और 181 और 0 के बीच का अंतर 1 नहीं है ...
डैश-टॉम-बैंग

1
@ डैश-टॉम-बैंग आप काफी सही हैं। मुझे नहीं पता कि मैं क्या सोच रहा था, लेकिन यह बिल्कुल भी सही नहीं था कि मैं इसे फिर से देखता हूं। कृपया मेरी पिछली टिप्पणी की अवहेलना करें।
Chewy Gumball

4

हमेशा दोनों शाखाओं को करने और तुलना परिणाम को चुनने देने की चाल है:

delta_theta = (angle1 > angle2) * (360 - angle2 - angle1)
              + (angle2 > angle1) * (angle2 - angle1);

मुझे तुलना के बिना इसे करने का एक तरीका नहीं पता है , लेकिन आमतौर पर शाखा वह है जो कोड को धीमा और लंबा बनाती है, तुलना नहीं। कम से कम मेरी राय में, यह मार्टिन के जवाब की तुलना में अधिक पठनीय है (कोई भी अच्छा सी प्रोग्रामर इसे एक शाखाहीन समकक्ष के रूप में मान्यता देगा और देखें कि यह क्या कर रहा है), लेकिन यह भी कम कुशल है।

लेकिन जैसा कि मैंने अपनी टिप्पणी में कहा, गहरे पाइपलाइनों और खराब भविष्यवाणी वाले प्रोसेसर पर शाखा रहित एल्गोरिदम अच्छे हैं - एक माइक्रोकंट्रोलर में आमतौर पर एक छोटी पाइप लाइन होती है, और एक डेस्कटॉप पीसी में आमतौर पर अच्छी भविष्यवाणी होती है, इसलिए जब तक आप गेमिंग कंसोल, ब्रांच संस्करण को लक्षित नहीं करते हैं अगर यह निर्देश गिनती को कम करता है तो संभवतः सबसे अच्छा मार्ग है।

हमेशा की तरह, प्रोफाइलिंग - जो आपके सिस्टम के लिए ऑप-काउंटिंग जितनी सरल हो सकती है - आपको असली जवाब देगी।


2

सच मानकर -1 और झूठ का मूल्यांकन 0, और '~', 'और' और '| बिटवाइज़ हैं नहीं , और और या ऑपरेटरों क्रमशः, और हम साथ काम कर रहे two's-पूरक गणित कर रहे हैं:

temp1 := angle1 > angle2
/* most processors can do this without a jump; for example, under the x86 family,
   it's the result of CMP; SETLE; SUB .., 1 instructions */
temp2 := angle1 - angle2
temp1 := (temp1 & temp2) | (~temp1 & -temp2)
/* in x86 again: only SUB, AND, OR, NOT and NEG are used, no jumps
   at this point, we have the positive difference between the angles in temp1;
   we can now do the same trick again */
temp2 := temp1 > 180
temp2 := (temp2 & temp1) | (~temp2 & (360 - temp1))
/* the result is in temp2 now */

+1 क्योंकि यह चतुर है, लेकिन एक माइक्रोकंट्रोलर पर यह संभवतः ब्रांचिंग संस्करण की तुलना में बहुत खराब है।

माइक्रोकंट्रोलर पर थोड़ा निर्भर करता है, लेकिन, हाँ, यह आमतौर पर इसके लायक नहीं है; एक (छोटी) सशर्त कूद आमतौर पर काफी तेज होती है। इसके अलावा, तीसरी और पांचवीं पंक्तियों को इस तरह से एक्सोर (^) ऑपरेशन का उपयोग करके थोड़ा तेज होने के लिए फिर से लिखा जा सकता है, लेकिन मैंने उन्हें वर्तमान रूप में स्पष्टता के लिए छोड़ दिया: temp1: = temp2 ^ ((temp2 ^ -temp2) & ~ temp1), temp2: = temp1 ^ ((temp1 ^ (360 - temp1)) & ~ temp2)
मार्टिन सोजका

1

इस बारे में क्या?

min( (a1-a2+360)%360, (a2-a1+360)%360 )

नकारात्मक अंतर से बचने के लिए 360 का जोड़ वहाँ है, क्योंकि एक ऋणात्मक संख्या का एक मोड्यूल नकारात्मक परिणाम देता है। तब आपको दो संभावित परिणामों में से छोटा मिलता है।

अभी भी एक निहित निर्णय है, लेकिन मुझे नहीं पता कि इससे कैसे बचा जाए। मूल रूप से आप अंतर को दक्षिणावर्त या वामावर्त गणना करके दो कोणों की तुलना करते हैं, और ऐसा लगता है कि आप स्पष्ट रूप से इन दो अंतरों में से छोटा चाहते हैं। मुझे नहीं पता कि उनकी तुलना किए बिना उस परिणाम को कैसे प्राप्त किया जाए। अर्थात्, "एब्स", "मिनट", "अधिकतम" या कुछ समान ऑपरेटर का उपयोग किए बिना।


शाखा निर्देशों के बिना मिनट, अधिकतम, और पेटी की अनुपस्थिति की गणना करने के कई तरीके हैं, हालांकि यह एक माइक्रोकंट्रोलर है क्योंकि शाखा शायद सबसे तेज़ तरीका है। graphics.stanford.edu/~seander/bithacks.html#IntegerAbs

1

जबकि आपके प्रश्न ने उनका कोई संदर्भ नहीं दिया है, मैं इस धारणा पर काम करने जा रहा हूं कि आपके कोण की गणना का प्रश्न दो वैक्टरों के बीच न्यूनतम कोण जानना चाहता है ।

वह गणना आसान है। मान लें कि A और B आपके वैक्टर हैं:

angle_between = acos( Dot( A.normalized, B.normalized ) )

यदि आपके पास वैक्टर नहीं हैं और आप इस दृष्टिकोण का उपयोग करना चाहते हैं, तो आप यूनिट लंबाई वाले वैक्टर का निर्माण कर सकते हैं जो आपके कोणों को करते हैं new Vector2( cos( angle ), sin ( angle ) )


1
मैं जिस प्रोसेसर पर काम कर रहा हूं वह एक छोटा माइक्रोकंट्रोलर है। इसका मतलब यह नहीं है कि एक वेक्टर उत्पन्न करने के लिए ट्रिग फ़ंक्शंस का उपयोग करना कोणों के बीच अंतर पाने के लिए, हर चक्र कीमती है।
थॉमस ओ

1
एक माइक्रोकंट्रोलर पर मैं एक तरह से आश्चर्यचकित हूं कि एक शाखा का उपयोग करना बेहतर नहीं है, लेकिन अगर आप वास्तव में शाखा से बचना चाहते हैं, तो मेरे उत्तर में बहुत अधिक अंकगणित नहीं है।
जेसन

ठीक है, एक शाखा दो चक्र है और एक जोड़ / घटाना / आदि एक चक्र है, लेकिन ब्रांचिंग अतिरिक्त कार्यक्रम मेमोरी भी लेता है। यह महत्वपूर्ण नहीं है, लेकिन यह अच्छा होगा।
थॉमस ओ

मैं अपने जवाब महसूस कर सही है और मेरा गलत है, लेकिन मैं चारों ओर मेरे सिर नहीं मिल सकता है क्यों कि मामला है। :)
कियलोतन

1

मूल रूप से जेसन के उत्तर के समान ही, निरपेक्ष मान फ़ंक्शन के बजाय बिटवाइज़ संचालन का उपयोग करने के अलावा।

यह आपको 16-बिट शॉर्ट पूर्णांक मान रहा है!

short angleBetween(short a,short b) {
    short x = a - b;
    short y = x >> 15;
    y = ((x + y) ^ y) - 180;
    return 180 - ((x + y) ^ y);
}


0

चूंकि आप केवल अंकगणित से परे शाखाओं और "जटिल" संचालन को खत्म करने के बारे में परवाह करते हैं, इसलिए मैं इसकी सिफारिश करूंगा:

min(abs(angle1 - angle2), abs(angle2 - angle1))

absसभी कोणों के सकारात्मक होने के बावजूद आपको अभी भी वहाँ एक की आवश्यकता है । अन्यथा, सबसे नकारात्मक परिणाम हमेशा चुना जाएगा (और एब और बा की तुलना करते समय सकारात्मक, अद्वितीय ए और बी के लिए हमेशा एक नकारात्मक उत्तर होगा)।

नोट: यह कोण 1 और कोण 2 के बीच की दिशा को संरक्षित नहीं करेगा। कभी-कभी आपको एआई उद्देश्यों के लिए इसकी आवश्यकता होती है।

यह CeeJay के उत्तर के समान है, लेकिन सभी मोड्यूल को समाप्त करता है। मुझे नहीं पता कि साइकिल की लागत क्या है abs, लेकिन मुझे लगता है कि यह 1 या 2 है। यह कहना मुश्किल है कि लागत क्या है min। शायद 3? तो प्रति चक्र 1 घटाव के साथ, इस रेखा की लागत कहीं 4 से 9 के आसपास होनी चाहिए।


0

जाओ कम सापेक्ष में कोण पर हस्ताक्षर किए , (+/-) फार्म के नजरिए से है की दिशा में करना चाहते हैं :

  • अधिकतम 180 डिग्री पर | PI रेडियन
  • -अगर एंटिक्लॉकवाइज दिया जाए
  • + अगर दक्षिणावर्त हस्ताक्षरित

डिग्री

PITAU = 360 + 180 # for readablility
signed_diff = ( want - have + PITAU ) % 360 - 180

रेडियंस

PI = 3.14; TAU = 2*PI; PITAU = PI + TAU;
signed_diff = ( want - have + PITAU ) % TAU - PI

दलील

मैं इस धागे के बाद आया था जब मैंने यह पता लगाया था, एक समाधान की तलाश में था जो मोडुलो से बचा जाता है; अब तक मुझे कोई नहीं मिला है । यह समाधान परिप्रेक्ष्य संकेत को संरक्षित करने के लिए है, जैसा कि @ जेकब-फिलिप्स ने यह टिप्पणी की । यदि आपको सबसे कम अहस्ताक्षरित कोण की आवश्यकता है तो सस्ता समाधान है।


0

यह एक पुराना सवाल है, लेकिन मैं एक ही मामले में भाग गया - हस्ताक्षरित कोणीय अंतर और अधिमानतः शाखाओं और भारी गणित के बिना प्राप्त करना था। यह वही है जो मैंने समाप्त किया:

int d = (a - b) + 180 + N * 360; // N = 1, 2 or more.
int r = (d / 360) * 360;
return (d - r) - 180;

सीमा यह है कि 'बी' में 'ए' की तुलना में 'एन' घुमाव से अधिक नहीं होना चाहिए। यदि आप इसे सुनिश्चित नहीं कर सकते हैं और अतिरिक्त संचालन की अनुमति दे सकते हैं तो इसे पहली पंक्ति के रूप में उपयोग करें:

int d = ((a % 360) - (b % 360)) + 540;

मुझे इस पोस्ट की 13 वीं टिप्पणी से विचार मिला: http://blog.lexique-du-net.com/index.php?post/Calculate-the-real-difference-between-two-angles-keeps-the- संकेत


-1

मुझे लगता है मैं कह सकता था

angle1=angle1%360;
angle2=angle2%360;
var distance = Math.abs(angle1-angle2);
//edited
if(distance>180)
  distance=360-distance;

कोण, डिग्री में मापा जाता है पर विचार।


1
मुझे विश्वास नहीं है कि यह प्रश्न में समस्या को हल करता है। 345% 360 == 345, और एब्स (345-30) अभी भी 315 है।
ग्रेगरी एवरी-वियर

@ समूह: ठीक है!, मुझे गलती के लिए खेद है। मैं उत्तर का संपादन कर रहा हूं, इस नए की जांच करें। :)
विष्णु

1
वैसे, एंगल 1 = एंगल 1% 360; angle2 = angle2% 360; var दूरी = Math.abs (angle1-angle2); var दूरी = Math.abs (angle1-angle2)% 360 के समान ही है - बस धीमा।
मार्टिन सोज्का
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.