Как заполнить 2D-массив по диагонали на основе координат
Я создаю интерфейс прямоугольного массива, похожий на тепловую карту, и я хочу, чтобы "горячее" местоположение было в левом верхнем углу массива, а "холодное" -в правом нижнем углу. Поэтому мне нужно, чтобы массив заполнялся по диагонали следующим образом:
0 1 2 3
|----|----|----|----|
0 | 0 | 2 | 5 | 8 |
|----|----|----|----|
1 | 1 | 4 | 7 | 10 |
|----|----|----|----|
2 | 3 | 6 | 9 | 11 |
|----|----|----|----|
Так что на самом деле мне нужна функция f(x,y) такая, что
f(0,0) = 0
f(2,1) = 7
f(1,2) = 6
f(3,2) = 11
(или, конечно, аналогичная функция f(n), где f(7) = 10, f (9) = 6 и т. д.).
наконец, Да, я знаю, что этот вопрос похож на те, спросил здесь, здесь и здесь, но решения, описанные там, только пересекают и не заполняют матрицу.
5 ответов
интересная проблема, если вы ограничены, чтобы пройти через массив строку за строкой. Я разделил прямоугольник на три части. The верхний левый треугольник на нижний правый треугольник и ромбоид в середине.
на верхний левый треугольник значения в первом столбце (x=0) можно вычислить с помощью общего арифметического ряда 1 + 2 + 3 + .. + n = n*(n+1)/2
. Поля в этом треугольнике с одинаковым значением x+y находятся в одном и том же диагональ и есть значение этой суммы из первого colum + x.
тот же подход работает для нижний правый треугольник. Но вместо x
и y
, w-x
и h-y
, где w
- ширина и h
высота прямоугольника. Это значение должно быть вычтено из самого высокого значения w*h-1
в массиве.
есть два случая для ромбоид в середине. Если ширина прямоугольника больше чем (или равным) высоте, то нижнее левое поле прямоугольника является полем с наименьшим значением в ромбоиде и может быть вычислено эта сумма от ранее для h-1
. Оттуда вы можете представить, что ромбоид представляет собой прямоугольник с x-значением x+y
и y-значение y
из исходного прямоугольника. Итак, вычисления оставшихся значений в that новый прямоугольник легко.
В другом случае, когда высота больше ширины, то поле x=w-1
и y=0
можно вычислить, используя эту арифметическую сумму, а ромбоид можно представить как прямоугольник с x-значением x
а y-значение y-(w-x-1)
.
код можно оптимизировать, например, путем предварительного расчета значений. Я думаю, что есть также одна формула для всех случаев. Может быть, я подумаю об этом позже.
inline static int diagonalvalue(int x, int y, int w, int h) {
if (h > x+y+1 && w > x+y+1) {
// top/left triangle
return ((x+y)*(x+y+1)/2) + x;
} else if (y+x >= h && y+x >= w) {
// bottom/right triangle
return w*h - (((w-x-1)+(h-y-1))*((w-x-1)+(h-y-1)+1)/2) - (w-x-1) - 1;
}
// rhomboid in the middle
if (w >= h) {
return (h*(h+1)/2) + ((x+y+1)-h)*h - y - 1;
}
return (w*(w+1)/2) + ((x+y)-w)*w + x;
}
for (y=0; y<h; y++) {
for (x=0; x<w; x++) {
array[x][y] = diagonalvalue(x,y,w,h);
}
}
конечно, если нет такого ограничения, что-то вроде этого должно быть намного быстрее:
n = w*h;
x = 0;
y = 0;
for (i=0; i<n; i++) {
array[x][y] = i;
if (y <= 0 || x+1 >= w) {
y = x+y+1;
if (y >= h) {
x = (y-h)+1;
y -= x;
} else {
x = 0;
}
} else {
x++;
y--;
}
}
Как насчет этого (имея NxN
матрица):
count = 1;
for( int k = 0; k < 2*N-1; ++k ) {
int max_i = std::min(k,N-1);
int min_i = std::max(0,k-N+1);
for( int i = max_i, j = min_i; i >= min_i; --i, ++j ) {
M.at(i).at(j) = count++;
}
}
выполните шаги в 3-м примере - это дает индексы (для того, чтобы распечатать срезы) - и просто установите значение с увеличивающимся счетчиком:
int x[3][3];
int n = 3;
int pos = 1;
for (int slice = 0; slice < 2 * n - 1; ++slice) {
int z = slice < n ? 0 : slice - n + 1;
for (int j = z; j <= slice - z; ++j)
x[j][slice - j] = pos++;
}
в матрице M*N значения при прохождении, как в вашем примере, увеличиваются на n, за исключением граничных случаев, поэтому
f(0,0)=0
f(1,0)=f(0,0)+2
f(2,0)=f(1,0)+3
...и так далее до f (N,0). Тогда
f(0,1)=1
f(0,2)=3
а то
f(m,n)=f(m-1,n)+N, where m,n are index variables
и
f(M,N)=f(M-1,N)+2, where M,N are the last indexes of the matrix
это не доказано, но это должно дать вам что-то работать. Обратите внимание, что вам нужно только значение предыдущего элемента в каждой строке и несколько начальных значений для начала.
Если вам нужна простая функция, вы можете использовать рекурсивное определение.
H = height
def get_point(x,y)
if x == 0
if y == 0
return 0
else
return get_point(y-1,0)+1
end
else
return get_point(x-1,y) + H
end
end
это использует тот факт, что любое значение H+значение элемента слева от него. Если элемент уже находится в крайнем левом столбце, то вы найдете ячейку, которая находится в его дальней верхней правой диагонали, и переместитесь влево оттуда, и добавьте 1.
Это хороший шанс использовать динамическое программирование, и " кэш " или memoize функции, которые вы уже завершенный.
если вы хотите, чтобы что-то" строго " было сделано f(n), вы можете использовать отношение:
n = ( n % W , n / H ) [integer division, with no remainder/decimal]
и работайте свою функцию оттуда.
если вы хотите чисто array-populating-by-rows метод, без рекурсии, вы можете следовать этим правилам:
- если вы находитесь в первой ячейке строки, "запомните" элемент в ячейке (R-1) (где R-ваша текущая строка) первой строка и добавьте к ней 1.
- в противном случае просто добавьте H в ячейку, которую вы последний раз вычисляли (т. е. ячейку слева).
Psuedo-Code: (предполагая, что массив индексируется arr[row,column]
)
arr[0,0] = 0
for R from 0 to H
if R > 0
arr[R,0] = arr[0,R-1] + 1
end
for C from 1 to W
arr[R,C] = arr[R,C-1]
end
end