Наименьшая разница между 2 углами

137

Учитывая 2 угла в диапазоне -PI -> PI вокруг координаты, каково значение наименьшего из 2 углов между ними?

Принимая во внимание, что разница между PI и -PI составляет не 2 PI, а ноль.

Пример:

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

Том Дж Новелл
источник
2
Я прочитал 3 раза, прежде чем я понял, что вы имели в виду. Пожалуйста, добавьте пример или объясните лучше ...
Коби
Представьте себе круг с двумя линиями, выходящими из центра, между этими линиями есть 2 угла: угол, который они образуют внутри, то есть меньший угол, и угол, который они образуют снаружи, то есть больший угол. Оба угла при сложении образуют полный круг. Учитывая, что каждый угол может вписываться в определенный диапазон, каково значение меньших углов, принимая во внимание опрокидывание
Том Дж. Новелл
2
@JimG. это не тот же вопрос, в этом вопросе угол P1, использованный в другом вопросе, был бы неправильным ответом, это был бы другой, меньший угол. Кроме того, нет никакой гарантии, что угол будет с горизонтальной осью
Том Дж. Новелл

Ответы:

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
bennedich
источник
Проще и понятнее читать вслух, хотя, по сути, одно и то же, сначала bti вычисляет угол, вторая часть всегда гарантирует меньший из двух возможных углов
Tom J Nowell
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) для использования с плавающей запятой по модулю.
Джоппи
145

х - целевой угол. у - исходный или начальный угол:

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) - абс (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 # (его больше нет).
Кив
2
Обратите внимание, что начиная с Python 3, вы можете фактически использовать тау! Просто пиши from math import tau.
Мхартл
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
Но этот не содержит тригонометрических функций :)
nornagon
Какая эквивалентная формула для Java? если углы в градусах.
Soley
6

Для пользователей UnityEngine самый простой способ - использовать Mathf.DeltaAngle .

мистифицировать
источник
1
Не имеет подписанных выходных tho
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

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

Ули Гю
источник
7
Ошибка! Поскольку вы #define PIV2 определяете как «M_PI + M_PI», а не «(M_PI + M_PI)», строка arg = arg - PIV2;расширяется arg = arg - M_PI + M_PIи ничего не делает.
canton7