Moyennes des angles


15

Histoire, ou pourquoi nous faisons cela.

Aucun. Cet exercice est complètement inutile ... sauf si vous êtes Stephen Hawking .

Le défi

À partir d'une liste d'angles, trouvez la moyenne de ces angles. Par exemple, la moyenne de 91 degrés et -91 degrés est de 180 degrés. Vous pouvez utiliser un programme ou une fonction pour ce faire.

Contribution

Une liste de valeurs de degré représentant des mesures d'angle. Vous pouvez supposer qu'il s'agira d'entiers. Ils peuvent être saisis dans n'importe quel format pratique ou fournis comme arguments de fonction.

Production

La moyenne des valeurs entrées. Si plusieurs valeurs sont trouvées pour la moyenne, une seule doit être sortie. La moyenne est définie comme la valeur pour laquelle

entrez la description de l'image ici

est minimisé. La sortie doit se situer dans la plage (-180, 180] et être précise à au moins deux endroits derrière la virgule décimale.

Exemples:

> 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

Comme d'habitude avec , la soumission avec le moins d'octets gagne.

Classement

Voici un extrait de pile pour générer à la fois un classement régulier et un aperçu des gagnants par langue.

Pour vous assurer que votre réponse apparaît, veuillez commencer votre réponse avec un titre, en utilisant le modèle de démarque suivant:

## Language Name, N bytes

Nest la taille de votre soumission. Si vous améliorez votre score, vous pouvez conserver les anciens scores dans le titre, en les barrant. Par exemple:

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

Si vous souhaitez inclure plusieurs nombres dans votre en-tête (par exemple, parce que votre score est la somme de deux fichiers ou que vous souhaitez répertorier les pénalités de drapeau d'interprète séparément), assurez-vous que le score réel est le dernier numéro de l'en-tête:

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

Vous pouvez également faire du nom de la langue un lien qui apparaîtra ensuite dans l'extrait de classement:

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

Voici un salon de discussion pour toute question sur le problème: http://chat.stackexchange.com/rooms/30175/room-for-average-of-angles


90, -90 ne devrait-il pas donner 180 si 91, -91 donne 180?
Blue

2
Intuitivement, la moyenne de -91 et 91 est 0, pas 180. En utilisant votre définition, nous avons: (180-91) ^ 2 + (180- -91) ^ 2 => 81362, tandis que (0-91) ^ 2 + ( 0- -91) ^ 2 => 16562. Donc 180 ne peut sûrement pas être la moyenne. Qu'est-ce que j'oublie ici?
edc65

91% 360 = 91; -91% 360 = 269; (269 + 91) / 2 = 180. Tant pis, mal lu. Peut être? Je ne suis pas sûr maintenant.
Blue

OK merci. Toujours aucune idée de comment le trouver
edc65

3
Jusqu'à présent, aucun de vos cas de test ne rompt l'algorithme incorrect de simplement prendre tous les angles mod 360 °, en prenant leur moyenne, puis en soustrayant 360 ° si le résultat est supérieur à 180 °. Vous devez ajouter un cas comme [89 °, −89 °], qui devrait retourner 0 °.
Anders Kaseorg

Réponses:


7

Python 3, 129 octets

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]

Ce problème semble avoir généré pas mal de confusion. Intuitivement, l'idée est de couper le cercle d'angles à un moment donné, de dérouler le cercle en une ligne, de calculer la moyenne arithmétique sur cette ligne, puis de replacer le résultat dans le cercle. Mais il existe de nombreux points différents où vous pouvez choisir de couper le cercle. Il ne suffit pas d'en choisir arbitrairement, par exemple 0 ° ou 180 °. Vous devez tous les essayer et voir lequel donne la plus petite somme des distances au carré. Si votre solution est beaucoup moins compliquée que cela, c'est probablement faux.


1
@AndreasKaseorg Je pense que vous pouvez économiser un octet en changeant s**2pours*s
Ioannes

Voir mon commentaire sur la question.
msh210

@ msh210 Je ne sais pas pourquoi vous me dirigez ce commentaire spécifiquement. Ma solution fonctionne déjà de cette façon.
Anders Kaseorg

C'était en partie en réponse à la dernière phrase de ce message de réponse.
msh210

4

Python 3, 85 octets

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

Profite de la réponse qui n'a besoin d'être précise qu'à deux décimales près en essayant tous les angles possibles avec des incréments 1/200d'un degré. Cela prend moins d'une seconde sur ma machine.

Parce que Python ne nous permet pas de lister commodément les progressions arithmétiques des flottants, nous représentons les angles possibles en nombre entier [0,72000), qui se convertissent en un angle en tant (-180,180]que x -> 180 - x/200. Nous trouvons celui qui donne la somme minimale des différences angulaires au carré.

Pour deux angles avec un déplacement angulaire de d, la distance angulaire au carré est trouvée en se transformant en un angle équivalent en (-180,180]as 180-(d+180)%360, puis au carré. De manière pratique, l'angle donné par x/200est déjà décalé de 180degrés.


L'utilisation d'incréments de 1/200est en fait problématique. Pour le cas de test [1, 3, 3], cette solution retourne 2.335et est arrondie à 2.34alors que la bonne réponse devrait être 2.33.
Joel

@Joel Je ne sais pas d'où vous obtenez l'arrondi, il semble que les chiffres décimaux 2.33soient exacts dans cet exemple. Dans tous les cas, est-ce que changer le 200to 400ou le to 2000(et en 72000conséquence) le ferait fonctionner malgré l'arrondissement? Aussi, en regardant à nouveau ce vieux problème, je pense que je pourrais voir une meilleure façon.
xnor

0,01m=unergmjenXF(X)[s,s+0,01]F(s)<F(s+0,01)|m-s|<|m-s+0,01|roun(m)=sFF(s)>F(s+0,01)F(s)=F(s+0,01)roun(m)=s+0,01F

Voici un lien TIO à tester.
Joel

Oh, je viens de réaliser que tu as raison. Si la bonne réponse est 2.333...et que votre programme revient 2.335, elle est correcte jusqu'à deux décimales sans arrondir. Désolé.
Joel

3

Octave, 97 95 octets

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

Cela produit une fonction anonyme qui recherche juste le minimum de la fonction donnée sur une grille qui est juste assez fine. En entrée, la fonction accepte les vecteurs de colonne, par exemple [180; 60; -60]. Pour les tests, vous devez donner un nom à la fonction. Vous pouvez donc par exemple exécuter le code ci-dessus, puis l'utiliser ans([180, 60; -60]).


Oui, il retourne 180.
flawr

2

Javascript ES6, 87 octets

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

Exemple d'exécutions (testé dans Firefox):

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

Travail en cours

Cette version adopte une approche légèrement différente de la moyenne-tout-puis-faire-modulaire-math. Au contraire, les angles sont convertis en vecteurs, les vecteurs sont ajoutés et l'angle du vecteur résultant est ensuite calculé. Malheureusement, cette version est très instable avec le trig et je vais travailler sur une version mathématique modulaire.


1
f(-91,91)devrait retourner 180.
TheNumberOne

1
Même si elle a été implémentée correctement, une approche d'addition vectorielle ne peut pas calculer le résultat spécifié. L'addition vectorielle maximise la somme des cosinus des différences angulaires, plutôt que de minimiser la somme des carrés des différences angulaires.
Anders Kaseorg

2

CJam,  44  40 octets

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

Essayez-le en ligne dans l' interpréteur CJam .

Cas de 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

Idée

Nous calculons l'écart pour toutes les moyennes potentielles de -179,99 à 180,00 avec des pas de taille 0,01 , et sélectionnons celui avec l'écart le plus faible.

À cette fin, peu importe si nous prenons les degrés angulaires ou les radians. Plutôt que de cartographier les différences δ des angles des moyennes d'entrée et de potentiel dans [0,360 °) et de soustraire conditionnellement le résultat de 180 ° , nous pouvons simplement calculer les arccos (cos (πδ ÷ 180 °)) , car cos est à la fois périodique et pair, et arccos donne toujours une valeur dans [0, π) .

Code

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, donc jusqu'à ce que je puisse réellement comprendre la méthodologie, c'est ce que j'ai trouvé. C'est un peu un hack, mais comme la question indique que la réponse doit être correcte à 2.dp, cela devrait fonctionner.

Je vérifie essentiellement tous les angles entre 0 et 360 (par incréments de 0,01), puis je résous la formule de la question pour chacun de ces angles. Ensuite, l'angle avec la plus petite somme est choisi et converti en plage de -180 à 180.


Le code devrait avec Octave . Vous pouvez l'essayer avec l' interprète en ligne


1 °, 183 ° devrait donner −88 °, pas 92 °.
Anders Kaseorg

@AndersKaseorg réessayez maintenant.
Tom Carpenter

Non, tant pis. Retour à la planche à dessin ...
Tom Carpenter

1

JavaScript (ES6) 138

N'ayant pas la moindre idée d'un algorithme, cela essaie toutes les valeurs possibles avec une précision de 2 chiffres (-179,99 à 180,00). De toute façon assez rapide avec les cas de test.

Testez l'exécution de l'extrait ci-dessous dans un navigateur compatible EcmaScript 6 (implémentation des fonctions fléchées et des paramètres par défaut - 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>

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.