Hilbert-Curvify eine Matrix

19

Inspiriert von dieser Frage

Eine andere Möglichkeit, ein 2D-Bild in eine 1D-Zeichenfolge abzurollen, ist die Verwendung einer Hilbert-Kurve.

Abhängig von der Anzahl der beim Berechnen verwendeten Iterationen gibt es viele Versionen dieser Kurve. Nachfolgend sehen Sie ein Beispiel für Hilbert-Kurven von erster bis fünfter Ordnung.

Bildbeschreibung hier eingeben

Die Berechnungsweise für diese Kurve ist die folgende. Zuerst definieren wir die Hilbert-Kurve erster Ordnung als die in Abbildung gezeigte (die für n = 1), so dass sie in ein 1x1-Quadrat passt. Wir machen dann vier Kopien dieser Kurve und platzieren sie in einem 4x4-Quadrat, so dass sie alle die "Konkavität" zur linken Seite hin aufweisen. Wir drehen dann die beiden Kurven ganz links um 1, so dass die obere Konkavität nach oben zeigt, während die untere nach unten zeigt. Wir verbinden schließlich die Ecken der benachbarten Hilbertkurven. Um eine Kurve (n + 1) zu erhalten, müssen wir den Vorgang nur mit vier Kurven n-ter Ordnung wiederholen. Wir können hier eine Visualisierung des Prozesses sehen (ich werde auch bald ein Bild hinzufügen, das den Prozess detailliert)

Ihre Aufgabe bei dieser Herausforderung ist es, eine Matrix von ganzen Zahlen entlang der Hilbert-Kurve niedrigster Ordnung für diese Matrix zu lösen .

Der Einfachheit halber wird die Kurve in der oberen linken Ecke der Matrix beginnen.

Sie können die Eingabe entweder als Liste mit ganzen Zahlen empfangen, wobei jede Unterliste eine Zeile der Matrix darstellt.

Sie können davon ausgehen, dass die Eingabe eine quadratische Matrix (n * n) ist.

Beispielsweise:

Eingang:

[[ 1, 2,]
 [ 3, 4 ]]

Ausgabe:

[ 1, 2, 4, 3 ]

Da wir die in der Abbildung gezeigte Hilbertkurve erster Ordnung verwenden

Eingang:

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

Ausgabe:

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

Verwendung der Hilbert-Kurve zweiter Ordnung

Standardlücken sind wie üblich nicht zulässig.

Das ist Code-Golf, also gewinnt die kürzeste Antwort in Byte.

WizardOfMenlo
quelle
1
Verbunden.
ETHproductions
@ StewieGriffin sicher, ich bin dabei
WizardOfMenlo
1
@StewieGriffin Ich habe eine kurze Zusammenfassung hinzugefügt, ich werde in der nächsten Stunde oder so nach Abschluss des Unterrichts einen gründlicheren Job machen
WizardOfMenlo
Die Matrix - Anforderungen nicht nur ein Platz sein, es muss auch n eine Potenz von 2 sein
mbomb007

Antworten:

5

MATL , 86 85 Bytes

Diese Lösung basiert auf dem File Exchange-Eintrag von Jonas Lundgren, der komplexe Zahlen verwendet, um die Hilbert-Kurve zu erstellen. Diese komplexen Zahlen werden dann in Indexwerte konvertiert, um die Elemente der Matrix abzurufen, die entlang der Kurve fallen.

nZl2/1XLJQXH1J-XI0,1L:"XJJZj1j*XKKH-JI-JH+IK-,4$h2/]XJJ1L*XJJH+J1)-XHGHXjHYj3$)1$Xd1$

Probieren Sie es online!

Erläuterung

%--- Define some numbers to be used throughout ---%
n                   % Retrieve the number of elements in the input matrix
Zl2/                % Compute the order of the curve (log2(numel(i))/2)
1XL                 % Store the order in the 1L clipboard
JQ XH               % Store 1 + j in H clipboard
1J- XI              % Store 1 - j in I clipboard
0                   % Place 0 onto the stack

%--- Compute the hilbert curve ---%
1L:"                % For k = 1:order
    XJ                   % Store the top of the stack (z) in J clipboard
    JZj                  % Compute the conjugate of z (stored in J)
    1j*                  % Multiply by j to get conj(z) * j
    XK                   % Store result in K clipboard
    KH- JI- JH+ IK- 4$h  % Horizontal concatenation of K-H, J-I, J+H, and I-K
    2/                   % Divide entire array by 2
]                   % End for loop
XJ                  % Store z in J clipboard

%----- Convert complex decimal values to complex integer indices ----%
J1L*                % Multiply z by the order
XJ                  % Store result in clipboard J
JH+                 % Add 1 + j to H
J1)-                % Subtract the first element of z
XH                  % Store integer complex numbers in H

%--- Retrieve the elements from the input along the curve ---%  
G HXj HYj 3$)       % Index into input using real/imag components input(real, imag)
                    % This will yield an numel(real) x numel(imag) matrix where 
            % the diagonal values are the values we want
1$Xd                % Extract the diagonals using diag with one input
1$                   % Display only the top element on the stack
Suever
quelle
@DonMuesli Ich arbeite an einer besseren Möglichkeit, damit umzugehen. Es ist definitiv alles andere als elegant! Danke für die Hinweise. Aktualisiert!
Suever
Ich habe mich nicht mit dieser speziellen Herausforderung befasst. Manchmal können Zwischenablagen nicht vermieden werden
Luis Mendo
5

APL (Dyalog Unicode) , 41 Byte SBCS

30 Bytes (!) Gespart, indem die Weisheit von APL Orchard konsultiert wurde, insbesondere @ngn und @ Sherlock9.

{0::⍵⋄∊∇¨⌽∘⊖¨@4,⌽@1⊢∘⍉\⌽↑∘⍵¨∘.,⍨2 ¯2÷⍨≢⍵}

Probieren Sie es online!

Erklärung wie folgt:

{0::⍵⋄∊∇¨⌽∘⊖¨@4,⌽@1⊢∘⍉\⌽↑∘⍵¨∘.,⍨2 ¯2÷⍨≢⍵}  Recursive function - takes input as an
                                           n*n square matrix
 0::⍵                                      Our base case - this is an error guard
                                           If there's any error, catch it and
                                          ⍝ return the function's input
                                      ≢⍵   Find the number of rows in the input
                                2 ¯2÷⍨     Divide the above by 2 and negative 2,
                                           resulting in a 2-element vector
                            ∘.,⍨           Outer product - take the above vector and
                                           apply concatenation (,) with each element
                                           against all elements in the vector. Since
                                           we have a 2-element vector, this results in
                                           a 2-by-2 matrix, e.g.
                                           [[(2,2),(22)],[(¯2,2),(¯22)]]
                        ↑∘⍵¨               For each element in the matrix, we apply
                                           "take" against our original input matrix.
                                           Take, given a negative number, will take
                                           elements from the end of a particular rank.
                                           With our argument above, this means that we end
                                           up with our original original input matrix
                                           split by quadrant into a 2-by-2 matrix.
                                           It is also worth noting that take expects
                                           an integer argument, so for matrices whose
                                           rowcount divided by two results in a decimal
                                           (i.e., 1-by-1 matrices), we throw an error
                                           which is caught by the guard above, returning
                                           the original input.
                                          Flip the above matrix about the vertical axis.
                   ⊢∘⍉\                    Apply a "monadic transpose scan". More details
                                           on how this works below, but for our purposes
                                           this applies transpose to each of the two 
                                           sub-matrices on the right half.
                ⌽@1                        Swap the two upper sub-matrices. Given our
                                           flip for the overall matrix above, this returns
                                           the two upper quadrants to their original
                                           positions.
               ,                           Ravel: flatten the 2-by-2 matrix into a
                                           4-element vector
         ⌽∘⊖¨@4                            Take the last element of the list (the lower
                                           right quadrant originally) and flip it
                                           along the vertical and horizontal axes. Given
                                           the transposition above, this has the final
                                           effect of transposition along the antidiagonal.
       ∇¨                                  For each element in the above vector, recurse.
                                          Recursively flatten the results into a single
                                           vector.

Weitere Details zu " Monadic Transpose Scan ".

Dyalog-Dokumentation zum Fehlerschutz .

voidhawk
quelle
3

Mathcad, 302 Bytes

Das folgende Mathcad-Programm basiert auf dem @ Sherlock9-Python-Programm. Sie unterscheidet sich durch die Krümmung rechteckiger Matrizen, indem die Teile der Hilbert-Kurve ignoriert werden, die außerhalb der Matrixgrenzen liegen. Beachten Sie, dass Mathcad ein relativ schlechtes String-Handling hat, weshalb ich die Lindenmayer-Symbole Ganzzahlen in der Hilbert-Funktion zugeordnet habe.

Bildbeschreibung hier eingeben

Mathcad arbeitet über eine 2D-Oberfläche, mit der der Benutzer mathematische Ausdrücke, Diagramme, Texte, Eingaben und Ausgaben platzieren (und beliebig mischen) kann. Ich habe ein Byte der minimalen Benutzertastatur-Entsprechungsoperation zum Erstellen eines Symbols gleichgesetzt (zum Beispiel wird der Definitionsoperator (: =) durch einfaches Eingeben von: eingegeben.

Stuart Bruff
quelle
3

Python 3, 327 289 275 271 239 234 Bytes

Dies ist eine Lösung, die ich von meiner Antwort auf eine andere Hilbertkurvenfrage hier geändert habe . Alle Golftipps sind willkommen.

Bearbeiten: Ändert, wie ginkrementiert und dekrementiert wird. Jetzt mit eval()und str.translate. Nicht mehr verwenden l=len(s).

def h(s):
 t=[s[0][0]];x=y=g=0;b="A"
 for j in range(len(bin(len(s)))-3):b=b.translate({65:"-BF+AFA+FB-",66:"+AF-BFB-FA+"})
 for c in b:g+=(c<"-")-(c=="-");a=c>"B";x,y=[[x,y],[[x+1-g%4,y],[x,y+g%4-2]][g%2]][a];t+=[s[x][y]]*a
 return t

Ungolfed:

# the following function is implemented in the code with b=b.translate

def hilbert(it):
    s="A"
    n=""
    for i in range(it):
        for c in s:
            if c == "A":
                n += "-BF+AFA+FB-"
            elif c == "B":
                n += "+AF-BFB-FA+"
            else:
                n += c
        s=n;n=""
    return s

def matrix_to_hilbert(mat):
    length = len(mat)       # this returns the number of rows in the matrix
    if length < 2:
        return mat
    it = len(bin(length)) - 3
    hil = hilbert(it)
    output = [mat[0][0]]    # a list that starts with the first element of the matrix
    x = 0
    y = 0
    heading = 0
    for char in hil:        # navigating the Hilbert curve
        if char == "-": heading += -1
        elif char == "+": heading += 1
        elif char == "F":
            if heading % 4 == 3: y += 1
            elif heading % 4 == 2: x -= 1
            elif heading % 4 == 1: y -= 1
            else: x += 1
            output.append(mat[x][y])
    return output
Sherlock9
quelle
2

Wolfram - 233

Basierend auf der Darstellung als Lindenmayer-System :

f[m_]:=m[[Sequence@@Reverse[#+1]]]&/@DeleteDuplicates@AnglePath[Pi/2,List@@StringReplace[Last@SubstitutionSystem[{"A"->"-BF+AFA+FB-","B"->"+AF-BFB-FA+"},"A",Round@Sqrt@Length@m],{"A"|"B"->"","-"->{0,-Pi/2},"+"->{0,Pi/2},"F"->{1,0}}]]
Swish
quelle
Könnten Sie ein paar Screenshots davon posten, die für Benutzer ohne Mathematica funktionieren?
WizardOfMenlo
2
Ist "Wolfram" anders als Mathematica? Wenn nicht, sollte es Mathematica heißen.
mbomb007
@WizardOfMenlo Hier funktioniert es online
swish
@swish Ich denke, Sie müssen die Berechtigung der Web-App ändern, es scheint blockiert zu sein
WizardOfMenlo
@ mbomb007 Wolfram ist der Name der Sprache , Mathematica ist wie eine IDE.
Swish
1

Ruby, 224 221 216 Bytes

Diese Antwort basiert auf meiner Python-Antwort .

->s{t=[s[0][0]];x=y=g=0;b=?A;(s.size.bit_length-1).times{b=b.split("").map{|c|c==?A?"-BF+AFA+FB-":c==?B?"+AF-BFB-FA+":c}.join("")};b.each_char{|c|g+=c==?-?-1:c==?+?1:0;(g%2>0?y+=g%4-2:x+=1-g%4;t<<s[x][y])if c==?F};t}

Ungolfing:

def hilbert(mat)
  result = mat[0][0]
  x = 0
  y = 0
  heading = 0
  b = "A"
  (mat.size.bit_length-1).times do each |j| # Hilbert curve using a Lindenmayer system
    a = b.split("").map do |char|
      if char == "A"
        "-BF+AFA+FB-"
      else if char == "B"
        "+AF-BFB-FA+"
      else
        char
      end
    end
    b = a.join("")
  end
  b.each_char do |char| # navigating the matrix
    if char == "-"
      heading += -1
    else if char == "+"
      heading += 1
    else if char == "F"
      if heading % 2 == 0
        y += heading % 4 - 2
      else
        x += 1 - heading % 4
      end
      result << s[x][y]
    end
  return result
  end
Sherlock9
quelle
1

CJam, 60

Lq~:A,2mL{:B1f^0B1B2B3f^]:+}*1+{AT=U=\2md'U^_~)@2*-':@+~;}%p

Probieren Sie es online aus

Erläuterung:

Ich baue das Fraktal als eine Reihe von Bewegungsrichtungen auf: 0 = rechts, 1 = unten, 2 = links, 3 = oben.

L          push an empty array (level 0 fractal)
q~:A       read the input, evaluate and store in A
,2mL       get the length (number of rows) and calculate the logarithm in base 2
            (to get the desired level)
{…}*       repeat <level> times
  :B       store the previous-level fractal in B
  1f^      XOR it with 1 (top-left part)
  0        (move right)
  B        copy the fractal (top right part)
  1        (move down)
  B        copy the fractal (bottom right part)
  2        (move left)
  B3f^     copy the fractal and XOR it with 3 (bottom left part)
  ]:+      put everything in an array and concatenate the parts
1+         add a dummy move (needed for the last step)
{…}%       apply to each direction in the array
  AT=U=    push A[T][U] (T and U are initially 0)
  \2md     bring the direction to the top and get the quotient and remainder mod 2
  'U^      XOR the 'U' character with the remainder,
            to get the variable we want to modify
  _~)      make a copy of it, then evaluate it and increment
  @2*-     bring the quotient to the top, multiply by 2 and subtract
  ':@+     concatenate ':' with the variable name
  ~;       evaluate (this updates the variable) and pop the result
p          pretty-print the resulting array
aditsu
quelle