A menor diferença entre 2 ângulos


137

Dados 2 ângulos no intervalo -PI -> PI em torno de uma coordenada, qual é o valor do menor dos 2 ângulos entre eles?

Tendo em conta que a diferença entre PI e -PI não é 2 PI, mas zero.

Exemplo:

Imagine um círculo, com 2 linhas saindo do centro, existem 2 ângulos entre essas linhas, o ângulo que fazem no interior, também conhecido como ângulo menor , e o ângulo que fazem do lado de fora, também conhecido como ângulo maior. Ambos os ângulos, quando somados, formam um círculo completo. Dado que cada ângulo pode caber dentro de um determinado intervalo, qual é o menor valor de ângulos, levando em consideração a rolagem


2
Eu li três vezes antes de entender o que você quis dizer. Por favor, adicione um exemplo ou explique melhor ...
Kobi

Imagine um círculo, com 2 linhas saindo do centro, existem 2 ângulos entre essas linhas, o ângulo que fazem no interior, também conhecido como ângulo menor, e o ângulo que fazem do lado de fora, também conhecido como ângulo maior. Ambos os ângulos, quando somados, formam um círculo completo. Dado que cada ângulo pode caber dentro de um determinado intervalo, qual é o valor ângulos menores, tendo em conta o rollover
Tom J Nowell


2
@JimG. essa não é a mesma pergunta; nessa questão, o ângulo P1 usado na outra pergunta seria a resposta incorreta, seria o outro ângulo menor. Além disso, não há garantia de que o ângulo é com o eixo horizontal
Tom J Nowell

Respostas:


193

Isso fornece um ângulo assinado para qualquer ângulo:

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

Cuidado em muitos idiomas: a modulooperação retorna um valor com o mesmo sinal que o dividendo (como C, C ++, C #, JavaScript, lista completa aqui ). Isso requer uma modfunção personalizada da seguinte forma:

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

Ou então:

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

Se os ângulos estão dentro de [-180, 180], isso também funciona:

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

De uma maneira mais detalhada:

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

Mais simples e faz mais sentido ler em voz alta, embora efetivamente a mesma coisa, primeiras figuras IPV fora o ângulo, segunda parte torna-se sua sempre o menor dos 2 ângulos possíveis
Tom J Nowell

1
embora se queira fazer um% 360, por exemplo, se eu tivesse o ângulo 0 e o ângulo alvo 721, a resposta correta seria 1, a resposta dada acima seria 361
Tom J Nowell

1
Um equivalente mais conciso, embora potencialmente mais caro, da segunda declaração da última abordagem, é a -= 360*sgn(a)*(abs(a) > 180). (Venha para pensar sobre isso, se você tiver implementações sem ramos de sgne abs, em seguida, que o poder característica realmente começar a compensar precisando duas multiplicações.)
mmirate

1
O exemplo "Ângulo assinado para qualquer ângulo" parece funcionar na maioria dos cenários, com uma exceção. No cenário double targetA = 2; double sourceA = 359;'a' será igual a -357,0 em vez de 3.0
Stevoisiak

3
No C ++, você pode usar std :: fmod (a, 360) ou fmod (a, 360) para usar o módulo de ponto flutuante.
Joeppie

145

x é o ângulo alvo. y é a fonte ou o ângulo inicial:

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

Retorna o ângulo delta assinado. Observe que, dependendo da sua API, a ordem dos parâmetros para a função atan2 () pode ser diferente.


13
x-yfornece a diferença de ângulo, mas pode estar fora dos limites desejados. Pense neste ângulo que define um ponto no círculo unitário. As coordenadas desse ponto são (cos(x-y), sin(x-y)). atan2retorna o ângulo para esse ponto (que é equivalente a x-y), exceto que seu intervalo é [-PI, PI].
Max


2
uma solução simples de uma linha e resolvida para mim (não a resposta selecionada;)). mas o bronzeado inverso é um processo caro.
Mohan Kumar

2
Para mim, a solução mais elegante. Pena que possa ser computacionalmente caro.
focs

Para mim, a solução mais elegante também! Resolvi meu problema perfeitamente (queria ter uma fórmula que me desse o ângulo de virada assinado , que é o menor dentre as duas direções / ângulos de virada possíveis).
Jürgen Brauer

41

Se seus dois ângulos são x e y, então um dos ângulos entre eles é abs (x - y). O outro ângulo é (2 * PI) - abs (x - y). Portanto, o valor do menor dos 2 ângulos é:

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

Isso fornece o valor absoluto do ângulo e assume que as entradas estão normalizadas (ou seja: dentro da faixa [0, 2π)).

Se você deseja preservar o sinal (ou seja: direção) do ângulo e também aceitar ângulos fora do intervalo, [0, 2π)você pode generalizar o que foi descrito acima. Aqui está o código Python para a versão generalizada:

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

Observe que o %operador não se comporta da mesma maneira em todos os idiomas, principalmente quando valores negativos estão envolvidos; portanto, se for necessário portar alguns ajustes de sinal.


1
@bradgonesurfing Isso é / era verdade, mas, para ser justo, seus testes verificaram coisas que não foram especificadas na pergunta original, especificamente entradas não normalizadas e preservação de sinais. A segunda versão na resposta editada deve passar nos testes.
Laurence Gonsalves

A segunda versão também não funciona para mim. Tente 350 e 0, por exemplo. Ele deve retornar -10 mas retorna -350
kjyv

@kjyv Não consigo reproduzir o comportamento que você descreve. Você pode postar o código exato?
Laurence Gonsalves

Ah, me desculpe. Testei exatamente sua versão com rad e graus em python novamente e funcionou bem. Então, deve ter sido um erro na minha tradução para C # (não o tenho mais).
kjyv

2
Observe que, a partir do Python 3, você pode realmente usar o tau nativamente! Apenas escreva from math import tau.
mhartl 8/01

8

Eu enfrento o desafio de fornecer a resposta assinada:

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

1
Ah ... a resposta é uma função Python, a propósito. Desculpe, eu estava no modo Python por um momento. Espero que esteja tudo bem.
David Jones

Vou conectar a nova fórmula no meu código no andar de cima e ver o que acontece com ela! (agradecimento ^ _ ^)
Tom J Nowell

1
Tenho certeza de que a resposta de PeterB também está correta. E maldosamente hackish. :)
David Jones

4
Mas este não contém funções trigonométricas :)
nornagon

Qual é a fórmula equivalente para java? se os ângulos estão em grau.
Soley



2

Um código eficiente em C ++ que funciona para qualquer ângulo e em ambos: radianos e graus é:

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

Não há necessidade de calcular funções trigonométricas. O código simples na linguagem C é:

#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);
}

seja dif = a - b, em radianos

dif = difangrad(a,b);

Seja dif = a - b, em graus

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

Sem pecado, sem cos, sem bronzeado, .... apenas geometria !!!!


7
Erro! Como você define # PIV2 como "M_PI + M_PI", não "(M_PI + M_PI)", a linha se arg = arg - PIV2;expande para arg = arg - M_PI + M_PIe assim nada faz.
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.