Hilbert-Curvify a Matrix

19

Zainspirowany tym pytaniem

Innym sposobem na rozwinięcie obrazu 2D w ciąg 1D jest użycie krzywej Hilberta.

Istnieje wiele wersji tej krzywej, w zależności od liczby iteracji użytych podczas jej obliczania. Poniżej przykład krzywych Hilberta od pierwszego rzędu do piątego rzędu.

wprowadź opis zdjęcia tutaj

Sposób obliczenia tej krzywej jest następujący. Najpierw definiujemy Krzywą Hilberta pierwszego rzędu jako pokazaną na rysunku (tę dla n = 1), tak aby pasowała do kwadratu 1x1. Następnie wykonujemy cztery kopie tej krzywej, rozmieszczając je w kwadracie 4x4, tak aby wszystkie prezentowały „wklęsłość” w kierunku lewej strony. Następnie odwracamy dwie krzywe 1 rzędu w lewo, tak aby wklęsłość górna skierowana była ku górze, a dno skierowane w dół. W końcu łączymy rogi sąsiednich krzywych Hilberta. Jeśli chcemy uzyskać krzywą uporządkowania (n + 1), wystarczy powtórzyć proces z czterema krzywymi rzędu n. Możemy zobaczyć wizualizację procesu tutaj (będę również dodać obraz szczegółowo proces wkrótce)

Twoim zadaniem w tym wyzwaniu jest rozwinięcie macierzy liczb całkowitych wzdłuż najniższej kolejności krzywej Hilberta dla tej macierzy.

Dla uproszczenia krzywą zaczynamy od lewego górnego rogu matrycy.

Możesz otrzymać dane wejściowe jako listę liczb całkowitych, gdzie każda lista podrzędna reprezentuje wiersz macierzy.

Możesz założyć, że wejście będzie macierzą kwadratową (n * n).

Na przykład:

Wejście:

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

Wynik:

[ 1, 2, 4, 3 ]

Ponieważ używamy krzywej Hilberta pierwszego rzędu pokazanej na rysunku

Wejście:

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

Wynik:

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

Korzystanie z krzywej Hilberta drugiego rzędu

Jak zwykle standardowe luki nie są dozwolone.

To jest golf golfowy, więc wygrywa najkrótsza odpowiedź w bajtach.

WizardOfMenlo
źródło
1
Związane z.
ETHprodukcje
@StewieGriffin na pewno, jestem na tym
WizardOfMenlo
1
@StewieGriffin Dodałem krótkie podsumowanie, wykonam dokładniejszą robotę w ciągu około godziny, po ukończeniu lekcji
WizardOfMenlo
W razie potrzeby do matrycy być kwadratowy jeden potrzebuje również brak się moc 2.
mbomb007

Odpowiedzi:

5

MATL , 86 85 bajtów

To rozwiązanie jest oparte na wpisie wymiany plików Jonasa Lundgrena, który wykorzystuje liczby zespolone do wygenerowania krzywej Hilberta. Te liczby zespolone są następnie konwertowane na wartości indeksu, aby pobrać elementy macierzy, które opadają wzdłuż krzywej.

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

Wypróbuj online!

Wyjaśnienie

%--- 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
źródło
@DonMuesli Pracuję nad lepszym sposobem radzenia sobie z tym. To zdecydowanie dalekie od elegancji! Dzięki za wskazówki. Zaktualizowano!
Suever
Nie analizowałem tego konkretnego wyzwania. Czasami nie da się uniknąć
schowków
5

APL (Dyalog Unicode) , 41 bajtów SBCS

Zaoszczędzono 30 bajtów (!), Sprawdzając mądrość sadu APL, zwłaszcza @ngn i @ Sherlock9.

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

Wypróbuj online!

Wyjaśnienie w następujący sposób:

{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.

Więcej informacji na temat „ monadycznego skanowania transpozycji ”.

Dokumentacja Dyalog na temat zabezpieczeń przed błędami .

jastrząb
źródło
3

Mathcad, 302 bajty

Poniższy program Mathcad jest oparty na programie Python @ Sherlock9. Różni się tym, że krzywizuje prostokątne macierze, ignorując te części Krzywej Hilberta, które leżą poza granicami macierzy. Zauważ, że ponieważ Mathcad ma stosunkowo słabą obsługę ciągów, zamapowałem symbole Lindenmayera na liczby całkowite w funkcji Hilberta.

wprowadź opis zdjęcia tutaj

Mathcad działa poprzez interfejs 2D, który pozwala użytkownikowi umieszczać (i dowolnie miksować) wyrażenia matematyczne, wykresy, tekst, dane wejściowe i wyjściowe. Zrównowałem bajt z operacją odpowiadającą minimalnej klawiaturze użytkownika, aby utworzyć symbol (na przykład operator definicji (: =) wprowadza się po prostu wpisując:.

Stuart Bruff
źródło
3

Python 3, 327 289 275 271 239 234 bajtów

Jest to rozwiązanie, które zmodyfikowałem z odpowiedzi na inne pytanie krzywej Hilberta tutaj . Wszelkie wskazówki dotyczące gry w golfa są mile widziane.

Edycja: Zmieniono sposób gzwiększania i zmniejszania. Teraz za pomocą eval()i str.translate. Już nie używa 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

Nie golfowany:

# 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
źródło
2

Wolfram - 233

W oparciu o reprezentację jako system Lindenmayera :

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}}]]
śmigać
źródło
Czy możesz opublikować zrzuty ekranu z jego działaniem dla użytkowników, którzy nie mają Mathematica?
WizardOfMenlo,
2
Czy „Wolfram” różni się od Mathematica? Jeśli nie, należy go nazwać Mathematica.
mbomb007
@WizardOfMenlo Tutaj działa online
świst
@swish Myślę, że musisz zmienić uprawnienia aplikacji sieci Web, wygląda na to, że jest zablokowana
WizardOfMenlo
@ mbomb007 Wolfram to nazwa języka , Mathematica jest jak IDE.
świst
1

Rubinowy, 224 221 216 bajtów

Ta odpowiedź jest oparta na mojej odpowiedzi w języku Python .

->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
źródło
1

CJam, 60

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

Wypróbuj online

Wyjaśnienie:

Fraktal buduję jako serię kierunków ruchu: 0 = prawo, 1 = dół, 2 = lewo, 3 = góra.

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
źródło