Site WWW de Laurent Bloch
Slogan du site

ISSN 2271-3905
Cliquez ici si vous voulez visiter mon autre site, orienté vers des sujets moins techniques.

Pour recevoir (au plus une fois par semaine) les nouveautés de ce site, indiquez ici votre adresse électronique :

Accélération d’algorithme
Article mis en ligne le 7 mars 2006
dernière modification le 25 novembre 2014

par Laurent Bloch

Auteurs : William Saurin, Laurent Bloch

Recherche du maximum d’ un ensemble de valeurs

Dans un tableau A on cherche à déterminer la plus grande valeur. Pour cela on peut décider de conserver l’indice de la valeur maximale dans un variable qu’on apellera max et parcourir le tableau, chaque fois que A[max] sera plus petit que la valeur courante du tableau on fera passer max à la valeur courante de i.

Nom de l'algo : Max
Données d'entrées : un tableau A
Résultat : le rang de plus grande valeur de A
max ← 0
pour i allant de 1 à longueur de A - 1 faire
   si A[i] > A[max] alors
      max ← i
   finsi
fait
retourner max

On passe par la ligne si A[i] > A[max] alors autant de fois qu’ il y a d’éléments du tableau - 1 ;

Recherche de segments maximaux O(n3)

Un tableau A contient des nombres positifs et négatifs. En fait ce sont les coefficients d’hydrophobicité des acides aminés consécutifs d’une protéine transmembranaire. En voici quatre exemples, pour vos essais :

;; V1
#(38.0 -61.0 -33.0 44.0 -100.0 -81.0 -89.0 44.0 -5.0 34.0 -63.0 -98.0 -1.0 -99.0 21.0 97.0 84.0 83.0 46.0 -56.0 -26.0 99.0 -60.0 -96.0 -38.0 92.0 -86.0 -5.0 83.0 73.0 -4.0 99.0 92.0 -29.0 -89.0 99.0 -74.0 -96.0 -34.0 -100.0)

;; V2
#(-67.0 74.0 100.0 -99.0 -90.0 100.0 -5.0 -98.0 36.0 -79.0 67.0 32.0 -88.0 -91.0 -100.0 -79.0 -89.0 98.0 67.0 -22.0 47.0 -49.0 -96.0 -85.0 -92.0 88.0 16.0 63.0 61.0 86.0 -78.0 81.0 85.0 -81.0 36.0 -25.0 59.0 -95.0 81.0 -9.0 1.0)

;; AIE-1
#(29.0 -100.0 -28.0 51.0 69.0 -94.0 89.0 -72.0 -96.0 17.0 98.0 100.0 -31.0 2.0 88.0 85.0 -8.0 99.0 -37.0 -57.0 63.0 -87.0 33.0 -99.0 -96.0 59.0 75.0 90.0 -40.0 96.0 -76.0 -76.0 -100.0 90.0 11.0 67.0 -100.0 -46.0 -84.0 75.0 90.0 75.0 79.0 75.0 33.0)

;; AIE-2
#(-93.0 91.0 31.0 -99.0 -58.0 100.0 62.0 -79.0 10.0 -42.0 -94.0 63.0 -92.0 -56.0 57.0 -92.0 97.0 -49.0 40.0 -38.0 -57.0 -72.0 38.0 94.0 -33.0 100.0 41.0 -31.0 91.0 52.0 69.0 4.0 -15.0 92.0 -26.0 100.0 -99.0 69.0 87.0 -93.0 -98.0 99.0 97.0 15.0)

Dans ce tableau A on cherche à determiner quels sont les positions i et j telles que la somme A[i] + A[i+1] + ... +A[j] est la plus grande possible. La séquence des acides aminés de rang i à j correspond probablement à une partie de la protéine qui est dans la membrane.

Allons y lentement.

On peut imaginer partir de chaque valeur possible de i et calculer toutes les sommes pour toutes les cases successives, chaque fois qu’on a une valeur plus élevée que la plus haute obtenue pour l’instant on met à jour des variables imax et jmax.

Nom de l'algo : Segments maximaux 1
données d'entrées: tableau A
résultats : le triplet imax, jmax, vmax contenant les indices de début et de fin du segment maximal ainsi que la valeur vmax du segment maximal.
imax ← 0
jmax ← 0
Vmax ← a[0]
pour i allant de 0 à longueur de A - 1 faire
   pour j allant de i à taille de A -1 faire
      V ← 0
      pour k allant de i à j faire
         V ← v + A[k]
      fait
      si V > Vmax alors
         V ← Vmax
         imax ← i
         jmax ← j
      finsi
   fait
fait
retourner imax, jmax, vmax

combien de fois faisons nous V ← v + A[k] ?

combien de fois faisons nous si V > Vmax alors ?

combien de fois faisons nous V ← 0 ?

Le programme O(n3)

(module segmax1
   (main segread))

(define (segread args)
   (let* ((the-file (cadr args))
          (the-vector (with-input-from-file the-file read)))
      (let ((les-resultats (segmax1 the-vector)))
         (print "imax = " (vector-ref les-resultats 0)
                " jmax = " (vector-ref les-resultats 1)
                " vmax = " (vector-ref les-resultats 2)
                " longueur du tableau = "
                (vector-ref les-resultats 3))
         )))

(define (segmax1 A)
   (let ((L (vector-length A))
         (imax 0)
         (jmax 0)
         (vmax (vector-ref A 0)))
      (do ((i 0 (+ 1 i)))
          ((= i L)
           (vector imax jmax vmax L))
          (do ((j 0 (+ 1 j)))
              ((= j L))
              (let ((V 0))
                 (do ((k i (+ k 1)))
                     ((> k j)
                      (if (> V vmax)
                          (begin
                             (set! vmax V)
                             (set! imax i)
                             (set! jmax j))))
                     (set! V (+ V (vector-ref A k)))
                     ))))))

Aller plus vite ? O(n2)

On peut aussi commencer par sommer les cases successives du tableau que l’on enregistrera dans un tableau annexe B de la sorte dans la case de rang i+1 de B on aura la somme de valeurs des cases A[0], A[1], A[2], ... jusqu’à A[i]. La valeur B[0] est donc nécessairement 0. Dans ce cas la valeur de B[j+1] - b[i] est la valeur de la somme des cases A[i] + a[i+1] + ... + a[j]. On voit qu’il est alors possible d’éliminer la boucle interne de l’algorithme Segments maximaux 1.

On obtient l’algorithme :

Nom de l'algo : Segment maximaux 2
données d'entrées : tableau A
résultats : le triplet imax, jmax, vmax contenant les indices de début et de fin du segment maximal ainsi que la valeur vmax du segment maximal.
creer un tableau B de longueur longueur de A +1
B[0] ← 0
pour i allant de 0 à longueur de A - 1 faire
   B[i+1] ← B[i] + A[i]
fait
imax ← 0
jmax ← 0
Vmax ← B[1]
pour i allant de 0 à longueur de A-1 faire
   pour j allant de i à taille de A-1 faire
      V ← B[j+1] - B[i]
      si V > Vmax alors
         Vmax ← V
         imax ← i
         jmax ← j
      finsi
   fait
fait
retourner imax, jmax, vmax

Le programme O(n2)

(module segmax2
   (main segread))

(define (segread args)
   (let* ((the-file (cadr args))
          (the-vector (with-input-from-file the-file read)))
      (let ((les-resultats (segmax2 the-vector)))
         (print "imax = " (vector-ref les-resultats 0)
                " jmax = " (vector-ref les-resultats 1)
                " vmax = " (vector-ref les-resultats 2)
                " longueur du tableau = "
                (vector-ref les-resultats 3))
         )))

(define (segmax2 A)
   (let* ((L (vector-length A))
          (B (make-vector
              (+ 1 L) 0)))
      (do ((i 0 (+ i 1)))
          ((= i L))
          (vector-set! B (+ i 1)
                      (+ (vector-ref A i)
                         (vector-ref B i))))
      (let ((imax 0)
            (jmax 0)
            (vmax (vector-ref B 1)))
         (do ((i 0 (+ 1 i)))
             ((= i L)
              (vector imax jmax vmax L))
             (do ((j i (+ 1 j)))
                 ((= j L))
                 (let ((V
                        (- (vector-ref B
                                       (+ j 1))
                           (vector-ref B
                                     i))))
                    (if (> V vmax)
                        (begin
                           (set! vmax V)
                           (set! imax i)
                           (set! jmax j)))))))
      ))

Aller plus vite ? O(n1)

Considérons le tableau B de la section précédente. Le segment maximal est celui dont les cases extrêmes sont i et j tel que pour lesquels B[j+1] - B[i] est maximal ! En quelque sorte si nous considérons le graphe de la fonction décrite par B, ce qui nous intéresse c’est le plus grand dénivelé entre une valeur de B[j+1] et B[i].

Si lorsque nous examinons successivement toutes les valeurs de B, et que au moment où nous examinons B[j+1] nous sachions quel est le imax tel que B[imax] est la plus petite valeur de B observée entre les cases 0 et j de B alors il est clair que B[j+1] -B[imax] est la valeur du segment de A dont les sommes des valeurs se terminant en A[j] est la plus élevée.

Décidons donc de créer un tableau C de même longueur que B et tel qu’en C[k] on trouve la position imax telle que B[imax] est la plus petite valeur de B entre les cases 0 et k. Dans ce cas on sait que B[j+1] -B[C[j]] est la valeur du segment maximal se terminant en A[j], il commence en C[j]. Le segment maximal de A est donc celui compris entre C[j] et j tel que B[j+1] -B[C[j]] est maximal. L’algorithme devient donc calculer B, calculer C, calculer D qui contient en j les valeurs de B[j+1] -B[C[j]], trouver la valeur la position de la valeur maximale de D, c’est la position de fin du segment maximal, à cette position en C on trouve la position de début du segment et en D a cette position la valeur du segment. L’algorithme s’écrit :

Nom de l'algo: Segment maximaux 3
données d'entrées: tableau A
résultats : le triplet imax, jmax, vmax contenant les indices de début et de fin du segment maximal ainsi que la valeur vmax du segment maximal.
créer un tableau B de longueur longueur de A +1
créer un tableau C de longueur longueur de A +1
créer un tableau D de longueur longueur de A +1
B[0] ← 0
pour i allant de 0 à longueur de A - 1 faire
   B[i+1] ← B[i] + A[i]
fait
C[0] ← 0
pour i allant de 1 à longueur de A - 1 faire
   si B[C[i-1]] >= B[i] 
      alors C[i] = i
      sinon C[i] = C[i-1]      
   finsi
fait
pour i allant de 0 à longueur de A - 1 faire
   D[i] ← B[i+1] - B[C[i]]
fait
jmax ← Position(Max(D))
imax ← C[jmax]
Vmax ← D[jmax]
retourner imax, jmax, vmax

Le programme O(n1)

(module segmax3
   (main segread))

(define (segread args)
   (let* ((the-file (cadr args))
          (the-vector (with-input-from-file the-file read)))
      (let ((les-resultats (segmax3 the-vector)))
         (print "imax = " (vector-ref les-resultats 0)
                " jmax = " (vector-ref les-resultats 1)
                " vmax = " (vector-ref les-resultats 2)
                " longueur du tableau = "
                (vector-ref les-resultats 3))
         )))

(define (PosMaxTab A)
   (let ((Lvec (vector-length A)))
      (do ((i 0 (+ i 1))
           (imax 0 (if (> (vector-ref A i)
                          (vector-ref A imax))
                       i imax)))
          ((= i Lvec)
           imax))))

(define (segmax3 A)
   (let* ((L (vector-length A))
          (B (make-vector (+ 1 L) 0))
          (C (make-vector (+ 1 L) 0))
          (D (make-vector (+ 1 L) 0)))
      (do ((i 0 (+ i 1)))
          ((= i L))
          (vector-set! B (+ i 1)
                      (+ (vector-ref A i)
                         (vector-ref B i))))
      (do ((i 1 (+ i 1)))
          ((= i L))
          (vector-set!
           C i
           (if (>= (vector-ref
                    B
                    (vector-ref C (- i 1)))
                   (vector-ref B i))
               i
               (vector-ref C (- i 1)))))
      (do ((i 0 (+ i 1)))
          ((= i L))
          (vector-set!
           D i
           (- (vector-ref B (+ i 1))
              (vector-ref B (vector-ref C i)))))
      (let* ((jmax (PosMaxTab D))
             (imax (vector-ref C jmax))
             (Vmax (vector-ref D jmax)))
         (vector imax jmax Vmax L)
      )))

Exercice : a-t-on besoin de tous ces tableaux ?

Que fait :

Nom de l'algo: mystere-1 
entrée: A
sortie: imax, jmax, vmax
créer un tableau B de longueur (longueur de A) + 1
B[0] ← 0
pour i allant de 1 à longueur de A faire
   B[i] ← A[i-1] + B[i-1]
   si B[i] < 0 alors B[i] ← 0 finsi
   si B[i-1] = 0 et B[i] != 0 alors idebut = i -1 finsi
   si B[i] > vmax 
      alors Vmax ← b[i] , jmax ← i-1, imax ← idebut finsi
fait
retourner imax, jmax, vmax