Mappa del gatto di Arnold


21

Sfida

Data un'immagine raster a colori * con la stessa larghezza e altezza, produce l'immagine trasformata sotto la mappa del gatto di Arnold . (* dettagli vedi sotto)

Definizione

Data la dimensione dell'immagine N, supponiamo che le coordinate di un pixel siano indicate come numeri tra 0e N-1.

La mappa dei gatti di Arnold viene quindi definita come segue:

Un pixel alle coordinate [x,y]viene spostato in [(2*x + y) mod N, (x + y) mod N].

Questo non è altro che una trasformazione lineare sul toro: la parte gialla, viola e verde viene mappata sul quadrato iniziale a causa del mod N.

visualizzazione

Questa mappa (chiamiamola f) ha le seguenti proprietà:

  • È biiettivo , ciò significa reversibile: è una trasformazione lineare con la matrice [[2,1],[1,1]]. Poiché ha determinante 1e ha solo voci intere, anche l'inverso ha solo voci intere ed è dato da [[1,-1],[-1,2]], ciò significa che è anche biiettivo su coordinate intere.

  • È un elemento di torsione del gruppo di mappe biiettive di N x Nimmagini, il che significa che se lo applichi abbastanza volte, otterrai di nuovo l'immagine originale: f(f(...f(x)...)) = xla quantità di volte in cui la mappa applicata a se stessa risulta nell'identità è garantita essere inferiore o uguale a 3*N. Di seguito puoi vedere l'immagine di un gatto dopo un determinato numero di applicazioni iterate della mappa del gatto di Arnold e un'animazione di come appare un'applicazione ripetuta:

molteplici applicazioni ripetute

Dettagli

  • Il tuo programma non ha necessariamente a che fare con immagini, ma sono accettabili anche array / matrici 2D, stringhe o strutture 2D simili.

  • Non importa se il (0,0)punto è in basso a sinistra o in alto a sinistra. (O in qualsiasi altro angolo, se è più conveniente nella tua lingua.) Specifica quale convenzione usi nella tua presentazione.

Casi test

In forma di matrice ( [1,2,3,4]è la riga superiore, 1ha indice (0,0), 2ha indice (1,0), 5ha indice (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

Come immagine (in basso a sinistra (0,0)):


1
Povera Lena. Spero che tu abbia continuato a ripetere abbastanza a lungo
Luis Mendo il

2
Possiamo prendere le dimensioni dell'immagine come input? È sempre quadrato?
xnor

1
Sì l'immagine è sempre quadrata, e non sono sicuro circa la dimensione, c'è qualcosa contro permettendo che?
flawr

Risposte:


10

Gelatina , 9 byte

Zṙ"JC$µ2¡

Provalo online! Le coordinate sono come nella risposta.

Spiegazione

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

Questo avvolge la matrice in una direzione, poi nell'altra.


Algoritmo fantastico!
Greg Martin,

7

MATL , 23 byte

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

Il (0,0)punto è in alto a sinistra, come negli esempi nel testo della sfida.

Provalo online!

Spiegazione

Una matrice in MATL può essere indicizzata con un singolo indice anziché due. Questo è chiamato indicizzazione lineare e utilizza l' ordine delle colonne principali . Ciò è illustrato dalla seguente matrice 4 × 4, in cui il valore di ciascuna voce coincide con il suo indice lineare:

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

Esistono due approcci simili per implementare la mappatura nella sfida:

  1. Costruisci una matrice di indicizzazione che rappresenti la mappatura inversa di Arnold su indici lineari e usala per selezionare i valori dalla matrice originale. Per il caso 4 × 4, la matrice di indicizzazione sarebbe

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

    dicendo che per esempio l'originale 5in x = 2, y = 1 va in x = 3, y = 2. Questa operazione è chiamata indicizzazione di riferimento : usa la matrice di indicizzazione per dire quale elemento scegliere dalla matrice originale. Questo è functon ), che accetta due input (nella sua configurazione predefinita).

  2. Costruisci una matrice di indicizzazione che rappresenti la mappatura diretta di Arnold su indici lineari e usala per scrivere i valori nella matrice originale. Per il caso 4 × 4, la matrice di indicizzazione sarebbe

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

    dicendo che la voce x = 2, y = 1 della nuova matrice verrà sovrascritta sulla voce con indice lineare 10, cioè x = 3, y = 2. Questo si chiama indicizzazione di assegnazione : utilizzare la matrice di indicizzazione, una matrice di dati e la matrice originale e scrivere i dati sulla matrice originale negli indici specificati. Questa è la funzione( , che accetta tre input (nella sua configurazione predefinita).

Il metodo 1 è più semplice, ma il metodo 2 si è rivelato più breve.

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

5

Mathematica, 44 byte

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

Una porta del fantastico algoritmo di Lynn . C'è un carattere invisibile a 3 byte, U + F3C7 nella codifica UTF-8, prima dell'ultimo ]; Mathematica lo rende come un apice Te prende la trasposizione di una matrice.

Mathematica, 54 byte

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

Funzione senza nome che accetta due argomenti, un numero intero positivo #e un array 2D #2di dimensioni #x #e che restituisce un array 2D di forma simile. Come nel caso di test dato, il punto con coordinate {0,0} si trova in alto a sinistra e l'asse x è orizzontale. Implementazione semplice usando l'inverso [[1,-1],[-1,2]]menzionato nella domanda, con una -1nella prima coordinata per tenere conto del fatto che gli array sono intrinsecamente 1-indicizzati in Mathematica. Se non ci è permesso di prendere la dimensione della matrice come argomento aggiuntivo, allora questa soluzione diventa più lunga di nove byte (sostituisci la prima #— non la #2— con a=Length@#e tutte le successive #con as).


Dang,
battimi

3

Python 2, 89 82 77 73 byte

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

L'input è un elenco di elenchi
La stringa all'interno di exec traspone l'elenco di elenchi e ruota ciclicamente ciascun elenco in base all'indice di riga (basato su 0 - la terza riga viene ruotata 2 volte a destra).
Questo processo viene eseguito 2 volte sull'input.

+4 byte che eseguiranno la trasformazione N volte

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

2

Haskell, 55 byte

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

Esempio di utilizzo: [[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,0è l'angolo in alto a sinistra. Questo utilizza la trasformazione inversa.


1

Python, 69 byte

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

Un miglioramento del metodo di trasporre e spostare due volte Rod . Applica l'operazione M -> [r[-i:]+r[:-i]for i,r in enumerate(zip(*M))]due volte creando e valutando la stringa

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

Questo batte leggermente una trasformazione diretta (70 byte), supponendo che l'immagine sia quadrata e che la sua lunghezza possa essere presa come input:

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

1

Macro ImageJ, 29 byte

v=getPixel((x+y)%w,(2*y+x)%h)
  • Apri immagine di Lena
  • Dal menu Processo selezionare Matematica / Macro ...

Questo non esegue f ^ (- 1)? Ottiene il valore in pixel alle coordinate in cui è supposto che lo sposti . Probabilmente intendi v=getPixel((2*y-x)%w,(x-y)%h).
Robin Koch,

@RobinKoch Grazie, 2*x+ycambiato in2*y+x
rahnema1 il

Non è né quello che ho scritto né quello che intendevo dire. Hai bisogno della trasformazione inversa per il tuo approccio. Per f(x,y) = (2x+y, x+y)questa trasformazione inversa è descritta da f^(-1) = (x-y, 2y-x). (L'altro mio commento era sbagliato.) Quindi il tuo codice dovrebbe essere v=getPixel((x-y)%w,(2*y-x)%h).
Robin Koch,

Ho testato la mia formula e il risultato è lo stesso dell'immagine di Lena nella domanda
rahnema1

@RobinKoch Puoi scaricare ImageJ e testare entrambe le formule
rahnema1,

1

Java, 160

golfed:

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;}

Ungolfed:

  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;
  }
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.