Гильберт-кривизна матрицы


19

Вдохновленный этим вопросом

Другой способ развернуть двумерное изображение в одномерную строку - использовать кривую Гильберта.

Существует много версий этой кривой, в зависимости от количества итераций, используемых при ее вычислении. Ниже приведен пример Кривых Гильберта от первого до пятого порядка.

введите описание изображения здесь

Способ вычисления этой кривой заключается в следующем. Сначала мы определяем кривую Гильберта первого порядка, как показано на рисунке (для n = 1), чтобы она помещалась в квадрат 1x1. Затем мы делаем четыре копии этой кривой, располагая их в квадрате 4x4, чтобы они все представляли «вогнутость» в левой части. Затем мы переворачиваем две крайние левые кривые порядка 1, чтобы верхняя вогнутость была обращена к вершине, а нижняя - к нижней. Наконец, мы соединяем углы соседних кривых Гильберта. Если вы хотите получить кривую (n + 1) -порядка, нам просто нужно повторить процесс с четырьмя кривыми n-го порядка. Мы можем увидеть визуализацию процесса здесь (я также добавлю изображение, детализирующее процесс в ближайшее время)

Ваша задача в этой задаче - развернуть матрицу целых чисел вдоль кривой Гильберта самого низкого порядка для этой матрицы.

Для простоты у нас будет кривая, начинающаяся с верхнего левого угла матрицы.

Вы можете получить входные данные в виде списка целых чисел, где каждый подсписок представляет строку матрицы.

Вы можете предположить, что на входе будет квадратная матрица (n * n).

Например:

Входные данные:

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

Выход:

[ 1, 2, 4, 3 ]

Поскольку мы используем кривую Гильберта первого порядка, показанную на рисунке

Входные данные:

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

Выход:

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

Использование кривой Гильберта второго порядка

Как обычно, стандартные лазейки не допускаются.

Это код-гольф, поэтому выигрывает самый короткий ответ в байтах.



@StewieGriffin уверен, я нахожусь на этом
— WizardOfMenlo

1
@StewieGriffin Я добавил краткое резюме, я сделаю более тщательную работу в течение следующего часа или около того, после окончания уроков
— WizardOfMenlo

Матрица должна быть не только квадратной, но и n должна быть степенью 2.
— mbomb007

Ответы:


5

MATL , 86 85 байт

Это решение основано на записи обмена файлами Джонаса Лундгрена, которая использует комплексные числа для генерации кривой Гильберта. Эти комплексные числа затем преобразуются в значения индекса, чтобы извлечь элементы матрицы, которые попадают вдоль кривой.

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

Попробуйте онлайн!

объяснение

%--- 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 Я работаю над лучшим способом справиться с этим. Это определенно далеко не элегантно! Спасибо за указатели. Обновлено!
— Suever

Я не изучал этот конкретный вызов. Иногда нельзя избежать буфера обмена
— Луис Мендо

5

APL (Dyalog Unicode) , 41 байт SBCS

Сохранено 30 байтов (!) Благодаря советам мудрости APL Orchard, особенно @ngn и @ Sherlock9.

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

Попробуйте онлайн!

Объяснение следующим образом:

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

Подробнее о " монаданных транспонировании ".

Дьялог документация на охрану ошибок .


3

Mathcad, 302 байта

Приведенная ниже программа Mathcad основана на программе Python @ Sherlock9. Он отличается тем, что изгибает прямоугольные матрицы, игнорируя те части кривой Гильберта, которые лежат за пределами границ матрицы. Обратите внимание, что поскольку Mathcad имеет относительно плохую обработку строк, я сопоставил символы Lindenmayer с целыми числами в функции Гильберта.

введите описание изображения здесь

Mathcad работает через 2D-интерфейс, который позволяет пользователю размещать (и свободно смешивать) математические выражения, графики, текст, входные и выходные данные. Я приравнял байт к минимальной эквивалентной операции клавиатуры пользователя для создания символа (например, оператор определения (: =) вводится простым вводом:.


3

Python 3, 327 289 275 271 239 234 байта

Это решение я изменил из своего ответа на другой вопрос о кривой Гильберта здесь . Любые советы по гольфу приветствуются.

Изменить: Изменено, как gувеличивается и уменьшается. Теперь с помощью eval()и str.translate. Больше не использую 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

Вольфрам - 233

Основываясь на представлении в системе 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}}]]

Не могли бы вы опубликовать несколько скриншотов, чтобы он работал, для пользователей, у которых нет Mathematica?
— WizardOfMenlo

2
"Wolfram" отличается от Mathematica? Если нет, его следует назвать Mathematica.
— mbomb007

@WizardOfMenlo Здесь это работает онлайн
— swish

@swish Думаю, вам нужно изменить разрешение веб-приложения, оно, похоже, заблокировано
— WizardOfMenlo

@ mbomb007 Wolfram - это название языка , Mathematica похожа на IDE.
— swish

1

Рубин, 224 221 216 байт

Этот ответ основан на моем ответе 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

Попробуйте онлайн

Объяснение:

Я строю фрактал как последовательность направлений движения: 0 = вправо, 1 = вниз, 2 = влево, 3 = вверх.

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.