Hilbert-Curvifique uma Matriz


19

Inspirado por esta pergunta

Outra maneira de desenrolar uma imagem 2D em uma sequência 1D é usar uma Curva de Hilbert.

Há muitas versões dessa curva, dependendo do número de iterações usadas durante o cálculo. Abaixo segue o exemplo das curvas de Hilbert da primeira ordem à quinta ordem.

insira a descrição da imagem aqui

A maneira de calcular essa curva é a seguinte. Primeiro, definimos a Curva de Hilbert de primeira ordem como a mostrada na figura (a de n = 1), para que ela caiba em um quadrado 1x1. Nós, então, fazemos quatro cópias dessa curva, espaçando-as em um quadrado 4x4, para que todas apresentem a "concavidade" no lado esquerdo. Em seguida, invertemos as duas curvas 1 da ordem mais à esquerda, para que a concavidade superior fique voltada para o topo, enquanto a inferior fica voltada para o fundo. Finalmente, conectamos os cantos das curvas Hilbert adjacentes. Se desejar obter uma curva de ordem (n + 1), basta repetir o processo com quatro curvas de ordem n. Podemos ver uma visualização do processo aqui (também adicionarei uma imagem detalhando o processo em breve)

Sua tarefa neste desafio é desenrolar uma matriz de números inteiros ao longo da ordem mais baixa de Hilbert Curve para essa matriz.

Por uma questão de simplicidade, teremos a curva começando no canto superior esquerdo da matriz.

Você pode receber a entrada como uma lista da lista de números inteiros, onde cada sub-lista representa uma linha da matriz.

Você pode assumir que a entrada será uma matriz quadrada (n * n).

Por exemplo:

Entrada:

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

Resultado:

[ 1, 2, 4, 3 ]

Como estamos usando a primeira curva de Hilbert mostrada na figura

Entrada:

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

Resultado:

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

Usando a segunda ordem Hilbert Curve

Como sempre, brechas padrão não são permitidas.

Isso é código-golfe, então a resposta mais curta em byte vence.



@StewieGriffin com certeza, eu estou nele
— WizardOfMenlo

11
@StewieGriffin eu adicionei um resumo breve, eu vou fazer um trabalho mais aprofundado na próxima hora ou assim, depois das aulas de acabamento
— WizardOfMenlo

As necessidades de matriz para não só ser um quadrado, ele também precisa de n ser uma potência de 2.
— mbomb007

Respostas:


5

MATL , 86 85 bytes

Esta solução é baseada na entrada File Exchange de Jonas Lundgren, que utiliza números complexos para gerar a curva de Hilbert. Esses números complexos são então convertidos em valores de índice para recuperar os elementos da matriz que caem ao longo da curva.

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

Experimente online!

Explicação

%--- 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

@ DonMuesli Estou trabalhando em uma maneira melhor de lidar com isso. Está definitivamente longe de ser elegante! Obrigado pelas indicações. Atualizada!
— Suever 30/03/16

Eu não olhei para esse desafio específico. Às vezes pranchetas não pode ser evitada
— Luis Mendo

5

APL (Dyalog Unicode) , 41 bytes SBCS

Economizou 30 bytes (!) Consultando a sabedoria do APL Orchard, especialmente @ngn e @ Sherlock9.

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

Experimente online!

Explicação da seguinte maneira:

{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),(2,¯2)],[(¯2,2),(¯2,¯2)]]
                        ↑∘⍵¨              ⍝ 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.

Mais detalhes em " varredura de transposição monádica ".

Documentação Dyalog sobre proteções contra erros .


3

Mathcad, 302 bytes

O programa Mathcad abaixo é baseado no programa @ Sherlock9 Python. Difere curvando matrizes retangulares ignorando as partes da Curva de Hilbert que ficam fora dos limites da matriz. Observe que, como o Mathcad possui um manuseio de string relativamente ruim, mapeei os símbolos Lindenmayer para números inteiros na função Hilbert.

insira a descrição da imagem aqui

O Mathcad funciona através de uma interface 2D que permite ao usuário inserir (e misturar livremente) expressões matemáticas, gráficos, texto, entradas e saídas. Equacionei um byte à operação equivalente no teclado mínimo do usuário para criar um símbolo (por exemplo, o operador de definição (: =) é inserido digitando simplesmente:.


3

Python 3, 327 289 275 271 239 234 bytes

Esta é uma solução que modifiquei da minha resposta para outra pergunta da curva de Hilbert aqui . Todas as dicas de golfe são apreciadas.

Editar: Alterado como gé incrementado e decrementado. Agora usando eval()e str.translate. Não está mais usando 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

2

Wolfram - 233

Com base na representação como sistema Lindenmayer :

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

Você poderia postar algumas capturas de tela dele funcionando para usuários que não possuem o Mathematica?
— precisa

2
"Wolfram" é diferente do Mathematica? Caso contrário, deve ser chamado de Mathematica.
— mbomb007

@WizardOfMenlo Aqui está trabalhando online
— swish

@swish Acho que você precisa alterar a permissão do aplicativo Web, que parece estar bloqueado
— WizardOfMenlo

@ mbomb007 Wolfram é o nome da linguagem , Mathematica é como um IDE.
— swish

1

Ruby, 224 221 216 bytes

Esta resposta é baseada em minha resposta 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

1

CJam, 60

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

Experimente online

Explicação:

Estou construindo o fractal como uma série de direções de movimento: 0 = direita, 1 = abaixo, 2 = esquerda, 3 = acima.

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.