Medie degli angoli


15

Storia o perché lo stiamo facendo.

Nessuna. Questo esercizio è completamente inutile ... a meno che tu non sia Stephen Hawking .

La sfida

Dato un elenco di angoli, trova la media di quegli angoli. Ad esempio, la media di 91 gradi e -91 gradi è 180 gradi. È possibile utilizzare un programma o una funzione per fare ciò.

Ingresso

Un elenco di valori in gradi che rappresentano le misure angolari. Puoi presumere che saranno numeri interi. Possono essere inseriti in qualsiasi formato conveniente o forniti come argomenti di funzione.

Produzione

La media dei valori immessi. Se viene trovato più di un valore per la media, ne deve essere emesso solo uno. La media è definita come il valore per il quale

inserisci qui la descrizione dell'immagine

è ridotto al minimo. L'output deve essere compreso nell'intervallo di (-180, 180] e deve essere accurato di almeno due posizioni dietro il punto decimale.

Esempi:

> 1 3
2
> 90 -90
0 or 180
> 0 -120 120
0 or -120 or 120
> 0 810
45
> 1 3 3
2.33
> 180 60 -60
180 or 60 or -60
> 0 15 45 460
40
> 91 -91
180
> -89 89
0

Come al solito con , l'invio con il minor numero di byte.

Classifica

Ecco uno snippet di stack per generare sia una classifica regolare che una panoramica dei vincitori per lingua.

Per assicurarti che la tua risposta venga visualizzata, ti preghiamo di iniziare la risposta con un titolo, usando il seguente modello Markdown:

## Language Name, N bytes

dov'è Nla dimensione del tuo invio. Se si migliora il punteggio, è possibile mantenere i vecchi punteggi nel titolo, colpendoli. Per esempio:

## Ruby, <s>104</s> <s>101</s> 96 bytes

Se si desidera includere più numeri nell'intestazione (ad es. Perché il punteggio è la somma di due file o si desidera elencare separatamente le penalità del flag dell'interprete), assicurarsi che il punteggio effettivo sia l' ultimo numero nell'intestazione:

## Perl, 43 + 2 (-p flag) = 45 bytes

Puoi anche rendere il nome della lingua un collegamento che verrà quindi visualizzato nello snippet della classifica:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes

Ecco una chat room per qualsiasi domanda sul problema: http://chat.stackexchange.com/rooms/30175/room-for-average-of-angles


90, -90 non dovrebbe dare 180 se 91, -91 dovrebbe dare 180?
Blu,

2
Intuitivamente la media di -91 e 91 è 0, non 180. Usando la tua definizione abbiamo: (180-91) ^ 2 + (180- -91) ^ 2 => 81362, mentre (0-91) ^ 2 + ( 0- -91) ^ 2 => 16562. Quindi 180 sicuramente non può essere la media. Cosa mi sto perdendo qui?
edc65,

91% 360 = 91; -91% 360 = 269; (269 + 91) / 2 = 180. Non importa, frainteso. Può essere? Non sono sicuro ora.
Blu,

Ok grazie. Ancora non ho idea di come trovarlo
edc65,

3
Nessuno dei tuoi casi di test ha finora rotto l'algoritmo errato di prendere semplicemente tutti gli angoli mod 360 °, prendere la loro media e quindi sottrarre 360 ​​° se il risultato è maggiore di 180 °. Dovresti aggiungere un caso come [89 °, −89 °], che dovrebbe restituire 0 °.
Anders Kaseorg,

Risposte:


7

Python 3, 129 byte

lambda l:min([sum(((b-a)%360)**2for b in l)*len(l)-s*s,180-(180-a-s/len(l))%360]for a in l for s in[sum((b-a)%360for b in l)])[1]

Questo problema sembra aver generato molta confusione. Intuitivamente, l'idea è di tagliare il cerchio degli angoli ad un certo punto, scartare il cerchio su una linea, calcolare la media aritmetica su quella linea e quindi avvolgere il risultato sul cerchio. Ma ci sono molti punti diversi in cui puoi scegliere di tagliare il cerchio. Non è sufficiente sceglierne uno arbitrariamente, come 0 ° o 180 °. Devi provarli tutti e vedere quale si traduce nella somma più piccola di distanze al quadrato. Se la tua soluzione è significativamente meno complicata di così, probabilmente è sbagliata.


1
@AndreasKaseorg Penso che puoi salvare un byte cambiando s**2ins*s
Ioannes

Vedi il mio commento sulla domanda.
msh210,

@ msh210 Non sono sicuro del motivo per cui mi stai indirizzando questo commento in particolare. La mia soluzione funziona già in questo modo.
Anders Kaseorg,

Era parzialmente in risposta all'ultima frase di questo post di risposta.
msh210,

4

Python 3, 85 byte

lambda l:180-min(range(72000),key=lambda x:sum((180-(x/200+i)%360)**2for i in l))/200

Sfrutta la risposta solo per essere precisi con due decimali provando tutti gli angoli possibili con incrementi di 1/200grado. Questo richiede meno di un secondo sulla mia macchina.

Poiché Python non ci consente di elencare convenientemente le progressioni aritmetiche dei float, rappresentiamo i possibili angoli come numero intero [0,72000) , che si converte in un angolo (-180,180]come x -> 180 - x/200. Troviamo quello che fornisce la somma minima delle differenze angolari quadrate.

Per due angoli con uno spostamento angolare di d, la distanza angolare quadrata viene rilevata trasformandosi in un angolo equivalente in (-180,180]as 180-(d+180)%360, quindi squadrando. Convenientemente, l'angolo dato da x/200è già sfalsato di 180gradi.


L'uso di incrementi di 1/200è effettivamente problematico. Per il test case [1, 3, 3], questa soluzione ritorna 2.335ed è arrotondata a 2.34mentre dovrebbe essere la risposta corretta 2.33.
Gioele,

@Joel Non sono sicuro da dove stai ottenendo l'arrotondamento, sembra che le cifre decimali 2.33siano proprio in questo esempio. In ogni caso, la modifica di 200to 400o to 2000(e 72000corrispondentemente) lo farà funzionare nonostante l'arrotondamento? Inoltre, guardando di nuovo questo vecchio problema, penso che potrei vedere un modo migliore.
xnor

0.01m=un'rgmionXf(X)[S,S+0.01]f(S)<f(S+0.01)|m-S|<|m-S+0.01|round(m)=Sff(S)>f(S+0.01)f(S)=f(S+0.01)round(m)=S+0.01f

Ecco un link TIO che puoi testare.
Gioele,

Oh, ho appena capito che hai ragione. Se la risposta è corretta 2.333...e il programma ritorna 2.335, è corretto fino a due cifre decimali senza arrotondamento. Scusa per quello.
Gioele,

3

Ottava, 97 95 byte

p=pi;x=p:-1e-5:-p;z=@(L)sum((p-abs(abs(x-mod(L,360)*p/180)-p)).^2);@(L)x(z(L)==min(z(L)))*180/p

Questo produce una funzione anonima che cerca solo il minimo della funzione data su una griglia che è abbastanza bene. Come input la funzione accetta vettori di colonna, ad es [180; 60; -60]. Per i test è necessario assegnare un nome alla funzione. Quindi potresti ad esempio eseguire il codice sopra e quindi utilizzare ans([180, 60; -60]).


Sì, restituisce 180.
flawr

2

Javascript ES6, 87 byte

with(Math)f=(...n)=>(t=>180/PI*atan(t(sin)/t(cos)))(f=>n.reduce((p,c)=>p+=f(c*PI/180)))

Esecuzioni di esempio (testate in Firefox):

f(-91,91)     // -0
f(-90,90)     // 0
f(0,-120,120) // 0
f(0,810)      // 44.999999999999936

Lavori in corso

Questa versione ha un approccio leggermente diverso rispetto alla matematica media-tutto-quindi-fare-modulare. Piuttosto, gli angoli vengono convertiti in vettori, i vettori vengono aggiunti e viene quindi calcolato l'angolo del vettore risultante. Sfortunatamente, questa versione è molto instabile con il trig e lavorerò su una versione matematica modulare.


1
f(-91,91)dovrebbe restituire 180.
TheNumberOne il

1
Anche se è stato implementato correttamente, un approccio di addizione vettoriale non può calcolare il risultato specificato. L'aggiunta vettoriale massimizza la somma dei coseni delle differenze angolari, piuttosto che minimizzare la somma dei quadrati delle differenze angolari.
Anders Kaseorg,

2

CJam,  44  40 byte

Ie3_2*,f-:e-2{ea:~f{-P*180/mcmC2#}:+}$0=

Provalo online nell'interprete CJam .

Casi test

$ for i in 1\ 3 90\ -90 0\ -120\ 120 0\ 810 1\ 3\ 3 180\ 60\ -60 0\ 15\ 45\ 460 91\ -91 -89\ 89
> do cjam <(echo 'Ie3_2*,f-:e-2{ea:~f{-P*180/mcmC2#}:+}$0=') $i
> echo
> done
2.0
180.0
0.0
45.0
2.33
60.0
40.0
180.0
0.0

Idea

Calcoliamo la deviazione per tutte le medie potenziali da -179,99 a 180,00 con passi di dimensione 0,01 e selezioniamo quella con la deviazione più bassa.

A questo scopo, non importa se prendiamo le distanze angolari gradi o radianti. Anziché mappare le differenze δ degli angoli dall'input e le medie potenziali in [0,360 °) e sottrarre condizionalmente il risultato da 180 ° , possiamo semplicemente calcolare archi (cos (πδ ÷ 180 °)) , poiché cos è sia periodico che uniforme, e arccos restituisce sempre un valore in [0, π) .

Codice

Ie3        e# Push 18e3 = 18,000.
_2*        e# Copy and multiply by 2. Pushes 36,000.
,          e# Push the range [0 ... 35,999].
f-         e# Subtract each element from 18,000. Pushes [18,000 ... -17,999].
:e-2       e# Divide each element by 100. Pushes [180.00 ... -179.99].
{          e# Sort; for each element A of that array:
  ea:~     e#   Push and evaluate the array of command-line arguments.
  f{       e#   For each input angle, push A and the angle; then:
    -      e#     Subtract the angle from A.
    P*180/ e#     Convert from degrees to radians.
    mcmC   e#     Apply cos, then arccos to the result.
    2#     e#     Square.
  }        e#
  :+       e#   Add the squares. This calculates the deviation.
}$         e# A's with lower deviations come first.
0=         e# Select the first element of the sorted array.

1

MATLAB, 151

p=360;n=mod(input(''),p);a=0:0.01:p;m=[];for b=a e=b-n;f=mod([e;-e],p);c=min(f);d=c.^2;m=[m sum(d)];end;[~,i]=min(m);a=a(i);if a>180 a=a-p;end;disp(a);

Ok, quindi fino a quando non riesco davvero a capire qual è la metodologia, questo è quello che ho escogitato. È un po 'un trucco, ma poiché la domanda afferma che la risposta deve essere corretta a 2.dp, dovrebbe funzionare.

Fondamentalmente controllo ogni angolo tra 0 e 360 ​​(con incrementi di 0,01) e quindi risolvo la formula nella domanda per ciascuno di quegli angoli. Quindi l'angolo con la somma più piccola viene selezionato e convertito in un intervallo compreso tra -180 e 180.


Il codice dovrebbe con Octave . Puoi provarlo con l' interprete online


1 °, 183 ° dovrebbe comportare −88 °, non 92 °.
Anders Kaseorg,

@AndersKaseorg riprova adesso.
Tom Carpenter,

No, non importa. Di nuovo al tavolo da disegno ...
Tom Carpenter,

1

JavaScript (ES6) 138

Non avendo la più pallida idea di un algoritmo, questo prova tutti i possibili valori con precisione a 2 cifre (da -179,99 a 180,00). Abbastanza veloce con i casi di test comunque.

Prova a eseguire lo snippet di seguito in un browser conforme a EcmaScript 6 (implementando le funzioni freccia e i parametri predefiniti - AFAIK Firefox)

A=l=>(D=(a,b,z=a>b?a-b:b-a)=>z>180?360-z:z,m=>{for(i=-18000;i++<18000;)l.some(v=>(t+=(d=D(v%360,i/100))*d)>m,t=0)||(m=t,r=i)})(1/0)||r/100

// Test
console.log=x=>O.innerHTML+=x+'\n'

;[[1,3],[89,-89],[90,-90],[91,-91],[0,120,-120],[0,810],[1,3,3],[180,60,-60],[0,15,45,460],[1,183]]
.forEach(t=>console.log(t+' -> '+A(t)))

// Less golfed

A=l=>{
  D=(a,b,z=a>b?a-b:b-a) => z>180?360-z:z; // angular distance
  m=1/0;
  for(i=-18000;i++<18000;) // try all from -179.99 to 180
  {
    t = 0;
    if (!l.some(v => (t+=(d=D(v%360,i/100))*d) > m))
    {
      m = t;
      r = i;
    }  
  }  
  return r/100;
}
<pre id=O></pre>

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.