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.