Arnolds Katzenkarte

21

Herausforderung

Wenn Sie ein Farbrasterbild * mit der gleichen Breite und Höhe erhalten, geben Sie das Bild transformiert unter Arnolds Katzenkarte aus . (* Details siehe unten)

Definition

Bei der Größe des Bildes Nnehmen wir an, dass die Koordinaten eines Pixels als Zahlen zwischen 0und angegeben werden N-1.

Arnolds Katzenkarte wird dann folgendermaßen definiert:

Ein Pixel an Koordinaten [x,y]wird nach verschoben [(2*x + y) mod N, (x + y) mod N].

Dies ist nichts anderes als eine lineare Transformation auf dem Torus: Der gelbe, violette und grüne Teil werden aufgrund von auf das Anfangsquadrat zurück abgebildet mod N.

Visualisierung

Diese Karte (nennen wir sie f) hat folgende Eigenschaften:

  • Es ist bijektiv , das heißt reversibel: Es ist eine lineare Transformation mit der Matrix [[2,1],[1,1]]. Da es eine Determinante hat 1und nur ganzzahlige Einträge hat, hat das Inverse auch nur ganzzahlige Einträge und ist gegeben durch [[1,-1],[-1,2]], dh es ist auch bijektiv für ganzzahlige Koordinaten.

  • Es ist ein Torsionselement der Gruppe der bijektiven Karten von N x NBildern, dh wenn Sie es ausreichend oft anwenden, erhalten Sie das Originalbild zurück: f(f(...f(x)...)) = xDie Häufigkeit, mit der die Karte auf sich selbst angewendet wird, führt garantiert zu einer geringeren Identität oder gleich 3*N. Im Folgenden sehen Sie das Bild einer Katze nach einer bestimmten Anzahl iterierter Anwendungen von Arnolds Katzenkarte und eine Animation, wie eine wiederholte Anwendung aussieht:

mehrfache wiederholte Anwendungen

Einzelheiten

  • Ihr Programm muss sich nicht unbedingt mit Bildern befassen, aber auch 2D-Arrays / Matrizen, Strings oder ähnliche 2D-Strukturen sind akzeptabel.

  • Es spielt keine Rolle, ob sich Ihr (0,0)Punkt links unten oder links oben befindet. (Oder in einer anderen Ecke, wenn dies in Ihrer Sprache praktischer ist.) Bitte geben Sie an, welche Konvention Sie in Ihrer Einreichung verwenden.

Testfälle

In Matrixform ( [1,2,3,4]ist die oberste Zeile, 1hat Index(0,0) , 2hat Index (1,0), 5hat 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

Als Bild (links unten ist (0,0) ):

fehlerhaft
quelle
1
Die arme Lena. Ich hoffe, Sie haben lange genug iteriert
Luis Mendo
2
Können wir die Bildgröße als Eingabe nehmen? Ist es immer quadratisch?
Xnor
1
Ja, das Bild ist immer quadratisch, und ich bin mir nicht sicher, wie groß es sein soll. Gibt es etwas dagegen, das zuzulassen?
Fehler

Antworten:

10

Gelee , 9 Bytes

Zṙ"JC$µ2¡

Probieren Sie es online! Die Koordinaten sind wie in der Antwort.

Erläuterung

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

Diese Wicklung schert die Matrix in die eine Richtung und dann in die andere.

Lynn
quelle
Fantastischer Algorithmus!
Greg Martin
7

MATL , 23 Bytes

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

Der (0,0)Punkt ist oben links, wie in den Beispielen im Herausforderungstext.

Probieren Sie es online!

Erläuterung

Eine Matrix in MATL kann mit einem Index anstelle von zwei Indizes indiziert werden. Dies wird als lineare Indizierung bezeichnet und verwendet die Hauptreihenfolge der Spalten . Dies wird durch die folgende 4 × 4-Matrix veranschaulicht, in der der Wert an jedem Eintrag mit seinem linearen Index übereinstimmt:

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

Es gibt zwei ähnliche Ansätze, um das Mapping in der Challenge zu implementieren:

  1. Erstellen Sie eine Indizierungsmatrix, die die inverse Abbildung von Arnold auf lineare Indizes darstellt, und wählen Sie die Werte aus der ursprünglichen Matrix aus. Für den 4 × 4-Fall wäre die Indexierungsmatrix

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

    man sagt zum Beispiel, dass das Original 5bei x = 2, y = 1 zu x = 3, y = 2 geht. Diese Operation wird als Referenzindizierung bezeichnet : Verwenden Sie die Indexierungsmatrix, um zu bestimmen, welches Element aus der ursprünglichen Matrix ausgewählt werden soll. Dies ist eine Funktion ), die zwei Eingaben akzeptiert (in der Standardkonfiguration).

  2. Erstellen Sie eine Indexierungsmatrix, die Arnolds direkte Zuordnung zu linearen Indizes darstellt, und schreiben Sie die Werte in die ursprüngliche Matrix. Für den 4 × 4-Fall wäre die Indexierungsmatrix

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

    Mitteilen , dass der Eintrag x = 2, y = 1 der neuen Matrix auf den Eintrag mit linearem Index überschrieben wird 10, das, ist x = 3, y = 2 ist . Dies wird als Zuweisungsindizierung bezeichnet : Verwenden Sie die Indexierungsmatrix, eine Datenmatrix und die Originalmatrix, und schreiben Sie die Daten an den angegebenen Indizes auf die Originalmatrix. Dies ist die Funktion (, die (in der Standardkonfiguration) drei Eingaben akzeptiert.

Methode 1 ist einfacher, aber Methode 2 hat sich als kürzer herausgestellt.

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
quelle
5

Mathematica, 44 Bytes

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

Eine Portierung von Lynns fantastischem Algorithmus . Es gibt ein unsichtbares 3-Byte-Zeichen, U + F3C7 in der UTF-8-Codierung, vor dem letzten ]. Mathematica gibt es hochgestellt wiederT und übernimmt die Transponierung einer Matrix.

Mathematica, 54 Bytes

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

Unbenannte Funktion, die zwei Argumente verwendet, eine positive Ganzzahl #und ein 2D-Array #2mit den Dimensionen #x #, und ein 2D-Array mit ähnlicher Form zurückgibt. Wie im gegebenen Testfall befindet sich der Punkt mit den Koordinaten {0,0} oben links und die x-Achse ist horizontal. Einfache Implementierung mit dem [[1,-1],[-1,2]]in der Frage erwähnten Inversen , wobei -1in der ersten Koordinate berücksichtigt wird, dass Arrays in Mathematica von Natur aus 1-indiziert sind. Wenn wir die Dimension der Matrix nicht als zusätzliches Argument verwenden dürfen, wird diese Lösung neun Byte länger (ersetzen Sie das erste #- nicht das #2- mit a=Length@#und alle nachfolgenden #s durch as).

Greg Martin
quelle
Dang, schlagen Sie mich dazu
JungHwan Min
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

Die Eingabe ist eine Liste von Listen.
Die Zeichenfolge in der Exec transponiert die Liste von Listen und dreht jede Liste zyklisch um den Zeilenindex (0 basierend - die 3. Zeile wird zweimal nach rechts gedreht).
Dieser Vorgang wird zweimal für die Eingabe ausgeführt.

+4 Bytes, die die Transformation N-mal durchführen

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

Haskell, 55 Bytes

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

Anwendungsbeispiel: [[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,0ist die obere linke Ecke. Dies verwendet die inverse Transformation.

nimi
quelle
1

Python, 69 Bytes

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

Eine Verbesserung gegenüber Rods Transponierungs- und Shift-2-Methode . Wendet die Operation M -> [r[-i:]+r[:-i]for i,r in enumerate(zip(*M))]zweimal an, indem die Zeichenfolge erstellt und ausgewertet wird

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

Damit wird eine direkte Transformation (70 Byte) knapp unterbunden, vorausgesetzt, das Bild ist quadratisch und seine Länge kann als Eingabe verwendet werden:

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

ImageJ-Makro, 29 Byte

v=getPixel((x+y)%w,(2*y+x)%h)
  • Bild von Lena öffnen
  • Wählen Sie im Menü Process die Option Math / Macro ...
rahnema1
quelle
Führt dies nicht f ^ (- 1) aus? Der Pixelwert wird an den Koordinaten abgerufen, zu denen er verschoben werden soll. Du meinst wahrscheinlich v=getPixel((2*y-x)%w,(x-y)%h).
Robin Koch
@RobinKoch Danke, 2*x+ygeändert zu2*y+x
rahnema1
Das habe ich weder geschrieben noch gemeint. Sie benötigen die inverse Transformation für Ihren Ansatz. Für f(x,y) = (2x+y, x+y)diese inverse Transformation wird durch beschrieben f^(-1) = (x-y, 2y-x). (Mein anderer Kommentar war falsch.) Also sollte Ihr Code sein v=getPixel((x-y)%w,(2*y-x)%h).
Robin Koch
Ich habe meine Formel getestet und das Ergebnis ist das gleiche wie das von Lena in der Frage
rahnema1
@RobinKoch Du kannst ImageJ herunterladen und beide Formeln testen
rahnema1
1

Java, 160

Golf gespielt:

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

quelle