Multiplication de deux matrices en Java

La multiplication de matrices est un exercice classique qui illustre les tableaux à deux dimensions et les triples boucles imbriquées en Java. Voici l'algorithme de base, avec gestion des dimensions et une version optimisée.

Rappel mathématique

Pour multiplier une matrice A de dimensions m × n par une matrice B de dimensions n × p, il faut que le nombre de colonnes de A égale le nombre de lignes de B. Le résultat C est de dimensions m × p, avec :

C[i][j] = Σ A[i][k] × B[k][j]  pour k de 0 à n-1

Implémentation en Java

public class MatrixMath {

    public static int[][] multiply(int[][] a, int[][] b) {
        int m = a.length;
        int n = a[0].length;
        int p = b[0].length;

        if (n != b.length) {
            throw new IllegalArgumentException(
                "Dimensions incompatibles : " + n + " ≠ " + b.length);
        }

        int[][] c = new int[m][p];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < p; j++) {
                int somme = 0;
                for (int k = 0; k < n; k++) {
                    somme += a[i][k] * b[k][j];
                }
                c[i][j] = somme;
            }
        }
        return c;
    }

    public static void main(String[] args) {
        int[][] a = {
            {1, 2, 3},
            {4, 5, 6}
        }; // 2×3
        int[][] b = {
            {7,  8},
            {9,  10},
            {11, 12}
        }; // 3×2

        int[][] c = multiply(a, b);
        // c = {{58, 64}, {139, 154}}
        for (int[] ligne : c) {
            System.out.println(java.util.Arrays.toString(ligne));
        }
    }
}

Complexité

L'algorithme naïf est en O(m × n × p), soit O(n³) pour deux matrices carrées n×n. Sur une matrice 1000×1000, c'est un milliard d'opérations — quelques secondes sur un CPU moderne.

Petite optimisation : inverser l'ordre des boucles

L'ordre i → j → k cause de mauvais accès mémoire sur B (colonne par colonne). Inverser en i → k → j améliore significativement le cache :

for (int i = 0; i < m; i++) {
    for (int k = 0; k < n; k++) {
        int aik = a[i][k];
        for (int j = 0; j < p; j++) {
            c[i][j] += aik * b[k][j];
        }
    }
}

Sur des matrices de 512×512 et plus, cette version tourne 2 à 4 fois plus vite — simplement parce qu'elle respecte mieux la hiérarchie mémoire du CPU.

Version pour matrices de doubles

public static double[][] multiply(double[][] a, double[][] b) {
    int m = a.length, n = a[0].length, p = b[0].length;
    double[][] c = new double[m][p];
    for (int i = 0; i < m; i++) {
        for (int k = 0; k < n; k++) {
            double aik = a[i][k];
            for (int j = 0; j < p; j++) {
                c[i][j] += aik * b[k][j];
            }
        }
    }
    return c;
}

Pour aller plus loin

  • Algorithme de Strassen : O(n2.807), rentable sur des matrices ≥ 128, plus complexe à écrire.
  • Cache-blocking : découper en sous-matrices qui tiennent en cache L2.
  • Parallélisation : ForkJoinPool ou parallelStream sur les lignes de C.
  • Bibliothèques natives : ojAlgo, netlib-java (BLAS/LAPACK) pour de la performance industrielle.

Pour un TP, un code de concours ou un petit traitement scientifique, l'algorithme naïf avec boucles i → k → j est largement suffisant. Pour des matrices de taille production, appelez une bibliothèque native.