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 :
ForkJoinPoolouparallelStreamsur 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.