Carte du chat d'Arnold

21

Défi

Étant donné une image raster couleur * avec la même largeur et la même hauteur, sortez l'image transformée sous la carte de chat d'Arnold . (* détails voir ci-dessous)

Définition

Étant donné la taille de l'image, Nnous supposons que les coordonnées d'un pixel sont données sous forme de nombres entre 0et N-1.

La carte du chat d'Arnold est alors définie comme suit:

Un pixel aux coordonnées [x,y]est déplacé vers [(2*x + y) mod N, (x + y) mod N].

Ce n'est rien d'autre qu'une transformation linéaire sur le tore: les parties jaune, violette et verte sont remappées sur le carré initial en raison de la mod N.

visualisation

Cette carte (appelons-la f) a les propriétés suivantes:

  • C'est bijectif , c'est-à-dire réversible: c'est une transformation linéaire avec la matrice [[2,1],[1,1]]. Comme il a un déterminant 1et qu'il n'a que des entrées entières, l'inverse n'a également que des entrées entières et est donné par [[1,-1],[-1,2]], cela signifie qu'il est également bijectif sur les coordonnées entières.

  • Il s'agit d'un élément de torsion du groupe de cartes d' N x Nimages bijectives , ce qui signifie que si vous l'appliquez suffisamment de fois, vous récupérerez l'image d'origine: f(f(...f(x)...)) = xle nombre de fois où la carte appliquée à elle-même se traduit par l'identité est garanti inférieur ou égal à 3*N. Dans ce qui suit, vous pouvez voir l'image d'un chat après un nombre donné d'applications itérées de la carte de chat d' Arnold , et une animation de ce à quoi ressemble une application répétée:

plusieurs applications répétées

Détails

  • Votre programme ne doit pas nécessairement traiter d'images, mais les matrices / matrices 2D, les chaînes ou les structures 2D similaires sont également acceptables.

  • Peu importe que votre (0,0)point soit en bas à gauche ou en haut à gauche. (Ou dans tout autre coin, si cela est plus pratique dans votre langue.) Veuillez spécifier la convention que vous utilisez dans votre soumission.

Cas de test

Sous forme matricielle ( [1,2,3,4]est la ligne supérieure, 1a un index (0,0), 2a un index (1,0), 5a un index (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

Comme image (en bas à gauche (0,0)):

flawr
la source
1
Pauvre Lena. J'espère que vous avez continué à répéter assez longtemps
Luis Mendo
2
Pouvons-nous prendre la taille de l'image comme entrée? Est-il toujours carré?
xnor
1
Oui, l'image est toujours carrée, et je ne suis pas sûr de la taille, y a-t-il quelque chose contre cela?
flawr

Réponses:

10

Gelée , 9 octets

Zṙ"JC$µ2¡

Essayez-le en ligne! Les coordonnées sont comme dans la réponse.

Explication

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

Cela enveloppe la matrice dans un sens, puis dans l'autre.

Lynn
la source
Algorithme fantastique!
Greg Martin
7

MATL , 23 octets

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

Le (0,0)point est en haut à gauche, comme dans les exemples du texte du défi.

Essayez-le en ligne!

Explication

Une matrice en MATL peut être indexée avec un seul index au lieu de deux. C'est ce qu'on appelle l'indexation linéaire et utilise l'ordre des colonnes principales . Ceci est illustré par la matrice 4 × 4 suivante, dans laquelle la valeur à chaque entrée coïncide avec son indice linéaire:

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

Il existe deux approches similaires pour implémenter la cartographie dans le défi:

  1. Créez une matrice d'indexation qui représente le mappage inverse d'Arnold sur des indices linéaires et utilisez-la pour sélectionner les valeurs dans la matrice d'origine. Pour le cas 4 × 4, la matrice d'indexation serait

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

    dire que par exemple l'original 5à x = 2, y = 1 va à x = 3, y = 2. Cette opération est appelée indexation de référence : utilisez la matrice d'indexation pour dire quel élément choisir dans la matrice d'origine. C'est functon ), qui prend deux entrées (dans sa configuration par défaut).

  2. Créez une matrice d'indexation qui représente le mappage direct d'Arnold sur des indices linéaires et utilisez-la pour écrire les valeurs dans la matrice d'origine. Pour le cas 4 × 4, la matrice d'indexation serait

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

    indiquant que l'entrée x = 2, y = 1 de la nouvelle matrice sera écrasée sur l'entrée avec un index linéaire 10, c'est-à-dire x = 3, y = 2. C'est ce qu'on appelle l' indexation d'affectation : utilisez la matrice d'indexation, une matrice de données et la matrice d'origine, et écrivez les données sur la matrice d'origine aux indices spécifiés. C'est la fonction (, qui prend trois entrées (dans sa configuration par défaut).

La méthode 1 est plus simple, mais la méthode 2 s'est avérée plus courte.

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
la source
5

Mathematica, 44 octets

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

Un port de l'algorithme fantastique de Lynn . Il y a un caractère invisible de 3 octets, U + F3C7 dans l'encodage UTF-8, avant le dernier ]; Mathematica le rend en exposant Tet prend la transposition d'une matrice.

Mathematica, 54 octets

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

Fonction sans nom prenant deux arguments, un entier positif #et un tableau 2D #2de dimensions #x #, et renvoyant un tableau 2D de forme similaire. Comme dans le cas de test donné, le point avec les coordonnées {0,0} est en haut à gauche et l'axe x est horizontal. Implémentation simple utilisant l'inverse [[1,-1],[-1,2]]mentionné dans la question, avec un -1dans la première coordonnée pour tenir compte du fait que les tableaux sont intrinsèquement 1-indexés dans Mathematica. Si nous ne sommes pas autorisés à prendre la dimension de la matrice comme argument supplémentaire, alors cette solution devient plus longue de neuf octets (remplacez le premier #- pas le #2- par a=Length@#et tous les #s suivants par as).

Greg Martin
la source
Dang, bat-moi dessus
JungHwan Min
3

Python 2, 89 82 77 73 octets

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

L'entrée est une liste de listes
La chaîne à l'intérieur de l'exécutif transpose la liste des listes et fait tourner cycliquement chaque liste par l'index de la ligne (base 0 - la 3ème ligne est tournée 2 fois vers la droite).
Ce processus est effectué 2 fois sur l'entrée.

+4 octets qui feront la transformation N fois

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

Haskell, 55 octets

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

Exemple d'utilisation: [[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,0est le coin supérieur gauche. Cela utilise la transformation inverse.

nimi
la source
1

Python, 69 octets

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

Une amélioration de la méthode de transposition et de décalage deux fois de Rod . Applique l'opération M -> [r[-i:]+r[:-i]for i,r in enumerate(zip(*M))]deux fois en créant et en évaluant la chaîne

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

Cela bat de justesse une transformation directe (70 octets), en supposant que l'image est carrée et que sa longueur peut être prise en entrée:

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

Macro ImageJ, 29 octets

v=getPixel((x+y)%w,(2*y+x)%h)
  • Image ouverte de Lena
  • Dans le menu Process, sélectionnez Math / Macro ...
rahnema1
la source
Cela ne fonctionne-t-il pas f ^ (- 1)? Il obtient la valeur du pixel aux coordonnées où il est supprimé pour le déplacer . Vous voulez probablement dire v=getPixel((2*y-x)%w,(x-y)%h).
Robin Koch
@RobinKoch Thanks, 2*x+ychangé en2*y+x
rahnema1
Ce n'est ni ce que j'ai écrit ni ce que je voulais dire. Vous avez besoin de la transformation inverse pour votre approche. Pour f(x,y) = (2x+y, x+y)cette transformation inverse est décrite par f^(-1) = (x-y, 2y-x). (Mon autre commentaire était faux.) Donc votre code doit être v=getPixel((x-y)%w,(2*y-x)%h).
Robin Koch
J'ai testé ma formule et le résultat est le même que l'image de Lena dans la question
rahnema1
@RobinKoch Vous pouvez télécharger ImageJ et tester les deux formules
rahnema1
1

Java, 160

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

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

la source