Mapa del gato de Arnold

21

Reto

Dada una imagen ráster de color * con el mismo ancho y alto, genera la imagen transformada bajo el mapa del gato de Arnold . (* detalles ver abajo)

Definición

Dado el tamaño de la imagen N, suponemos que las coordenadas de un píxel se dan como números entre 0y N-1.

El mapa del gato de Arnold se define de la siguiente manera:

Se [x,y]mueve un píxel en las coordenadas [(2*x + y) mod N, (x + y) mod N].

Esto no es más que una transformación lineal en el toro: la parte amarilla, violeta y verde se mapea de nuevo en el cuadrado inicial debido a mod N.

visualización

Este mapa (llamémoslo f) tiene las siguientes propiedades:

  • Es biyectivo , eso significa reversible: es una transformación lineal con la matriz [[2,1],[1,1]]. Como tiene determinante 1y solo tiene entradas enteras, el inverso también tiene solo entradas enteras y está dado por [[1,-1],[-1,2]], esto significa que también es biyectivo en coordenadas enteras.

  • Es un elemento de torsión del grupo de mapas biyectivos de N x Nimágenes, lo que significa que si lo aplica muchas veces, recuperará la imagen original: f(f(...f(x)...)) = xla cantidad de veces que el mapa aplicado a sí mismo da como resultado la identidad será menor. o igual a 3*N. A continuación, puede ver la imagen de un gato después de un número determinado de aplicaciones iteradas del mapa del gato de Arnold , y una animación de cómo se ve una aplicación repetida:

múltiples aplicaciones repetidas

Detalles

  • Su programa no necesariamente tiene que lidiar con imágenes, pero también son aceptables matrices / matrices 2D, cadenas o estructuras 2D similares.

  • No importa si su (0,0)punto está en la parte inferior izquierda o en la parte superior izquierda. (O en cualquier otro rincón, si esto es más conveniente en su idioma). Especifique qué convención utiliza en su envío.

Casos de prueba

En forma de matriz ( [1,2,3,4]es la fila superior, 1tiene índice (0,0), 2tiene índice (1,0), 5tiene índice (0,1))

 1     2     3     4
 5     6     7     8
 9    10    11    12
13    14    15    16

maps to:

 1    14    11     8
12     5     2    15
 3    16     9     6
10     7     4    13

 --------------------

 1     2     3
 4     5     6
 7     8     9

 map to:

 1     8     6
 9     4     2
 5     3     7

Como imagen (abajo a la izquierda (0,0)):

falla
fuente
1
Pobre Lena. Espero que hayas estado iterando lo suficiente
Luis Mendo
2
¿Podemos tomar el tamaño de la imagen como entrada? ¿Es siempre cuadrado?
xnor
1
Sí, la imagen siempre es cuadrada, y no estoy seguro del tamaño, ¿hay algo en contra de permitir eso?
flawr

Respuestas:

10

Jalea , 9 bytes

Zṙ"JC$µ2¡

Pruébalo en línea! Las coordenadas son como en la respuesta.

Explicación

      µ2¡   Twice:
Z             Transpose, then
 ṙ"           Rotate rows left by
   JC$          0, -1, -2, -3, …, 1-n units.

Esto envuelve la matriz en una dirección, luego en la otra.

Lynn
fuente
Fantástico algoritmo!
Greg Martin el
7

MATL , 23 bytes

tt&n:qt&+&y\tb+&y\b*+Q(

El (0,0)punto está en la parte superior izquierda, como en los ejemplos en el texto del desafío.

Pruébalo en línea!

Explicación

Una matriz en MATL puede indexarse ​​con un solo índice en lugar de dos. Esto se llama indexación lineal y utiliza el orden de columnas principales . Esto se ilustra en la siguiente matriz 4 × 4, en la que el valor en cada entrada coincide con su índice lineal:

1   5   9  13
2   6  10  14
3   7  11  15
4   8  12  16

Hay dos enfoques similares para implementar el mapeo en el desafío:

  1. Cree una matriz de indexación que represente la asignación inversa de Arnold en índices lineales y úsela para seleccionar los valores de la matriz original. Para el caso 4 × 4, la matriz de indexación sería

     1  8 11 14
    15  2  5 12
     9 16  3  6
     7 10 13  4
    

    diciendo que, por ejemplo, el original 5en x = 2, y = 1 va a x = 3, y = 2. Esta operación se denomina indexación de referencia : use la matriz de indexación para indicar qué elemento elegir de la matriz original. Esta es la función ), que toma dos entradas (en su configuración predeterminada).

  2. Cree una matriz de indexación que represente la asignación directa de Arnold en índices lineales y úsela para escribir los valores en la matriz original. Para el caso 4 × 4, la matriz de indexación sería

     1 10  3 12
     6 15  8 13
    11  4  9  2
    16  5 14  7
    

    diciendo que la entrada x = 2, y = 1 de la nueva matriz se sobrescribirá en la entrada con índice lineal 10, es decir, x = 3, y = 2. Esto se llama indexación de asignación : use la matriz de indexación, una matriz de datos y la matriz original, y escriba los datos en la matriz original en los índices especificados. Esta es la función (, que toma tres entradas (en su configuración predeterminada).

El método 1 es más sencillo, pero el método 2 resultó ser más corto.

tt     % Take the input implicitly and push two more copies
&n     % Get its size as two (equal) numbers: N, N
:qt    % Push range [0  1 ... N-1] twice. This represents the original x values
&+     % Matrix of all pairwise additions. This represents x+y
&y     % Push a copy of N onto the top of the stack
\      % Modulo. This is the new y coordinate: y_new
t      % Push another copy
b+     % Bubble up the remaining copy of [0 1 ... N-1] and add. This is 2*x+y
&y     % Push a copy of N onto the top of the stack
\      % Modulo. This is the new x coordinate: x_new
b*+    % Bubble up the remaining copy of N, multiply, add. This computes
       % x_new*N+y_new, which is the linear index for those x_new, y_new 
Q      % Add 1, because MATL uses 1-based indexing
(      % Assigmnent indexing: write the values of the original matrix into
       % (another copy of) the original matrix at the entries given by the
       % indexing matrix. Implicitly display the result
Luis Mendo
fuente
5

Mathematica, 44 bytes

(n=MapIndexed[RotateLeft[#,1-#2]&,#]&)@*n

Un puerto del fantástico algoritmo de Lynn . Hay un carácter invisible de 3 bytes, U + F3C7 en la codificación UTF-8, antes del último ]; Mathematica lo representa como un superíndice Ty toma la transposición de una matriz.

Mathematica, 54 bytes

Table[#2[[Mod[2x-y-1,#]+1,Mod[y-x,#]+1]],{x,#},{y,#}]&

Función sin nombre que toma dos argumentos, un entero positivo #y una matriz 2D #2de dimensiones #x #, y devuelve una matriz 2D de forma similar. Como en el caso de prueba dado, el punto con coordenadas {0,0} está en la esquina superior izquierda y el eje x es horizontal. Implementación directa usando el inverso [[1,-1],[-1,2]]mencionado en la pregunta, con una -1en la primera coordenada para tener en cuenta el hecho de que las matrices están inherentemente indexadas en 1 en Mathematica. Si no se nos permite tomar la dimensión de la matriz como argumento adicional, entonces esta solución se alargará nueve bytes (reemplace el primero #—no el #2—con a=Length@#y todos los #s posteriores con as).

Greg Martin
fuente
Dang,
golpéame
3

Python 2, 89 82 77 73 bytes

def f(a):exec'a=[l[-i:]+l[:-i]for i,l in enumerate(zip(*a))];'*2;return a

La entrada es una lista de listas
La cadena dentro del archivo ejecutivo transpone la lista de listas y rota cíclicamente cada lista por el índice de línea (basado en 0 - la tercera línea se gira 2 veces a la derecha).
Este proceso se realiza 2 veces a la entrada.

+4 bytes que harán la transformación N veces

def f(a,n):exec'a=[l[-i:]+l[:-i]for i,l in enumerate(zip(*a))];'*2*n;return a
Barra
fuente
2

Haskell, 55 bytes

m#n|r<-[0..n-1]=[[m!!mod(2*y-x)n!!mod(x-y)n|x<-r]|y<-r]

Ejemplo de uso: [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] # 4-> [[1,14,11,8],[12,5,2,15],[3,16,9,6],[10,7,4,13]].

0,0es la esquina superior izquierda Esto usa la transformación inversa.

nimi
fuente
1

Python, 69 bytes

lambda M:eval("[r[-i:]+r[:-i]for i,r in enumerate(zip(*"*2+"M))]))]")

Una mejora en el método de transposición y desplazamiento de Rod dos veces . Aplica la operación M -> [r[-i:]+r[:-i]for i,r in enumerate(zip(*M))]dos veces creando y evaluando la cadena

[r[-i:]+r[:-i]for i,r in enumerate(zip(*[r[-i:]+r[:-i]for i,r in enumerate(zip(*M))]))]

Esto supera por poco una transformación directa (70 bytes), suponiendo que la imagen es cuadrada y su longitud se puede tomar como entrada:

lambda M,n:[[M[(2*j-i)%n][(i-j)%n]for i in range(n)]for j in range(n)]
xnor
fuente
1

Macro ImageJ, 29 bytes

v=getPixel((x+y)%w,(2*y+x)%h)
  • Imagen abierta de Lena
  • En el menú Proceso, seleccione Matemáticas / Macro ...
rahnema1
fuente
¿No funciona esto f ^ (- 1)? Obtiene el valor de píxel en las coordenadas a las que se supone que debe moverlo . Probablemente te refieres v=getPixel((2*y-x)%w,(x-y)%h).
Robin Koch
@RobinKoch Gracias, 2*x+ycambiado a2*y+x
rahnema1
Eso no es lo que escribí ni lo que quise decir. Necesita la transformación inversa para su enfoque. Para f(x,y) = (2x+y, x+y)esta transformación inversa se describe por f^(-1) = (x-y, 2y-x). (Mi otro comentario fue incorrecto). Entonces su código debe ser v=getPixel((x-y)%w,(2*y-x)%h).
Robin Koch
Probé mi fórmula y el resultado es el mismo que la imagen de Lena en la pregunta
rahnema1
@RobinKoch Puede descargar ImageJ y probar ambas fórmulas
rahnema1
1

Java, 160

Golfizado:

int[][]f(int[][]m){int x=0,y,l=m.length,r[][]=new int[l][];for(;x<l;++x)r[x]=new int[l];for(x=0;x<l;++x)for(y=0;y<l;++y)r[(x+y)%l][(2*x+y)%l]=m[y][x];return r;}

Sin golf:

  int[][] f(int[][] m) {
    int x = 0, y, l = m.length, r[][] = new int[l][];
    for (; x < l; ++x) {
      r[x] = new int[l];
    }
    for (x = 0; x < l; ++x) {
      for (y = 0; y < l; ++y) {
        r[(x + y) % l][(2 * x + y) % l] = m[y][x];
      }
    }
    return r;
  }

fuente