각도 비교 및 ​​차이 해결


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, + infinite) 범위에 있다고 가정 할 수 있습니까? 예를 들어, 알고리즘은 -130 도와 450을 비교하는 데에도 효과가 있습니까?
egarcia

각도가 해당 범위로 정규화되었다고 가정합니다.
Thomas O

답변:


29

여기에 단순화되고 분기가없는 비교없는 최소 / 최대 버전이 있습니다.

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

입력이 충분히 구속되어 모듈러스를 제거했습니다 (마틴에게 지적 해 주셔서 감사합니다).

복근 2 개, 빼기 3 개.


모듈로가 필요하지 않으며 입력 값이 [0,360] 범위로 제한됩니다 (원래 제출에 대한 Thomas의 의견 참조). 꽤 깔끔한.
Martin Sojka

아, 네 말이 맞아요 시도했을 때 덜 엄격한 입력을 받았습니다.
JasonD

그러나 차이의 부호를 유지하여 왼쪽에있는 것을 알 수 있다면 어떨까요?
Jacob Phillips

9

그것들은 제대로 비교되지만, 정확한 45도 대신 315 도입니다.

315가 왜 틀렸다고 생각합니까? 한 방향으로, 그것은 315도, 다른 방향으로, 그것은 45입니다. 가능한 두 각도 중 가장 작은 각도를 선택하고 싶고 이것은 본질적으로 조건을 요구하는 것 같습니다. 랩 어라운드 산술 (예 : 모듈러스 연산자를 통해)로 한 각도를 점차적으로 증가 시키면 180도에 도달 할 때까지 각도가 커지고 감소하기 때문에 풀 수 없습니다.

두 각도를 모두 확인하고 측정하려는 방향을 결정하거나 양방향을 계산하고 원하는 결과를 결정해야한다고 생각합니다.


죄송합니다. 명확히해야합니다. 반대로 바꾸면 30-345는 -315이고 음의 각도는별로 의미가 없습니다. 나는 둘 사이의 가장 작은 각도를 찾고 있다고 생각합니다. 즉 45 도는 315보다 작습니다.
Thomas O

2
그러나 '반전'은 없습니다. 두 가지 각도와 두 가지 유형의 회전을 통해 서로 일치시킬 수 있습니다. 음의 각도는 완벽한 의미를 갖습니다. 결국 임의의 축에서 회전하는 정도입니다.
Kylotan

가장 작은 각도를 원하면 abs (a1 % 180-a2 % 180)가 해당 각도를 제공합니다. 그러나 방향을 알려주지는 않습니다. 복근을 제거하면 A1 A2 "로" "에서가는"작은 각도를 줄 것이다
질긴 풍선 껌

2
@Chewy, 응? 180과 0의 차이는 0이 아니며 181과 0의 차이는 1이 아닙니다 ...
dash-tom-bang

1
@ dash-tom-bang 당신은 아주 옳습니다. 내가 무슨 생각을했는지 모르겠지만, 다시 한 번 봐도 전혀 틀 렸습니다. 내 이전 의견을 무시하십시오.
Chewy Gumball

4

항상 두 가지를 모두 수행하고 비교 결과가 하나를 선택하게하는 트릭이 있습니다.

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

나는 비교 없이 그것을 할 수있는 방법을 모른다 . 그러나 일반적으로 지점비교 하지 않고 코드를 느리고 길게 만드는 것입니다. 적어도 내 의견으로는, 이것은 Martin의 대답보다 더 읽기 쉽습니다 (좋은 C 프로그래머라면 그것을 분기가없는 것으로 인식하고 수행중인 작업을 볼 것입니다).

그러나 내 의견에서 언급했듯이 분기가없는 알고리즘은 파이프 라인이 깊고 예측이 나쁜 프로세서에서 좋습니다. 마이크로 컨트롤러에는 일반적으로 작은 파이프 라인이 있고 데스크톱 PC에는 일반적으로 좋은 예측이 있으므로 게임 콘솔을 대상으로하지 않는 한 분기 버전 명령어 수를 줄이면 가장 좋은 경로 일 것입니다.

항상 그렇듯이 프로파일 링 (시스템에 대한 op-counting만큼 간단 할 수도 있음)은 실제 답변을 제공합니다.


2

가정 진정한 -1 평가하고 거짓 '|'0으로 평가를하고, '~', '&'와 비트입니다 하지 , 또는 사업자 각각, 우리는 함께 작업하는 보수 연산 :

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하지만 마이크로 컨트롤러에서는 분기 버전보다 성능이 훨씬 떨어질 수 있습니다.

마이크로 컨트롤러에 약간 의존하지만, 일반적으로 가치가 없습니다. (짧은) 조건부 점프는 일반적으로 충분히 빠릅니다. 또한 xor (^) 연산을 사용하여 세 번째와 다섯 번째 줄을 조금 더 빠르게 다시 작성할 수 있지만 명확성을 위해 현재 형식으로 남았습니다. temp1 : = temp2 ^ ((temp2 ^ -temp2) & ~ temp1), temp2 : = temp1 ^ ((temp1 ^ (360-temp1)) & ~ temp2)
Martin Sojka

1

이건 어때?

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

음수의 모듈로가 음의 결과를 반환하기 때문에 360을 더하면 음의 차이를 피할 수 있습니다. 그런 다음 가능한 두 결과 중 작은 것을 얻습니다.

여전히 암묵적인 결정이 있지만이를 피하는 방법을 모르겠습니다. 기본적으로 차이를 시계 방향 또는 시계 반대 방향으로 계산하여 두 각도를 비교 하고이 두 가지 차이 중 더 작은 것을 명시 적으로 원하는 것 같습니다. 나는 그것들을 비교하지 않고 그 결과를 얻는 방법을 모른다. 즉, "abs", "min", "max"또는 이와 유사한 연산자를 사용하지 않습니다.


분기 명령어없이 int의 최소, 최대 및 abs를 계산하는 몇 가지 방법이 있지만, 이것이 마이크로 컨트롤러이므로 분기가 가장 빠른 방법 일 수 있습니다. 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
내가 작업하고있는 프로세서는 소형 마이크로 컨트롤러입니다. 모든 사이클이 중요하기 때문에 각도 간의 차이를 얻기 위해 벡터를 생성하기 위해 삼각 함수를 사용하는 것은 이치에 맞지 않습니다.
Thomas O

1
마이크로 컨트롤러에서는 브랜치를 사용하는 것이 낫지 않지만 놀랍게도 분기를 피하고 싶다면 내 대답에 많은 산술이 없습니다.
JasonD

분기는 두주기이고 더하기 / 빼기 등은 하나의주기이지만 분기는 추가 프로그램 메모리를 차지합니다. 중요하지는 않지만 좋을 것입니다.
Thomas O

나는 당신의 대답이 정확하고 내 것이 잘못되었다는 느낌을 얻지 만 그 이유에 대해 머리를 맞출 수는 없습니다 . :)
Kylotan

1

절대 값 함수 대신 비트 단위 연산을 사용하는 것을 제외하고는 기본적으로 JasonD의 대답과 동일합니다.

이것은 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

나는 생각한다

delta = (a2 + Math.ceil( -a2 / 360 ) * 360) - (a1 + Math.ceil( -a1 / 360 ) * 360);

0

산술 이외의 분기 및 "복잡한"연산 만 제거하므로 다음을 권장합니다.

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

여전히 필요합니다 abs모든 각도가 양수 임에도 불구하고 . 그렇지 않으면 가장 부정적인 결과가 항상 선택됩니다 (ab와 ba를 비교할 때 항상 긍정적이고 고유 한 a와 b에 대해 정확히 하나의 부정적인 답이있을 것입니다).

참고 : 각도 1과 각도 2 사이의 방향은 유지되지 않습니다. 때로는 AI 목적으로 필요합니다.

이것은 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

이론적 해석

모듈러스를 피하는 솔루션을 찾고, 이것을 알아 낸 후에이 스레드를 발견했습니다. 지금까지 나는 아무것도 찾지 못했다 . @ jacob-phillips 가이 의견을 물었 듯이이 솔루션은 원근 기호를 유지하기위한 것 입니다. 가장 짧은 부호없는 각도가 필요한 경우 저렴한 솔루션이 있습니다.


0

그것은 오래된 질문이지만 같은 경우가 발생했습니다-부호가있는 각도 차이를 가져야했고 가급적이면 가지와 무거운 수학이 없어야했습니다. 이것이 내가 끝낸 것입니다.

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

한계는 'b'가 'a'에 비해 'N'회전을 초과하지 않아야한다는 것입니다. 그것을 보장 할 수없고 추가 작업을 허용 할 수 있다면 이것을 첫 번째 줄로 사용하십시오 :

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

나는이 게시물의 13 코멘트에서 아이디어를 얻었다 : http://blog.lexique-du-net.com/index.php?post/Calculate-the-real-difference-between-two-angles-keeping-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, abs (345-30)는 여전히 315입니다.
Gregory Avery-Weir

@Gregory : 알았어! 실수해서 미안해. 답장을 수정하고 있습니다. 새로운 답을 확인하십시오. :)
Vishnu

1
그런데, angle1 = angle1 % 360; angle2 = 각 2 % 360; var distance = Math.abs (angle1-angle2); var distance = Math.abs (angle1-angle2) % 360과 동일합니다-조금 느립니다.
Martin Sojka
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.