it-roy-ru.com

Матрица перемещения в диагональных полосах

Я думал, что у этой проблемы есть тривиальное решение, пара циклов for и несколько причудливых счетчиков, но, видимо, это несколько сложнее.

Итак, мой вопрос, как бы вы написали (в C) функцию обхода квадратной матрицы в диагональных полосах.

Пример:

1  2  3
4  5  6
7  8  9

Должен быть пройден в следующем порядке:

[1],[2,4],[3,5,7],[6,8],[9]

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

30
alyx

Вот то, что вы можете использовать. Просто замените printfs тем, что вы действительно хотите сделать. 

#include <stdio.h>

int main()
{
    int x[3][3] = {1, 2, 3,
                   4, 5, 6,
                   7, 8, 9};
    int n = 3;
    for (int slice = 0; slice < 2 * n - 1; ++slice) {
        printf("Slice %d: ", slice);
        int z = (slice < n) ? 0 : slice - n + 1;
        for (int j = z; j <= slice - z; ++j) {
            printf("%d ", x[j][slice - j]);
        }
        printf("\n");
    }
    return 0;
}

Результат:

Slice 0: 1
Slice 1: 2 4
Slice 2: 3 5 7
Slice 3: 6 8
Slice 4: 9
61
Mark Byers

Я бы сдвинул строки так:

1  2  3  x  x
x  4  5  6  x
x  x  7  8  9

И просто итерировать столбцы. Это действительно может быть сделано без физического смещения.

39
Kugel

Давайте посмотрим, как индексируются матричные элементы. 

(0,0)   (0,1)   (0,2)   (0,3)   (0,4)  
(1,0)   (1,1)   (1,2)   (1,3)   (1,4)  
(2,0)   (2,1)   (2,2)   (2,3)   (2,4)  

Теперь давайте посмотрим на полосы: 

Stripe 1: (0,0)
Stripe 2: (0,1)    (1,0)  
Stripe 3: (0,2)    (1,1)    (2,0)
Stripe 4: (0,3)    (1,2)    (2,1)
Stripe 5: (0,4)    (1,3)    (2,2)
Stripe 6: (1,4)    (2,3)
Stripe 7: (2,4)

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

public static void printSecondaryDiagonalOrder(int[][] matrix) {
    int rows = matrix.length;
    int cols = matrix[0].length;
    int maxSum = rows + cols - 2;

    for (int sum = 0; sum <= maxSum; sum++) {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (i + j - sum == 0) {
                    System.out.print(matrix[i][j] + "\t");
                }
            }
        }
        System.out.println();
    }
}

Это не самый быстрый алгоритм (он выполняет операции (строки * столбцы * (строки + столбцы-2))), но логика, лежащая в его основе, довольно проста.

20
ioreskovic

Я нашел это здесь: Поперечная прямоугольная матрица в диагональных полосах

#include <stdio.h>

int main()
{
    int x[3][4] = { 1,  2,  3,  4,
                    5,  6,  7,  8,
                    9, 10, 11, 12};
    int m = 3;
    int n = 4;
    for (int slice = 0; slice < m + n - 1; ++slice) {
        printf("Slice %d: ", slice);
        int z1 = slice < n ? 0 : slice - n + 1;
        int z2 = slice < m ? 0 : slice - m + 1;
        for (int j = slice - z2; j >= z1; --j) {
                printf("%d ", x[j][slice - j]);
        }
        printf("\n");
    }
    return 0;
}

Результат:

Slice 0: 1
Slice 1: 5 2
Slice 2: 9 6 3
Slice 3: 10 7 4
Slice 4: 11 8
Slice 5: 12

Я обнаружил, что это довольно элегантный способ сделать это, так как ему требуется только память для 2 дополнительных переменных (z1 и z2), которые в основном содержат информацию о длине каждого среза. Внешний цикл перемещается по номерам срезов (slice), а внутренний цикл затем перемещается по каждому срезу с индексом: slice - z1 - z2. Вся остальная информация, которая вам понадобится, - где начинается алгоритм и как он проходит через матрицу. В предыдущем примере он сначала сместится вниз по матрице, а после того, как достигнет дна, сместится вправо: (0,0) -> (1,0) -> (2,0) -> (2,1) - > (2,2) -> (2,3). Снова этот паттерн фиксируется вариабелями z1 и z2. Строка увеличивается вместе с числом slice до тех пор, пока он не достигнет дна, затем z2 начнет увеличиваться, что можно использовать для поддержания постоянного индекса строки в его положении: slice - z2. Длина каждого среза известна как: slice - z1 - z2, выполняя следующее: (slice - z2) - (slice - z1 -z2) (минус как алгоритм движется в порядке возрастания m--, n ++) приводит к z1, который является критерием остановки для внутреннего цикла. Остается только индекс столбца, который удобно наследовать от того факта, что j является постоянным после того, как он достигает дна, после чего индекс столбца начинает увеличиваться.

Предыдущий алгоритм перемещается только в порядке возрастания слева направо, начиная сверху слева (0,0). Когда мне понадобился этот алгоритм, мне также нужно было искать в матрице в порядке убывания, начиная с нижнего левого угла (m, n). Поскольку алгоритм меня поразил, я решил докопаться и адаптировать его:

  • длина среза снова известна: slice -z1 - z2
  • Начальная позиция срезов: (2,0) -> (1,0) -> (0,0) -> (0,1) -> (0,2) -> (0,3)
  • Движение каждого среза - m ++ и n ++

Я нашел это весьма полезным, чтобы изобразить это следующим образом:

  • slice = 0 z1 = 0 z2 = 0 (2,0) (индекс столбца = индекс строки - 2)
  • slice = 1 z1 = 0 z2 = 0 (1,0) (2,1) (индекс столбца = rowindex - 1)
  • slice = 2 z1 = 0 z2 = 0 (0,0) (1,1) (2,2) (индекс столбца = rowindex + 0)
  • slice = 3 z1 = 0 z2 = 1 (0,1) (1,2) (2,3) (индекс столбца = rowindex + 1)
  • slice = 4 z1 = 1 z2 = 2 (0,2) (1,3) (индекс столбца = rowindex + 2)
  • slice = 5 z1 = 2 z2 = 3 (0,3) (индекс столбца = rowindex + 3)

Получив следующее: j = (m-1) - slice + z2 (с j ++) Используя выражение длины среза, чтобы сделать критерий остановки: ((m-1) - slice + z2)+(slice -z2 - z1) приводит к: (m-1) - z1 Теперь у нас есть аргументы для innerloop: for (int j = (m-1) - slice + z2; j < (m-1) - z1; j++)

Индекс строки известен по j, и снова мы знаем, что индекс столбца начинает увеличиваться только тогда, когда j начинает быть постоянным, и, таким образом, наличие j в выражении снова - неплохая идея. Из различий между вышеупомянутым суммированием я заметил, что разница всегда равна j - (slice - m +1), проверяя это для некоторых других случаев, я был уверен, что это будет справедливо для всех случаев (я не математик; P) и, таким образом, алгоритм для убывания Движение, начинающееся снизу слева, выглядит следующим образом:

#include <stdio.h>

int main()
{
    int x[3][4] = { 1,  2,  3,  4,
                    5,  6,  7,  8,
                    9, 10, 11, 12};
    int m = 3;
    int n = 4;
    for (int slice = 0; slice < m + n - 1; ++slice) {
        printf("Slice %d: ", slice);
        int z1 = slice < n ? 0 : slice - n + 1;
        int z2 = slice < m ? 0 : slice - m + 1;
        for (int j = (m-1) - slice + z2; j <= (m-1) - z1; j++) {
                printf("%d ", x[j][j+(slice-m+1)]);
        }
        printf("\n");
    }
    return 0;
}

Теперь я оставляю два других направления на ваше усмотрение ^^ (что важно только тогда, когда порядок действительно важен).

Этот алгоритм - довольно сложная задача, даже если вы думаете, что знаете, как он работает, он все равно может укусить вас за задницу. Однако я думаю, что это довольно красиво, потому что буквально движется по матрице, как и следовало ожидать. Меня интересует, знает ли кто-нибудь больше об алгоритме, например, об имени, поэтому я могу посмотреть, имеет ли смысл то, что я здесь сделал, и, возможно, есть лучшие решения.

4
Sjonnie

Я думаю, что это может быть решением для любого типа матрицы.

#include <stdio.h>

#define M 3
#define N 4

main(){
         int a[M][N] = {{1, 2, 3, 4}, 
                        {5, 6, 7, 8}, 
                        {9,10,11,12}};

         int i, j, t;
         for( t = 0; t<M+N; ++t)
              for( i=t, j=0; i>=0 ; --i, ++j)
                     if( (i<M) && (j<N) )
                             printf("%d ", a[i][j]);
         return 0;
}
4
tech.samar

Я думал, что у этой проблемы есть тривиальное решение, пара циклов for и несколько необычных счетчиков.

Точно.

Важно отметить, что если вы даете каждому элементу индекс ( i , j ), то элементы с одинаковой диагональю имеют одинаковое значение j + n - i , где n - ширина вашей матрицы. Поэтому, если вы перебираете матрицу обычным способом (то есть вложенные циклы над i и j ), то вы можете отслеживать диагонали в массиве, который адресован вышеупомянутым способом.

3
Konrad Rudolph

// Этот алгоритм работает для матриц всех размеров. ;)

    int x = 0;
    int y = 0;        
    int sub_x;
    int sub_y;

    while (true) {

        sub_x = x;
        sub_y = y;

        while (sub_x >= 0 && sub_y < y_axis.size()) {

            this.print(sub_x, sub_y);
            sub_x--;
            sub_y++;

        }

        if (x < x_axis.size() - 1) {

            x++;

        } else if (y < y_axis.size() - 1) {

            y++;

        } else {

            break;

        }

    }
2
Johnyk11

Ключ состоит в том, чтобы перебрать каждый элемент в первом ряду, и от него пойти вниз по диагонали. Затем выполните итерацию каждого элемента в последнем столбце (без первого, через который мы прошли в предыдущем шаге), а затем уменьшите его диагональ.

Вот исходный код, который предполагает, что матрица является квадратной матрицей (непроверенная, переведенная из рабочего кода Python):

#define N 10
void diag_step(int[][] matrix) {
    for (int i = 0; i < N; i++) {
        int j = 0;
        int k = i;
        printf("starting a strip\n");
        while (j < N && i >= 0) {
            printf("%d ", matrix[j][k]);
            k--;
            j++;
        }
        printf("\n");
    }

    for (int i = 1; i < N; i++) {
        int j = N-1;
        int k = i;
        printf("starting a strip\n");
        while (j >= 0 && k < N) {
            printf("%d ", matrix[k][j]);
            k++;
            j--;
        }
        printf("\n");
    }   
}   
1
abyx

Псевдокод:

N = 2 // or whatever the size of the [square] matrix
for x = 0 to N
  strip = []
  y = 0
  repeat
     strip.add(Matrix(x,y))
     x -= 1
     y -= 1
  until x < 0
  // here to print the strip or do some' with it

// And yes, Oops, I had missed it... 
// the 2nd half of the matrix...
for y = 1 to N    // Yes, start at 1 not 0, since main diagonal is done.
   strip = []
   x = N
   repeat
      strip.add(Matrix(x,y))
      x -= 1
      y += 1
   until x < 0
  // here to print the strip or do some' with it

(Предполагается, что x индексирует строки, y индексирует столбцы, поменяйте их местами, если матрица проиндексирована наоборот)

1
mjv

На всякий случай, если кому-то нужно сделать это в python, очень просто использовать numpy:

#M is a square numpy array    
for i in range(-M.shape[0]+1, M.shape[0]):
    print M.diagonal(offset=i)
1
Oriol Nieto
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int main() 
{
    int N = 0;
    cin >> N;

    vector<vector<int>> m(N, vector<int>(N, 0));

    for (int i = 0; i < N; ++i)
    {
        for (int j = 0; j < N; ++j)
        {
            cin >> m[i][j];
        }
    }

    for (int i = 1; i < N << 1; ++i)
    {
        for (int j = 0; j < i; ++j)
        {
            if (j < N && i - j - 1 < N)
            {                          
               cout << m[j][i - j - 1];
            }
        }
        cout << endl;
    }
    return 0;
}
0
user5380950

вы должны разбить матрицу на верхнюю и нижнюю части и перебрать каждую из них по отдельности, сначала одну половину строки, а другой столбец сначала…. Давайте предположим, что матрица имеет размер n * n и хранится в векторе, сначала строка, нулевое основание, петли являются исключительными для последнего элемента.

for i in 0:n
    for j in 0:i +1
        A[i + j*(n-2)]

the other half can be done in a similar way, starting with:
for j in 1:n
    for i in 0:n-j
        ... each step is i*(n-2) ...
0
Anycorn

Гораздо более простая реализация:

//Assuming arr as ur array and numRows and numCols as what they say.
int arr[numRows][numCols];
for(int i=0;i<numCols;i++) {
    printf("Slice %d:",i);
    for(int j=0,k=i; j<numRows && k>=0; j++,k--)
    printf("%d\t",arr[j][k]);
}
0
SinnerShanky

Я, вероятно, сделал бы что-то вроде этого (заранее извиняюсь за любые ошибки индекса, не отладил это):

// Operation to be performed on each slice:
void doSomething(const int lengthOfSlice,
                 elementType *slice,
                 const int stride) {
    for (int i=0; i<lengthOfSlice; ++i) {
        elementType element = slice[i*stride];
        // Operate on element ...
    }
}

void operateOnSlices(const int n, elementType *A) {
    // distance between consecutive elements of a slice in memory:
    const int stride = n - 1;

    // Operate on slices that begin with entries in the top row of the matrix
    for (int column = 0; column < n; ++column)
        doSomething(column + 1, &A[column], stride);

    // Operate on slices that begin with entries in the right column of the matrix
    for (int row = 1; row < n; ++row)
        doSomething(n - row, &A[n*row + (n-1)], stride);
}
0
Stephen Canon
static int[][] arr = {{ 1, 2, 3, 4},
                      { 5, 6, 7, 8},
                      { 9,10,11,12},
                      {13,14,15,16} };

public static void main(String[] args) {
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < i+1; j++) {
            System.out.print(arr[j][i-j]);
            System.out.print(",");
        }
        System.out.println();
    }

    for (int i = 1; i < arr.length; i++) {
        for (int j = 0; j < arr.length-i; j++) {
            System.out.print(arr[i+j][arr.length-j-1]);
            System.out.print(",");
        }
        System.out.println();
    }
}
0
engin
public void printMatrix(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length;
    for (int i = 0; i < m + n - 1; i++) {
         int start_row = i < m ? i : m - 1;
         int start_col = i < m ? 0 : i - m + 1;
         while (start_row >= 0 && start_col < n) {
               System.out.print(matrix[start_row--][start_col++]);
         }
         System.out.println("\n")
     }
}
0
TankZhuang