Je pensais que ce problème avait une solution triviale, quelques boucles for et quelques compteurs sophistiqués, mais apparemment, il est un peu plus compliqué.
Ma question est donc la suivante: comment écririez-vous (en C) une fonction traversante d’une matrice carrée en bandes diagonales.
Exemple:
1 2 3
4 5 6
7 8 9
Devrait être traversé dans l'ordre suivant:
[1],[2,4],[3,5,7],[6,8],[9]
Chaque bande ci-dessus est entourée de crochets .. Une des conditions est de pouvoir distinguer les bandes. Ce qui signifie que vous savez quand vous commencez une nouvelle bande. Ceci parce qu'il y a une autre fonction que je dois appeler pour chaque élément d'une bande, puis avant le début d'une nouvelle bande. Ainsi, une solution sans duplication de code est idéale.
Voici quelque chose que vous pouvez utiliser. Il suffit de remplacer les printfs par ce que vous voulez réellement faire.
#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;
}
Sortie:
Slice 0: 1
Slice 1: 2 4
Slice 2: 3 5 7
Slice 3: 6 8
Slice 4: 9
Je décalerais les lignes comme suit:
1 2 3 x x
x 4 5 6 x
x x 7 8 9
Et juste itérer les colonnes. Cela peut effectivement être fait sans déplacement physique.
Voyons comment les éléments de la matrice sont indexés.
(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)
Voyons maintenant les rayures:
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)
Si vous regardez de plus près, vous remarquerez une chose. La somme des indices de chaque élément de la matrice dans chaque bande est constante. Alors, voici le code qui fait cela.
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();
}
}
Ce n’est pas l’algorithme le plus rapide (opérations (lignes * cols * (lignes + colonnes 2))), mais la logique qui sous-tend cet algorithme est assez simple.
J'ai trouvé ceci ici: Matrice Traverse Rectangulaire en Bandes Diagonales
#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;
}
sortie:
Slice 0: 1
Slice 1: 5 2
Slice 2: 9 6 3
Slice 3: 10 7 4
Slice 4: 11 8
Slice 5: 12
J'ai trouvé cela une manière assez élégante de le faire car il ne nécessite que de la mémoire pour 2 variables supplémentaires (z1 et z2), qui contiennent essentiellement les informations sur la longueur de chaque tranche. La boucle externe parcourt les numéros de tranche (slice
) et la boucle interne parcourt ensuite chaque tranche avec l'index: slice - z1 - z2
. Toutes les autres informations dont vous avez besoin, puis de l'endroit où l'algorithme commence et de la façon dont il se déplace dans la matrice. Dans l'exemple précédent, la matrice sera déplacée en premier, puis à droite, après avoir atteint le bas: (0,0) -> (1,0) -> (2,0) -> (2,1) - > (2,2) -> (2,3). Encore une fois, ce modèle est capturé par les varibales z1 et z2. La ligne s'incrémente avec le nombre slice
jusqu'à ce qu'elle atteigne le bas, puis z2
commence à s'incrémenter, ce qui peut être utilisé pour maintenir l'index de ligne constant à sa position: slice - z2
. La longueur de chaque tranche est connue par: slice - z1 - z2
, en procédant comme suit: (slice - z2) - (slice - z1 -z2)
(moins lorsque l'algorithme se déplace dans l'ordre croissant m--, n ++) donne z1
, qui est le critère d'arrêt de la boucle interne. Il ne reste que l'indice de colonne, ce qui est commodément hérité du fait que j est constant après avoir atteint le bas, après quoi l'indice de colonne commence à s'incrémenter.
L'algorithme précédent ne se déplace que dans l'ordre croissant de gauche à droite en commençant par le haut à gauche (0,0). Lorsque j’avais besoin de cet algorithme, j’ai également eu besoin de chercher dans une matrice par ordre décroissant en commençant en bas à gauche (m, n). Parce que j'étais assez conquis par l'algorithme, j'ai décidé d'aller au fond des choses et de l'adapter:
slice -z1 - z2
J'ai trouvé assez utile de le décrire comme suit:
Dérivant ce qui suit: j = (m-1) - slice + z2
(avec j ++) En utilisant l'expression de la longueur de la tranche pour transformer le critère d'arrêt: ((m-1) - slice + z2)+(slice -z2 - z1)
résultats en: (m-1) - z1
Nous avons maintenant les arguments pour la boucle intérieure: for (int j = (m-1) - slice + z2; j < (m-1) - z1; j++)
L'index de ligne est connu de j, et nous savons à nouveau que l'index de colonne ne commence à s'incrémenter que lorsque j commence à être constant, et donc avoir à nouveau j dans l'expression n'est pas une mauvaise idée. D'après les différences entre la somme ci-dessus, j'ai constaté que la différence est toujours égale à j - (slice - m +1)
. En testant cela dans d'autres cas, j'étais persuadé que cela resterait valable pour tous les cas (je ne suis pas un mathématicien; P) et donc par Le mouvement en partant du bas à gauche se présente comme suit:
#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;
}
Maintenant, je vous laisse les deux autres directions ^^ (ce qui n’est important que lorsque l’ordre est réellement important).
Cet algorithme est tout à fait étonnant, même si vous pensez savoir comment cela fonctionne, il peut vous mordre à la gorge. Cependant, je pense que c’est très beau parce qu’il se déplace littéralement dans la matrice comme on peut s’y attendre. Je suis intéressé si quelqu'un en sait plus sur l'algorithme, un nom par exemple, afin que je puisse vérifier si ce que j'ai fait ici a réellement du sens et s'il existe peut-être de meilleures solutions.
Je pense que cela peut être une solution pour tout type de matrice.
#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;
}
Je pensais que ce problème avait une solution triviale, quelques boucles for et quelques compteurs sophistiqués
Précisément.
La chose importante à noter est que si vous attribuez à chaque élément un index ( i , j ), les éléments de même diagonale ont la même valeur j + n - i , où n est la largeur de votre matrice. Donc, si vous parcourez la matrice de la manière habituelle (c'est-à-dire des boucles imbriquées sur i et j ), vous pouvez suivre les diagonales dans un tableau traité de la manière susmentionnée.
// Cet algorithme fonctionne pour les matrices de toutes tailles. ;)
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;
}
}
L'essentiel est d'itérer chaque élément de la première ligne, puis de descendre de la diagonale. Ensuite, parcourez chaque élément de la dernière colonne (sans le premier, que nous avons décrit précédemment), puis réduisez sa diagonale.
Voici le code source qui suppose que la matrice est une matrice carrée (non testée, traduite du code python actif):
#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");
}
}
Pseudo code:
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
(Suppose que x indexes lignes, y indexes colonnes, inversez ces deux si la matrice est indexée dans l’inverse)
Juste au cas où quelqu'un aurait besoin de faire cela en python, c'est très facile avec numpy
#M is a square numpy array
for i in range(-M.shape[0]+1, M.shape[0]):
print M.diagonal(offset=i)
#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;
}
vous devez diviser la matrice en parties supérieure et inférieure et itérer chacune d'elles séparément, une demi-ligne en premier, une autre colonne en premier . supposons que la matrice est n * n, stockée dans un vecteur, une ligne en premier, base zéro, les boucles sont exclusives du dernier élément.
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) ...
Une implémentation beaucoup plus simple:
//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]);
}
Je ferais probablement quelque chose comme ceci (excuses à l'avance pour toute erreur d'index, n'ai pas débogué ceci):
// 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);
}
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();
}
}
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")
}
}