Как заполнить 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 метод, без рекурсии, вы можете следовать этим правилам:

  1. если вы находитесь в первой ячейке строки, "запомните" элемент в ячейке (R-1) (где R-ваша текущая строка) первой строка и добавьте к ней 1.
  2. в противном случае просто добавьте 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