У меня возникли проблемы с переносом головы вокруг этого преобразования. Я хочу рекурсивно преобразовать 2D-матрицу NxN в свою версию z-order.
Например, заданный массив:
[ 1 2 ]
[ 3 4 ]
Z-порядок
[ 1 2 3 4]
Каковы рекурсивные шаги для преобразования z-порядка?
Рекурсивный способ прост:
В коде
#include <iostream>
template<typename M, typename CBACK>
void zorder(const M& m, int y0, int x0, int size,
CBACK cback)
{
if (size == 1) {
// Base case, just one cell
cback(m[y0][x0]);
} else {
// Recurse in Z-order
int h = size/2;
zorder(m, y0, x0, h, cback); // top-left
zorder(m, y0, x0+h, h, cback); // top-right
zorder(m, y0+h, x0, h, cback); // bottom-left
zorder(m, y0+h, x0+h, h, cback); // bottom-right
}
}
void print(int x) {
std::cout << x << " ";
}
int main(int argc, const char *argv[]) {
int x[][4] = {{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12},
{13, 14, 15, 16}};
zorder(x, 0, 0, 4, print);
std::cout << std::endl;
return 0;
}
Вывод этой программы
1 2 5 6 3 4 7 8 9 10 13 14 11 12 15 16
Обратите внимание, что существует и другой нерекурсивный подход: для посещения элементов в z-порядке вы можете просто перебирать счетчик и принимать нечетные биты как y и четные биты как x (подсчет бит от 0):
int zorder_x_of(int index) {
int x = 0;
for (int b=0,k=0; (1<<b) <= index; b+=2,k++) {
x += ((index & (1<<b)) != 0) << k;
}
return x;
}
int zorder_y_of(int index) {
return zorder_x_of(index>>1);
}
template<typename M, typename CBACK>
void zorder2(const M& m, int size, CBACK cback)
{
for (int i=0; i<size*size; i++) {
cback(m[zorder_y_of(i)][zorder_x_of(i)]);
}
}
Заметка:
В приведенных выше образцах кода я создал функцию, которая принимает "обратный вызов" (называемый cback
), который будет вызываться с элементами матрицы, по одному за раз в z-порядке.
Чтобы разрешить использование как в качестве матрицы, так и как функцию обратного вызова, которая поддерживает двойную []
индексацию и все, что можно назвать, я использовал шаблон C++.
В основной программе в качестве матрицы я использовал двумерный массив целых чисел и функцию, но код мог бы скомпилироваться, например, с помощью std::vector< std::vector< double > >
качестве матрицы и объекта экземпляр класса, предоставляющего operator()(double)
качестве обратного вызова.
2D-матрица ведет себя внутренне как 1D-массив, то есть уже находится в "Z-порядке".
Просто перебирайте указатель, указывающий на первый элемент до NxM
где N
- количество столбцов, а M
- количество строк.
пример:
int arr[2][2] = {{2,4},{3,5}};
for (int i=0; i<2 * 2; ++i){
std::cout << *(&arr[0][0] + i); // or *(arr + i)
}
cback
но, конечно, один SO-ответ не являетсяcback
местом для объяснения метапрограммирования шаблонов C ++. Если вы ничего не знаете об этом, я бы посоветовал немного изучить эту тему, потому что шаблоны могут быть мощным оружием в некоторых случаях. Имейте в виду, однако, что существует серьезный риск быть втянутым в фетишизм шаблонов, когда шаблон злоупотребляет далеко за пределами здравого смысла и где даже глупости, такие как boost.spirit, кажутся разумными инструментами.