La differenza più piccola tra 2 angoli


137

Dati 2 angoli nell'intervallo -PI -> PI attorno a una coordinata, qual è il valore del più piccolo dei 2 angoli tra loro?

Tenendo conto che la differenza tra PI e -PI non è 2 PI ma zero.

Esempio:

Immagina un cerchio, con 2 linee che escono dal centro, ci sono 2 angoli tra quelle linee, l'angolo che formano all'interno, ovvero l'angolo più piccolo , e l'angolo che formano all'esterno, ovvero l'angolo più grande. Entrambi gli angoli quando sommati formano un cerchio completo. Dato che ogni angolo può rientrare in un determinato intervallo, qual è il valore degli angoli più piccoli, tenendo conto del rollover


2
Ho letto 3 volte prima di capire cosa intendevi. Per favore, aggiungi un esempio o spiega meglio ...
Kobi,

Immagina un cerchio, con 2 linee che escono dal centro, ci sono 2 angoli tra quelle linee, l'angolo che formano all'interno, ovvero l'angolo più piccolo, e l'angolo che formano all'esterno, ovvero l'angolo più grande. Entrambi gli angoli quando sommati formano un cerchio completo. Dato che ogni angolo può rientrare in un certo intervallo, qual è il valore degli angoli più piccoli, tenendo conto del rollover
Tom J Nowell,


2
@JimG. questa non è la stessa domanda, in questa domanda l'angolo P1 usato nell'altra domanda sarebbe la risposta errata, sarebbe l'altro angolo più piccolo. Inoltre, non vi è alcuna garanzia che l'angolo sia con l'asse orizzontale
Tom J Nowell,

Risposte:


193

Questo dà un angolo con segno per tutti gli angoli:

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

Attenzione in molte lingue l' modulooperazione restituisce un valore con lo stesso segno del dividendo (come C, C ++, C #, JavaScript, elenco completo qui ). Ciò richiede una modfunzione personalizzata in questo modo:

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

O così:

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

Se gli angoli si trovano entro [-180, 180] funziona anche:

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

In modo più dettagliato:

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

Più semplice e più sensato leggere ad alta voce, sebbene effettivamente la stessa cosa, la prima bti determina l'angolo, la seconda parte si assicura che sia sempre il più piccolo dei 2 angoli possibili
Tom J Nowell,

1
anche se si potrebbe voler fare un% 360, ad esempio se avessi l'angolo 0 e l'angolo target 721, la risposta corretta sarebbe 1, la risposta data da quanto sopra sarebbe 361
Tom J Nowell,

1
Un equivalente più conciso, sebbene potenzialmente più costoso, della seconda affermazione di quest'ultimo approccio è a -= 360*sgn(a)*(abs(a) > 180). (Vieni a pensarci, se hai implementazioni senza rami di sgne abs, allora quella caratteristica potrebbe effettivamente iniziare a compensare la necessità di due moltiplicazioni.)
mmirate

1
L'esempio "Angolo firmato per qualsiasi angolo" sembra funzionare nella maggior parte degli scenari, con un'eccezione. Nello scenario double targetA = 2; double sourceA = 359;'a' sarà uguale a -357,0 invece di 3,0
Stevoisiak,

3
In C ++ puoi usare std :: fmod (a, 360) o fmod (a, 360) per usare il modulo in virgola mobile.
Joeppie,

145

x è l'angolo target. y è la fonte o l'angolo iniziale:

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

Restituisce l'angolo delta firmato. Si noti che, a seconda dell'API, l'ordine dei parametri per la funzione atan2 () potrebbe essere diverso.


13
x-yti dà la differenza di angolo, ma potrebbe essere fuori dai limiti desiderati. Pensa a questo angolo che definisce un punto sul cerchio unitario. Le coordinate di quel punto sono (cos(x-y), sin(x-y)). atan2restituisce l'angolo per quel punto (che è equivalente a x-y) tranne che il suo intervallo è [-PI, PI].
Max


2
una soluzione semplice di una riga e risolta per me (non la risposta selezionata;)). ma l'abbronzatura inversa è un processo costoso.
Mohan Kumar,

2
Per me, la soluzione più elegante. Peccato che potrebbe essere costoso dal punto di vista computazionale.
foc

Anche per me la soluzione più elegante! Risolto perfettamente il mio problema (volevo avere una formula che mi dia l' angolo di virata firmato che è il più piccolo tra le due possibili direzioni / angolazioni di virata).
Jürgen Brauer,

41

Se i tuoi due angoli sono xey, uno degli angoli tra loro è abs (x - y). L'altro angolo è (2 * PI) - abs (x - y). Quindi il valore del più piccolo dei 2 angoli è:

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

Questo ti dà il valore assoluto dell'angolo e presuppone che gli input siano normalizzati (cioè: all'interno dell'intervallo [0, 2π)).

Se si desidera preservare il segno (ovvero: direzione) dell'angolo e accettare anche angoli al di fuori dell'intervallo, [0, 2π)è possibile generalizzare quanto sopra. Ecco il codice Python per la versione generalizzata:

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

Si noti che l' %operatore non si comporta allo stesso modo in tutte le lingue, in particolare quando sono coinvolti valori negativi, quindi se è necessario il porting di alcune regolazioni dei segni.


1
@bradgonesurfing Questo è / era vero, ma per essere onesti i tuoi test hanno verificato cose che non erano state specificate nella domanda originale, in particolare input non normalizzati e conservazione dei segni. La seconda versione nella risposta modificata dovrebbe superare i test.
Laurence Gonsalves,

Anche la seconda versione non funziona per me. Prova 350 e 0 per esempio. Dovrebbe restituire -10 ma restituisce -350
kjyv il

@kjyv Non riesco a riprodurre il comportamento che descrivi. Puoi pubblicare il codice esatto?
Laurence Gonsalves,

Ah, mi dispiace Ho testato di nuovo esattamente la tua versione con rad e gradi in pitone e ha funzionato bene. Quindi deve essere stato un errore nella mia traduzione in C # (non ce l'ho più).
kjyv,

2
Nota che, a partire da Python 3, puoi effettivamente usare tau in modo nativo! Basta scrivere from math import tau.
mhartl

8

Accetto la sfida di fornire la risposta firmata:

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

1
Ah ... la risposta è una funzione Python a proposito. Scusa, ero in modalità Python per un momento. Spero che vada bene.
David Jones,

Inserirò la nuova formula nel mio codice al piano di sopra e vedrò cosa ne sarà! (grazie ^ _ ^)
Tom J Nowell,

1
Sono abbastanza sicuro che anche la risposta di PeterB sia corretta. E maliziosamente hackish. :)
David Jones,

4
Ma questo non contiene funzioni di
trigger

Qual è la formula equivalente per java? se gli angoli sono in gradi.
Soley,



2

Un codice efficiente in C ++ che funziona con qualsiasi angolo e in entrambi: radianti e gradi è:

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

Non è necessario calcolare le funzioni trigonometriche. Il semplice codice in linguaggio 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);
}

sia dif = a - b, in radianti

dif = difangrad(a,b);

sia dif = a - b, in gradi

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

Niente peccato, niente cos, niente abbronzatura, .... solo geometria !!!!


7
Bug! Poiché #define PIV2 come "M_PI + M_PI", non "(M_PI + M_PI)", la linea si arg = arg - PIV2;espande arg = arg - M_PI + M_PIe quindi non fa nulla.
canton7,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.