Найменша різниця між 2 кутами


137

Враховуючи 2 кути в діапазоні -PI -> PI навколо координати, яке значення найменшого з 2 кутів між ними?

Беручи до уваги, що різниця між PI і -PI не 2 PI, а нуль.

Приклад:

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


2
Я прочитав 3 рази, перш ніж зрозумів, що ти маєш на увазі. Будь ласка, додайте приклад або поясніть краще ...
Кобі

Уявіть собі коло, у якому дві лінії розташовуються від центру, між цими лініями є 2 кути, кут, який вони роблять на внутрішній частині, як менший кут, і кут, який вони роблять зовні, як і більший кут. Обидва кути при складанні складають повне коло. Зважаючи на те, що кожен кут може вміщуватися в певному діапазоні, яке менше значення кутів, враховуючи перекидання
Том Дж. Ноуелл


2
@JimG. це не те саме питання, в цьому питанні кут Р1, використаний в іншому питанні, був би невірною відповіддю, це був би інший, менший кут. Також немає гарантії того, що кут знаходиться з горизонтальною віссю
Том Дж. Ноуелл

Відповіді:


193

Це дає підписаний кут для будь-яких кутів:

a = targetA - sourceA
a = (a + 180) % 360 - 180

Остерігайтеся, що для багатьох мов moduloоперація повертає значення з тим же знаком, що і дивіденд (наприклад, C, C ++, C #, JavaScript, повний список тут ). Для цього потрібна спеціальна modфункція на зразок:

mod = (a, n) -> a - floor(a/n) * n

Або так:

mod = (a, n) -> (a % n + n) % n

Якщо кути знаходяться в межах [-180, 180], це також працює:

a = targetA - sourceA
a += (a>180) ? -360 : (a<-180) ? 360 : 0

Більш докладно:

a = targetA - sourceA
a -= 360 if a > 180
a += 360 if a < -180

Простіше і більше сенсу читати вголос, хоча ефективно те саме, перша bti визначає кут, друга частина гарантує, що його завжди менший з 2 можливих кутів
Том Дж. Ноуелл,

1
хоча, можливо, хочеться зробити% 360, наприклад, якби у мене був кут 0 та цільовий кут 721, правильна відповідь була б 1, відповідь, подана вище, була б 361
Том Дж. Ноуелл,

1
Більш стислий, хоча потенційно дорожчий, еквівалент другого твердження останнього підходу, є a -= 360*sgn(a)*(abs(a) > 180). (Подумайте про це, якщо у вас є безгалузеві реалізації sgnта abs, то ця характеристика може насправді почати компенсувати необхідність двох множень.)
mmirate

1
Приклад "Підписаний кут для будь-якого кута", здається, працює в більшості сценаріїв, за одним винятком. У сценарії double targetA = 2; double sourceA = 359;"а" буде дорівнювати -357,0 замість 3,0
Stevoisiak

3
У C ++ ви можете використовувати std :: fmod (a, 360) або fmod (a, 360), щоб використовувати модуль з плаваючою комою.
Joeppie

145

x - цільовий кут. y - початковий або початковий кут:

atan2(sin(x-y), cos(x-y))

Він повертає підписаний дельта кут. Зауважте, що залежно від вашого API порядок параметрів функції atan2 () може бути різним.


13
x-yдає різницю в куті, але це може бути поза бажаними межами. Подумайте, як цей кут визначає точку на одиничному колі. Координати цієї точки є (cos(x-y), sin(x-y)). atan2повертає кут для цієї точки (що еквівалентно x-y), за винятком його діапазону [-PI, PI].
Макс

3
Це проходить тестовий набір gist.github.com/bradphelan/7fe21ad8ebfcb43696b8
bradgonesurfing

2
однорядне просте рішення і вирішене для мене (не обрана відповідь;)). але загар - це дорогий процес.
Мохан Кумар

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

Для мене також найелегантніше рішення! Я вирішив свою проблему ідеально (хотів мати формулу, яка дає мені підписаний кут повороту, який менший від двох можливих напрямків / кутів повороту).
Юрген Брауер

41

Якщо ваші два кути дорівнюють x і y, то один з кутів між ними - abs (x - y). Інший кут - (2 * PI) - abs (x - y). Отже значення найменшого з 2 кутів дорівнює:

min((2 * PI) - abs(x - y), abs(x - y))

Це дає вам абсолютне значення кута, і він передбачає, що входи нормалізуються (тобто: в межах діапазону [0, 2π)).

Якщо ви хочете зберегти знак (тобто: напрямок) кута, а також прийняти кути за межами діапазону, [0, 2π)ви можете узагальнити вище. Ось код Python для узагальненої версії:

PI = math.pi
TAU = 2*PI
def smallestSignedAngleBetween(x, y):
    a = (x - y) % TAU
    b = (y - x) % TAU
    return -a if a < b else b

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


1
@bradgonesurfing Це / було правдою, але, щоб бути справедливими, ваші тести перевірялися на предмет, які не були вказані в первісному запитанні, зокрема ненормовані введення та збереження знаків. Друга версія відредагованої відповіді повинна пройти тести.
Лоранс Гонсалвс

Друга версія також не працює для мене. Спробуйте, наприклад, 350 і 0. Він повинен повернутися -10, але повертається -350
kjyv

@kjyv Я не можу відтворити описану вами поведінку. Чи можете ви розмістити точний код?
Лоранс

Ах, пробач. Я перевірив саме вашу версію з rad і ступенями в python знову, і вона спрацювала чудово. Тож, мабуть, була помилка в моєму перекладі на C # (не маю цього більше).
kjyv

2
Зауважте, що, як і в Python 3, ви можете фактично використовувати тау самобутньо! Просто напишіть from math import tau.
mhartl

8

Я ставлюся до завдання надання підписаної відповіді:

def f(x,y):
  import math
  return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)

1
Ах ... відповідь - це функція Python, до речі. Вибачте, я був на хвилину в режимі Python. Сподіваюся, що це нормально.
Девід Джонс

Я підключу нову формулу до свого коду наверх та побачу, що з цього вийде! (подяка ^ _ ^)
Том Дж. Ноуелл

1
Я майже впевнений, що відповідь PeterB також вірна. І зле хакі. :)
Девід Джонс

4
Але ця функція не містить
триггерних

Яка еквівалентна формула для Java? якщо кути знаходяться в ступені.
Солей

6

Для користувачів UnityEngine найпростішим способом є лише використання Mathf.DeltaAngle .


1
Не має підписаного результату, тому
kjyv


2

Ефективний код у C ++, який працює під будь-яким кутом і в обох: радіанах та градусах:

inline double getAbsoluteDiff2Angles(const double x, const double y, const double c)
{
    // c can be PI (for radians) or 180.0 (for degrees);
    return c - fabs(fmod(fabs(x - y), 2*c) - c);
}

-1

Не потрібно обчислювати тригонометричні функції. Простий код на мові С:

#include <math.h>
#define PIV2 M_PI+M_PI
#define C360 360.0000000000000000000
double difangrad(double x, double y)
{
double arg;

arg = fmod(y-x, PIV2);
if (arg < 0 )  arg  = arg + PIV2;
if (arg > M_PI) arg  = arg - PIV2;

return (-arg);
}
double difangdeg(double x, double y)
{
double arg;
arg = fmod(y-x, C360);
if (arg < 0 )  arg  = arg + C360;
if (arg > 180) arg  = arg - C360;
return (-arg);
}

нехай dif = a - b, в радіанах

dif = difangrad(a,b);

нехай dif = a - b, в градусах

dif = difangdeg(a,b);

difangdeg(180.000000 , -180.000000) = 0.000000
difangdeg(-180.000000 , 180.000000) = -0.000000
difangdeg(359.000000 , 1.000000) = -2.000000
difangdeg(1.000000 , 359.000000) = 2.000000

Ні гріха, ні cos, ні загару, .... тільки геометрія !!!!


7
Баг! Оскільки ви #define PIV2 як "M_PI + M_PI", а не "(M_PI + M_PI)", рядок arg = arg - PIV2;розширюється на arg = arg - M_PI + M_PI, і тому нічого не робить.
canton7
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.