Promedios de ángulos

15

Historia, o por qué estamos haciendo esto.

Ninguna. Este ejercicio no tiene sentido ... a menos que seas Stephen Hawking .

El reto

Dada una lista de ángulos, encuentre el promedio de esos ángulos. Por ejemplo, el promedio de 91 grados y -91 grados es 180 grados. Puede usar un programa o función para hacer esto.

Entrada

Una lista de valores de grados que representan medidas de ángulos. Puede suponer que serán enteros. Se pueden ingresar en cualquier formato conveniente o proporcionarse como argumentos de función.

Salida

El promedio de los valores ingresados. Si se encuentra más de un valor para el promedio, solo se debe generar uno. El promedio se define como el valor para el cual

ingrese la descripción de la imagen aquí

se minimiza La salida debe estar dentro del rango de (-180, 180] y tener una precisión de al menos dos lugares detrás del punto decimal.

Ejemplos:

> 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

Como es habitual con , el envío con la menor cantidad de bytes.

Tabla de clasificación

Aquí hay un fragmento de pila para generar una tabla de clasificación regular y una descripción general de los ganadores por idioma.

Para asegurarse de que su respuesta se muestre, comience con un título, usando la siguiente plantilla de Markdown:

## Language Name, N bytes

¿Dónde Nestá el tamaño de su envío? Si mejora su puntaje, puede mantener los puntajes antiguos en el título, tachándolos. Por ejemplo:

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

Si desea incluir varios números en su encabezado (por ejemplo, porque su puntaje es la suma de dos archivos o desea enumerar las penalizaciones de la bandera del intérprete por separado), asegúrese de que el puntaje real sea el último número en el encabezado:

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

También puede hacer que el nombre del idioma sea un enlace que luego aparecerá en el fragmento de la tabla de clasificación:

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

Aquí hay una sala de chat para cualquier pregunta sobre el problema: http://chat.stackexchange.com/rooms/30175/room-for-average-of-angles

El numero uno
fuente
¿No debería 90, -90 dar 180 si 91, -91 da 180?
Azul
2
Intuitivamente, el promedio de -91 y 91 es 0, no 180. Usando su definición tenemos: (180-91) ^ 2 + (180- -91) ^ 2 => 81362, mientras que (0-91) ^ 2 + ( 0- -91) ^ 2 => 16562. Entonces 180 seguramente no puede ser el promedio. ¿Que me estoy perdiendo aqui?
edc65
91% 360 = 91; -91% 360 = 269; (269 + 91) / 2 = 180. No importa, mal leído. ¿Tal vez? No estoy seguro ahora.
Azul
OK gracias. Todavía no
tengo
3
Hasta el momento, ninguno de sus casos de prueba rompe el algoritmo incorrecto de simplemente tomar todos los ángulos mod 360 °, tomar su promedio y luego restar 360 ° si el resultado es mayor que 180 °. Debe agregar un caso como [89 °, −89 °], que debería devolver 0 °.
Anders Kaseorg

Respuestas:

7

Python 3, 129 bytes

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]

Este problema parece haber generado bastante confusión. Intuitivamente, la idea es cortar el círculo de ángulos en algún punto, desenvolver el círculo en una línea, calcular la media aritmética en esa línea y luego envolver el resultado nuevamente en el círculo. Pero hay muchos puntos diferentes donde puedes elegir cortar el círculo. No es suficiente elegir arbitrariamente uno, como 0 ° o 180 °. Debe probarlos todos y ver cuál resulta en la suma más pequeña de distancias al cuadrado. Si su solución es significativamente menos complicada que esto, probablemente sea incorrecta.

Anders Kaseorg
fuente
1
@AndreasKaseorg Creo que puede guardar un byte cambiando s**2as*s
Ioannes
Vea mi comentario sobre la pregunta.
msh210
@ msh210 No estoy seguro de por qué me estás dirigiendo este comentario específicamente. Mi solución ya funciona de esa manera.
Anders Kaseorg
Fue parcialmente en respuesta a la última oración de esta publicación de respuesta.
msh210
4

Python 3, 85 bytes

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

Aprovecha la respuesta que solo necesita ser precisa con dos decimales al intentar todos los ángulos posibles con incrementos 1/200de un grado. Esto toma menos de un segundo en mi máquina.

Debido a que Python no nos permite enumerar convenientemente progresiones aritméticas de flotadores, representamos los posibles ángulos como un número entero [0,72000), que se convierten en un ángulo (-180,180]como x -> 180 - x/200. Encontramos uno de estos que da la suma mínima de las diferencias angulares al cuadrado.

Para dos ángulos con un desplazamiento angular de d, la distancia angular al cuadrado se encuentra mediante la transformación a un ángulo equivalente en(-180,180] as 180-(d+180)%360, luego cuadrando. Convenientemente, el ángulo dado por x/200ya está compensado por 180grados.

xnor
fuente
Usar incrementos de 1/200es realmente problemático. Para el caso de prueba [1, 3, 3], esta solución regresa 2.335y se redondea a 2.34la respuesta correcta 2.33.
Joel
@ Joo No estoy seguro de dónde obtienes el redondeo, parece que los dígitos decimales 2.33son correctos en ese ejemplo. En cualquier caso, ¿cambiar el 200to to 400to 2000(y en 72000consecuencia) lo haría funcionar a pesar del redondeo? Además, al mirar este viejo problema nuevamente, creo que podría ver una mejor manera.
xnor
0,01metro=unrsolmetroyonorteXF(X)[s,s+0,01]F(s)<F(s+0,01)El |metro-sEl |<El |metro-s+0,01El |rotunortere(metro)=sFF(s)>F(s+0,01)F(s)=F(s+0,01)rotunortere(metro)=s+0,01F
Aquí hay un enlace TIO para que lo pruebe.
Joel
Oh, me acabo de dar cuenta de que tienes razón. Si la respuesta correcta es 2.333...y su programa regresa 2.335, es correcto hasta dos decimales sin redondear. Lo siento por eso.
Joel
3

Octava, 97 95 bytes

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

Esto produce una función anónima que solo busca el mínimo de la función dada en una cuadrícula que es lo suficientemente buena. Como entrada, la función acepta vectores de columna, por ejemplo [180; 60; -60]. Para realizar la prueba, debe asignar un nombre a la función. Entonces, por ejemplo, puede ejecutar el código anterior y luego usarlo ans([180, 60; -60]).

falla
fuente
Sí, devuelve 180.
flawr
2

Javascript ES6, 87 bytes

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

Ejecuciones de ejemplo (probado en Firefox):

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

Trabajo en progreso

Esta versión adopta un enfoque ligeramente diferente al promedio-todo-entonces-hacer-matemática modular. Por el contrario, los ángulos se convierten en vectores, los vectores se agregan y luego se calcula el ángulo del vector resultante. Desafortunadamente, esta versión es muy inestable con el trigonometría y estaré trabajando en una versión matemática modular.

Dendrobium
fuente
1
f(-91,91)debería devolver 180.
TheNumberOne
1
Incluso si se implementara correctamente, un enfoque de suma de vectores no puede calcular el resultado especificado. La suma vectorial maximiza la suma de cosenos de diferencias angulares, en lugar de minimizar la suma de cuadrados de diferencias angulares.
Anders Kaseorg
2

CJam,  44  40 bytes

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

Pruébelo en línea en el intérprete de CJam .

Casos de prueba

$ 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

Calculamos la desviación para todos los promedios potenciales de -179.99 a 180.00 con pasos de tamaño 0.01 , y seleccionamos el que tiene la desviación más baja.

Para este propósito, no importa si tomamos las distancias angulares grados o radianes. En lugar de mapear las diferencias δ de ángulos de los promedios de entrada y potencial en [0,360 °) y restar condicionalmente el resultado de 180 ° , simplemente podemos calcular arccos (cos (πδ ÷ 180 °)) , ya que cos es tanto periódico como par, y arccos siempre produce un valor en [0, π) .

Código

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.
Dennis
fuente
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, hasta que pueda entender cuál es la metodología, esto es lo que se me ocurrió. Es un truco, pero como dice la pregunta que la respuesta debe ser correcta a 2.dp, debería funcionar.

Básicamente verifico cada ángulo entre 0 y 360 (en incrementos de 0.01) y luego resuelvo la fórmula en la pregunta para cada uno de esos ángulos. Luego, el ángulo con la suma más pequeña se elige y se convierte en un rango de -180 a 180.


El código debería con Octave . Puedes probarlo con el intérprete en línea.

Tom Carpenter
fuente
1 °, 183 ° debería dar como resultado −88 °, no 92 °.
Anders Kaseorg
@AndersKaseorg intente nuevamente ahora.
Tom Carpenter
No, no importa. Volver a la mesa de dibujo de nuevo ...
Tom Carpenter,
1

JavaScript (ES6) 138

Al no tener la menor idea de un algoritmo, intenta todos los valores posibles con una precisión de 2 dígitos (-179,99 a 180,00). Muy rápido con los casos de prueba de todos modos.

Pruebe a ejecutar el fragmento a continuación en un navegador compatible con EcmaScript 6 (implementando funciones de flecha y parámetros predeterminados - 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>

edc65
fuente