DerHirschberg-Algorithmus berechnet das paarweiseSequenzalignment und hat einen zur Eingabelinearen Speicherbedarf. Der in den 1970er Jahren vonDan Hirschberg entwickelteAlgorithmus verwendet die Methode derDynamischen Programmierung und dasDivide-and-conquer Prinzip.
Der Hirschberg-Algorithmus ist ein allgemein einsetzbarer und optimaler Algorithmus zum Auffinden eines Sequenzalignment. Der bekannteBLAST-Algorithmus und derFASTA-Algorithmus sind nur suboptimale Heuristiken. Vergleicht man den Hirschberg-Algorithmus mit demNeedleman-Wunsch-Algorithmus, so handelt es sich beim Hirschberg-Algorithmus weniger um einen komplett neuen Algorithmus, sondern eher um eine clevere Strategie, die den Needleman-Wunsch-Algorithmus geschickt einsetzt, um den Speicherverbrauch zulinearisieren, was auch das Besondere an diesem Algorithmus ist: Die Berechnungen für ein Sequenzalignment benötigen nur linear viel Speicherplatz, womit diePlatzkomplexität des Algorithmus inO(n) liegt. Zur Berechnung eines Alignments zweier Zeichenketten undmit und besitzt der Algorithmus eine Laufzeitvon und einen Speicherverbrauch von. O.B.d.A soll im Folgenden gelten, so dass der Platzbedarf in liegt.
Anwendung findet der Algorithmus zum Beispiel in derBioinformatik zum Abgleich verschiedenerDNA- oderProteinsequenzen.
In einer leicht abgewandelten Form wird Hirschbergs Algorithmus auch dazu verwendet, um in einemGraphen parallelZusammenhangskomponenten mit Aufwand auf Prozessoren zu berechnen.
Zum Verständnis des Hirschberg-Algorithmus ist es zunächst wichtig zu verstehen, dass sich dieLevenshtein-Distanz auf linearem Speicherplatz berechnen lässt:
01 := 002 for j in 1..n loop03 := +04 end loop05 for i in 1..m loop06 s :=07 := +08 c :=09 for j in 1..n loop10 c :=11 s :=12 := c13 end loop14 end loop
In den Zeilen 1–4 wird das eindimensionale Feld mit n Plätzen Speicherbedarf initialisiert. In Zeile 6 wird die Initialisierung des ersten Elements in gerettet.Danach wird und mit dem Startwert für die nächste Zeile initialisiert. Die nachfolgende Abbildung zeigt eine Momentaufnahme eines Zeilendurchlaufs.In der inneren Schleife zeigt immer auf das jeweils zuvor berechnete Ergebnis, während das noch benötigte Ergebnis der letzten Zeile sichert. Nach Zeile 14steht dieLevenshtein-Distanz als Ergebnis in.
ε ...ε0 1 2 3 ...1...
s =0c = =1
Es sollte klar sein, dass sich diese Berechnung auch rückwärts durchführen lässt. Dabei wird die gedachte Matrix nicht von links nach rechts und von oben nach unten durchlaufen, sondern von rechts unten nach links oben:
01 := 002 for j in n-1..0 loop03 := +04 end loop05 for i in m-1..0 loop06 s :=07 := +08 c :=09 for j in n-1..0 loop10 c :=11 s :=12 := c13 end loop14 end loop
Der Divide-&-Conquer-Algorithmus vonHirschberg berechnet ein Alignment der Zeichenketten und, indem er Vorwärts- und Rückwärtsdurchlauf miteinander kombiniert (Zeilenangaben beziehen sich auf dennachfolgend angegebenen Pseudocode):
1. Wenn oder liegt eintriviales Alignment-Problem vor (Zeilen 14 – 22). Ein String bestehend aus nur einem Zeichen muss auf einen anderen String ausgerichtet werden und ein Alignment wird zurückgegeben. Ist und geht man über zu Schritt 2.
2. EinVorwärtsdurchlauf berechnet ein Alignment von und derersten Hälfte von (Zeilen 27 – 40). Das Ergebnis des Vorwärtsdurchlaufs ist ein Feld, dessen Elemente die Kosten für einen Durchlauf von bis (mit) angeben.
3. EinRückwärtsdurchlauf berechnet ein Alignment von mit derzweiten Hälfte von (Zeilen 42 – 55). Das Ergebnis ist ein weiteres Feld, dessen Elemente die Kosten für einen Durchlauf von zurück zu angeben.
4. In den Feldelementen und stehen die beidenLevenshtein-Distanzen für die globalen Alignments von und den jeweiligen Hälften von. In den restlichen Elementen von stehen die Distanzen von der ersten-Hälfte zu allen Präfixen von. Entsprechend enthalten die restlichen Elemente von die Distanzen von der zweiten-Hälfte zu allen Suffixen von.
5. DieLevenshtein-Distanz von zu kann nun errechnet werden, indem man entlang der mittleren Zeile der Alignment-Matrix läuft und nach einer kleinsten Summe von korrespondierenden- und-Elementen sucht. Ist eine solche minimale Summe gefunden, hat man eine Position in der mittleren Zeile gefunden, in der das optimale Alignment die mittlere Zeile bzw. die Mitte von schneidet. An dieser Stelle wird in zwei Teile zerteilt und damit kann auch das Alignment-Problem in zwei kleinere Alignment-Probleme zerteilt werden (Zeilen 59 – 65).
6. Schritt 1 wird rekursiv auf den beiden Teilen von und aufgerufen. Die beiden rekursiven Aufrufe geben Teil-Alignments zurück, die zu einem einzigen Alignment verknüpft werden. Das Alignment wird zurückgegeben (Zeilen 68 und 69).
01 --02 -- DerDivide-&-Conquer-Algorithmus von Hirschberg zur03 -- Berechnung des globalen Alignments auf linearem Speicher.04 --05 -- Bei besitzt der Algorithmus eine Laufzeit von06 -- und einen Speicherverbrauch von.07 --08 function HirschbergAlignment(x,y : string) return A is09 function SubAlignment(,,, : integer) return A is10 mitte,cut : integer11 s,c : real12 : array(..) of real13 begin14 if or then15 -- Konstruiere Matrix T für die Teil-Strings16 -- x(..) und y(..)17 -- Achtung: Nur linearer Speicherplatz erforderlich!18 T := ...19 -- Berechnetriviales Alignment auf Matrix T20 -- in linearer Laufzeit21 return Alignment(T,x(..),y(..))22 end if2324 mitte :=25 -- finde ausgehendvon den minimalen Pfad26 -- mit dem Vorwärtsalgorithmus:27 := 028 for j in.. loop29 :=30 end loop31 for i in..mitte loop32 s :=33 c :=34 := c35 for j in.. loop36 c :=37 s :=38 := c39 end loop40 end loop41 -- finde minimalen score-pfadnach42 := 043 for j in.. loop44 :=45 end loop46 for i in..mitte loop47 s :=48 c :=49 := c;50 for j in.. loop51 c :=52 s :=53 := c54 end loop55 end loop56 -- finde den Punkt aus.. in dem der Minimale Pfad die57 -- mittlere Zeile schneidet:58 --59 for j in.. loop60 if j= then61 cut :=62 elsif then63 cut := j64 end if65 end loop66 -- Alignment entsteht durch Konkatenation von linkem und67 -- rechtem Teil-Alignment:68 return SubAlignment(,,mitte,cut)69 SubAlignment(mitte,cut,,)70 end SubAlignment71 m,n : integer72 begin73 m :=; n :=74 -- Sonderbehandlung: ist der leere String und lässt keine Zerteilung zu:75 if m=0 then76 return77 else78 return SubAlignment(0,0,m,n)79 end if80 end HirschbergAlignment