On considère un tableau de la forme suivante X=[e1, e2, ..., en] avec .
où chaque est un entier (positif, nul ou négatif).
L’algorithme doit déterminer la plus grande somme possible d’un sous-tableau contigu de X.
Autrement dit, on cherche :

est l’indice de début et l’indice de fin du sous-tableau.

Exemple :
Prenons en compte le tableau .
Regardons ce que devrait renvoyer l’algorithme après la fin du traitement.


  • Pas considéré car .

  • La somme maximale devient

  • La somme maximale reste

  • La somme max devient

Ainsi, à la fin de cet algorithme, il devra renvoyer la sous-séquence qui a pour somme max .

  • Premier algorithme proposé pour résoudre le problème

On considère ici que X représente le tableau, maxS la somme maximale et S la somme du sous-tableau courant.

maxS = 0
for(g=0 ; g<n; g++){
	for(d=g ; d<n ; d++){
		S = 0
		for(i=g ; i<=d ; i++){
			S += X[i]
			if(maxS < S){
				maxS = S
			}
		}
	}
}

On cherche à savoir si notre programme est efficace ou non, pour ce faire il faut que l’on arrive à déterminer la complexité de ce dernier.
L’instruction qui s’exécutera le plus de fois dans notre code est :

S += X[i]

Car :

  • La boucle for(g=0 ; g<n ; g++) sera exécutée n fois.
  • La boucle for(d=g ; d<=n ; d++ sera exécutée environ n-g fois
  • La boucle for(i=g; i<=d ; i++) sera exécutée environ d-g+1 fois.
    On rajoute car on a , c’est le “ou égal” qui implique le rajout du .

Ainsi l’instruction S += i sera exécutée environ :

On peut aussi le faire comme M.Balev, avec le fameux tableau :

g \ d0123...n-2n-1
01234...n-1n
1-123...n-2n-1
2--12...n-3n-2
3---1...n-4n-3
...
n-2----...12
n-1----...-1

Warning

Si il ne se passe rien, l’instruction ne s’exécute pas !

Maintenant, il faut additionner tous les termes du tableau afin de savoir sur quelle complexité on est. En fait, chaque ligne représente la somme des allants de à , puis de à , et ainsi de suite.
Ainsi la somme des éléments du tableau sera donnée par :

Ainsi on peut simplifier cette expression car la somme des allant de à est donnée par :

Ainsi :

Il faut donc réussir à simplifier cette expression.

On se retrouve alors avec un algorithme de complexité cubique, autant dire que ce n’est pas très efficace en réalité…

Cherchons alors un moyen de réduire la complexité de cette algorithme, pour résoudre le même problème.
On sait que l’instruction la plus utilisée est S+=X[i].
On peut éviter de recalculer la somme courante à chaque fois, au lieu de repartir de à chaque fois pour chaque (g, d), on peut simplement ajouter le nouvel élément X[d] à la somme courante.

On obtient alors l’algorithme suivant :

maxS = 0;
for (g = 0; g < n; g++) {
    S = 0;
    for (d = g; d < n; d++) {
        S += X[d];
        if (S > maxS) {
            maxS = S;
        }
    }
}

Ainsi,

  • La première boucle est exécutée fois
  • La seconde boucle est exécutée fois

L’instruction S += X[d] est donc exécutée :

fois.
Le nombre d’exécution de l’instruction est donné par la complexité .

On se demande si il est encore possible d’améliorer l’algorithmes.
On peut utiliser l’algorithme diviser pour reigner, qui consiste à diviser le tableau en parties puis comparer les deux sommes. Hors cela n’est pas suffisant car si la sous séquences max se trouve au milieu du tableau, on en le saura pas, il faut donc aussi calculer les sommes max aux frontières gauche et aux frointières droite du tableau en partant du milieu.

On obtient alors le troisième algorithme :

algo(g, d){
  if (g>d) return 0;
  if (g==d) return max (X[g], 0);
  
  // Cas ou il y au moins deux éléments
  int m = (g+d)/2;
  int S1 = algo(g, m)
  int S2 = algo(m+1, d)
  
  // On calcule les deux sommes aux frontières
  // Frontière gauche
  int S3 = 0, S = 0;
  for(int i = m; i >= g; i--){
    S += X[i]
    if(S > S3) S3 = S;
  }  
  // Frontière droite
  int S4 = 0;
  S = 0;
  for(int i = m+1; i<=d; i++){
    S+=X[i];
    if (S>S4) S4=S;
  }
  return max(S1, S2, S3, S4);
}

Les compléxités sont données par :

On a réussit à partir d’un algo de complexité pour arriver à une complexité de .

On se retrouve alors avec algorithmes que l’on nomera algo1, algo2 et algo3 respectivement.
Chacun de complexité :

On se demande si il est encore possible d’améliorer.
Et oui, il est encore possible d’améliorer l’algorithme.

On considère la somme de la sous séquences des indices jusqu’à et X[i]$. Au départ, avant de commencer à parcourir le tableau, les deux sommes sont égales, c’est à dire nulle. Ensuite on parcours chaque élément du tableau et garde à chaque fois le max des valeurs.

On obtient l’algorithme suivant :

algo(X, n){
	maxS = max I = 0
	for(int i = 0; i<n; i++){
		maxI = max(maxI+X[i], 0);
		maxS = max(maxS, maxI);
	}
}

Ainsi en étudiant l’algorithme pour sa complexité en fonction de la taille de , on obtient :

On peut encore se demander si il est possible d’améliorer l’algorithme.
En fait, la réponse semble plutôt évidente car on est obligé de parcourir au moins une fois les éléments du tableau pour les comparer et ne pas les oublier. Alors la complexité minimale de l’algorithme est bien donnée par .