Calcule el número de bobinado

15

El número de devanado es el número entero de revoluciones netas en sentido antihorario que un observador debe haber hecho para seguir un camino cerrado dado. Tenga en cuenta que cualquier revolución en el sentido de las agujas del reloj cuenta negativamente hacia el número de devanado. Se permite que el camino se cruce.

A continuación se dan algunos ejemplos (descaradamente tomados de Wikipedia):

ingrese la descripción de la imagen aquí

Su objetivo es calcular el número de bobinado para una ruta determinada.

Entrada

Se supone que el observador está en el origen (0,0).

La entrada es una secuencia finita de puntos (pares de números enteros) de cualquier fuente de entrada deseada que describe la ruta lineal por partes. Puede aplanar esto en una secuencia 1D de números enteros si lo desea, y también puede mezclar la entrada para tomar todas las coordenadas x antes de todas las coordenadas y / viceversa. También puede tomar la entrada como un número complejo a+b i. La ruta puede auto intersectarse y puede contener segmentos de longitud cero. El primer punto es el inicio de la ruta y se supone que se encuentra en algún lugar del eje x positivo.

Ninguna parte del camino se cruzará con el origen. La ruta siempre estará cerrada (es decir, el primer punto y el punto perdido son los mismos). Su código puede implicar el último punto o requerir que se incluya.

Por ejemplo, dependiendo de su preferencia, ambas entradas especifican el mismo cuadrado:

punto final implícito

1,0
1,1
-1,1
-1,-1
1,-1

punto final explícito

1,0
1,1
-1,1
-1,-1
1,-1
1,0

Salida

La salida es un número entero único para el número de devanado. Esto puede ser a cualquier fuente (valor de retorno, stdout, archivo, etc.).

Ejemplos

Todos los ejemplos tienen el punto final explícitamente definido y se dan como pares x, y. Por cierto, también debería poder alimentar directamente estos ejemplos en cualquier código suponiendo puntos finales definidos implícitamente y las salidas deberían ser las mismas.

1. Prueba básica

1,0
1,1
-1,1
-1,-1
1,-1
1,0

Salida

1

2. Prueba repetida de puntos

1,0
1,0
1,1
1,1
-1,1
-1,1
-1,-1
-1,-1
1,-1
1,-1
1,0

Salida

1

3. Prueba en sentido horario

1,0
1,-1
-1,-1
-1,1
1,1
1,0

Salida

-1

4. Prueba exterior

1,0
1,1
2,1
1,0

Salida

0

5. Bobinado mixto

1,0
1,1
-1,1
-1,-1
1,-1
1,0
1,-1
-1,-1
-1,1
1,1
1,0
1,1
-1,1
-1,-1
1,-1
1,0
1,1
-1,1
-1,-1
1,-1
1,0

Salida

2

Puntuación

Este es el código de golf; el código más corto gana. Se aplican lagunas estándar. Puede usar cualquier función incorporada siempre que no estén específicamente diseñadas para calcular el número de devanado.

helloworld922
fuente
2
¿Se puede tomar la entrada como números complejos (o una representación de cadena de ellos, como "1-i"o "1-1i"?)
Level River St
Sí, se permite cualquier tipo de par.
helloworld922

Respuestas:

10

ES6, 83 bytes

a=>a.map(([x,y])=>r+=Math.atan2(y*b-x*c,y*c+x*b,b=x,c=y),b=c=r=0)&&r/Math.PI/2

Toma como entrada una matriz de pares de puntos que se interpretan como números complejos. En lugar de convertir cada punto en un ángulo, los puntos se dividen por el punto anterior, que Math.atan2 luego convierte en un ángulo entre -π y π, lo que determina automáticamente en qué dirección se está enrollando el camino. La suma de los ángulos es entonces 2π veces el número de devanado.

Como Math.atan2 no se preocupa por la escala de sus argumentos, en realidad no realizo la división completa, z / w = (z * w*) / (w * w*)sino que simplemente multiplico cada punto por el complejo conjugado del punto anterior.

Editar: Guardado 4 bytes gracias a @ edc65.

Neil
fuente
Agradable y rápido Y no entiendo tus matemáticas. Pero reducecasi siempre es una mala elección.
edc65
a=>a.map(([x,y])=>r+=Math.atan2(y*b-x*c,y*c+x*b,b=x,c=y),b=c=r=0)&&r/Math.PI/2usando el mapa en su lugar o reducir. Tienes mi voto de todos modos
edc65
@ edc65 Gracias; Lo usé reduceporque no me di cuenta de que Math.atan2 (0,0) es 0. (Bueno, depende de si uno de tus 0 es realmente -0.) Las matemáticas se basan en una división compleja, que normalmente se calcula como z / w = z * w* / |w|², pero no me importa la magnitud, así que es solo la multiplicación por el conjugado complejo. También un poco confusamente Math.atan2 acepta argumentos (y, x).
Neil
Admito que no entiendo el código, pero si su descripción es precisa, entonces creo que su respuesta es incorrecta. De hecho, si ingresó puntos desde este camino (estoy dando una imagen para mayor claridad), entonces el número de devanado es 1, mientras que su problema generaría 2.
Wojowu
@Wojowu Lo siento, quise decir el ángulo entre los puntos medidos desde el origen, en lugar de los ángulos externos del polígono, por lo que para su imagen, mi código debería calcular la respuesta como 1.
Neil
3

MATL , 11 bytes

X/Z/0)2/YP/

La entrada es una secuencia de números complejos que incluye el punto final.

Pruébalo en línea!

Explicación

La mayor parte del trabajo se realiza mediante la Z/función ( unwrap), que desenvuelve los ángulos en radianes cambiando los saltos absolutos mayores o iguales que pi a su complemento 2 * pi.

X/       % compute angle of each complex number
Z/       % unwrap angles
0)       % pick last value. Total change of angle will be a multiple of 2*pi because 
         % the path is closed. Total change of angle coincides with last unwrapped
         % angle because the first angle is always 0
2/       % divide by 2
YP/      % divide by pi
Luis Mendo
fuente
1
MATL y Jelly han empatado la mayoría de los desafíos matemáticos últimamente. Estoy impresionado, casi has superado el lenguaje de Dennis ...
ETHproductions
@ETHproductions ¡Gracias por tus bonitas palabras! Sí, han estado atados en algunos desafíos recientes. Por otro lado, he visto bastantes problemas en los que el conteo de bytes de Jelly es aproximadamente la mitad que MATL :-D
Luis Mendo
2

Jalea, 11 bytes

æAI÷ØPæ%1SH

Esto toma la entrada como una lista de coordenadas y y una lista de coordenadas x.

Pruébalo aquí .

lirtosiast
fuente
1

Python, 111

La respuesta más larga hasta ahora. Mis motivaciones son 1) aprender python y 2) posiblemente portar esto a pyth.

from cmath import *
q=input()
print reduce(lambda x,y:x+y,map(lambda (x,y):phase(x/y)/pi/2,zip(q[1:]+q[:1],q)))

La entrada se da como una lista de números complejos.

Ideona

Creo que el enfoque es similar a la respuesta ES6.

Cuando se multiplican 2 números complejos, el argumento o la fase del producto es la suma del argumento o la fase de los dos números. Así, cuando un número complejo se divide por otro, la fase del cociente es la diferencia entre las fases del numerador y el denominador. Por lo tanto, podemos calcular el ángulo atravesado para cada punto y el siguiente punto. Sumar estos ángulos y dividir por 2π da el número de devanado requerido.

Trauma digital
fuente