Problème: carré magique

Problème: carré magique#

Un carré magique est une grille carrée contenant des entiers telle que la somme de chaque ligne, de chaque colonne et de chaque diagonale ait la même valeur. Un carré magique de \(n\) lignes est dit normal s’il contient chaque entier compris entre \(1\) et \(n^2\) exactement une fois. Par exemple, le tableau suivant est un carré magique normal:

\[\begin{split} \begin{array}{|c|c|c|} \hline 6 & 7 & 2\\\hline 1 & 5 & 9\\\hline 8 & 3 & 4\\\hline \end{array} \end{split}\]

Dans la suite de l’exercice, il s’agit d’écrire étape par étape les fonctions utiles pour tester si un tableau bidimensionnel est un carré magique normal. Vous pouvez utiliser les fonctions des étapes précédentes, même si vous ne les avez pas implantées. Pour alléger les notations, on définit un raccourci Grille pour les tableaux bidimensionnels d’entiers:

using Grille = vector<vector<int>>;

Dans les tests, on pourra supposer que l’on a à disposition la variable suivante:

Grille carreMagique = { { 6, 7, 2}, {1, 5, 9}, {8, 3, 4} };
Question 7

Définissez deux autres variables appelées carre et rectangle contenant des tableaux d’entiers à deux dimensions tels que :

  • carre soit une grille carrée 3x3 qui n’est pas un carré magique

  • rectangle soit un tableau deux dimensions qui n’est pas un carré (le nombre de colonne est différent du nombre de lignes)

/// BEGIN SOLUTION
Grille carre = { { 6, 7, 2}, {1, 6, 9}, {8, 3, 4} };
Grille rectangle = { { 6, 7}, {1, 6}, {8, 3} };
/// END SOLUTION

On considère la fonction estCarre documentée ci-dessous.

/** renvoie si un tableau à 2 dimensions est un carré
* @param g de type Grille
* @return true si toutes les lignes ont le même nombre de colonnes qui 
*         est égal au nombre de ligne, false sinon
**/
Question 8

Proposez quatre tests pour cette fonction (en utilisant CHECK) sur les trois variables carreMagique, carre, rectangle ainsi que sur la variable triangle définie ci-dessous :

Grille triangle = {{1,2,4},{3,5},{2}};
/// BEGIN SOLUTION
CHECK( estCarre(carreMagique) );
CHECK( estCarre(carre) );
CHECK( not estCarre(rectangle) );
CHECK( not estCarre(triangle) );
/// END SOLUTION

La définition ci-dessous de estCarre est erronée. Annotez les tests ci-dessus pour indiquer lesquels ne passent pas, puis annotez la définition ci-dessous pour indiquer comment la corriger.

bool estCarre(Grille g) {
    for ( int i = 0 ; i < g.size() ; i++ ) {
        if ( g[i].size() != g.size() ) {
            return false;
        } else {
            return true;
        }
    }
}
Question 9

Définissez une fonction sommeColonne qui prend en paramètres un entier \(c\) et une grille et qui renvoie la somme des éléments de la \(c\)-ième colonne de la grille. Par exemple, l’appel sur l’exemple sommeColonne(carreMagique, 1) renvoie \(7+5+3=15\). On supposera que le numéro de colonne est un indice valide.

Dans la suite, on supposera que l’on dispose aussi d’une fonction sommeLigne analogue.

/// BEGIN SOLUTION
int sommeColonne(Grille g, int c) {
    int somme = 0;
    for (int i = 0 ; i < g.size() ; i++)
        somme = somme + g[i][c];
    return somme;
}
/// END SOLUTION
Question 10

Spécifiez et définissez une fonction sommeDiagonaleMajeure qui prend en paramètre une grille carrée et renvoie la somme des éléments de la diagonale majeure de la grille (la diagonale majeure est celle qui commence en haut à gauche et termine en bas à droite; dans l’exemple ci-dessus, elle contient 6, 5 et 4).

/// BEGIN SOLUTION
/** renvoie la somme des éléments de la diagonale majeure d'une grille
 * @param g de type Grille
 * @return la somme des éléments de la diagonale majeure de g
 **/
int sommeDiagonaleMajeure(Grille g) {
    int somme = 0;
    for (int i = 0 ; i < g.size() ; i++)
        somme = somme + g[i][i];
    return somme;
}
/// END SOLUTION

Question 11

On suppose que l’on dispose d’une fonction sommeDiagonaleMineure qui prend en paramètre une grille carrée et renvoie la somme des éléments de la diagonale mineure de la grille (la diagonale mineure est celle qui commence en bas à gauche et termine en haut à droite; dans l’exemple ci-dessus, elle contient 8, 5 et 2). Écrivez un test pour la fonction sommeDiagonaleMineure (ne pas écrire la fonction, seulement le test).

/// BEGIN SOLUTION
CHECK( sommeDiagonaleMineure(carreMagique) == 15 );
/// END SOLUTION
Question 12

Définissez une fonction estCarreMagique qui prend en paramètre une grille, et renvoie true s’il s’agit d’un carré magique (pas forcément normal), false sinon comme dans les tests proposés.

bool estCarreMagique(Grille g) {
    /// BEGIN SOLUTION
    int somme = sommeLigne(g, 0);
    if ( not estCarre(g) )
        return false;
    for ( int i = 0 ; i < g.size() ; i++ )
        if ( somme != sommeLigne(g, i) or somme != sommeColonne(g, i) )
            return false;
    if ( somme != sommeDiagonaleMajeure(g) or somme != sommeDiagonaleMineure(g) )
        return false;
    return true;
    /// END SOLUTION
}
CHECK(     estCarreMagique(carreMagique) );
CHECK( not estCarreMagique(carre) );
CHECK( not estCarreMagique(rectangle) );
CHECK( not estCarreMagique(triangle) );

Question 13

\(\clubsuit\) On a défini la fonction histogramme suivante :

using Tableau = vector<int>;
Tableau histogramme(Grille g) {
    int n = g.size();
    vector<int> H = vector<int>(n*n);
    for ( int i = 0 ; i < n*n ; i++ )
        H[i] = 0;
    for ( int i = 0 ; i < n ; i++ )
        for ( int j = 0 ; j < g[i].size() ; j++ )
            if ( g[i][j] > 0 and g[i][j] <= n*n )
                H[g[i][j]-1] += 1;
    return H;
}

Donnez le résultat de la fonction sur l’exemple suivant ainsi que sur la grille carreMagique :

Grille exemple = {{3, 0, 3}, {2, 2, 2}, {1, 4, 1}};
/// BEGIN SOLUTION
CHECK( histogramme(exemple) == vector<int>({2, 3, 2, 1, 0, 0, 0, 0, 0});
CHECK( histogramme(carreMagique) == vector<int>({1, 1, 1, 1, 1, 1, 1, 1, 1});
/// END SOLUTION
Question 14

\(\clubsuit\) Définissez une fonction estCarreMagiqueNormal qui prend en paramètre une grille et renvoie true s’il s’agit d’un carré magique normal, false sinon.
Indication : on pourra utiliser la fonction précédente.

/// BEGIN SOLUTION
bool estCarreMagiqueNormal(Grille g) {
    vector <int> H;
    if (!estCarreMagique(g))
        return false;
    H = histogramme(g);
    for (int i = 0 ; i < H.size() ; i++)
        if (H[i]!=1) 
		    return false;
    return true;
}
/// END SOLUTION