PM-07 p. 1/13 Programozási módszertan Dinamikus programozás: A leghosszabb közös részsorozat Werner Ágnes Villamosmérnöki és Információs Rendszerek Tanszék e-mail: werner.agnes@virt.uni-pannon.hu
PM-07 p. 2/13 Dinamikus programozás jellemzői Egy dinamikus programozási algoritmus kifejlesztése 4 lépésre bontható fel: 1. Jellemezzük az optimális megoldás szerkezetét. 2. Rekurzív módon definiáljuk az optimális megoldás értékét. 3. Kiszámítjuk az optimális megoldás értékét alulról felfelé történő módon. 4. A kiszámított információk alapján megszerkesztünk egy optimális megoldást.
PM-07 p. 3/13 Bevezetés Biológiai alkalmazásokban gyakran össze kell hasonlítani pl. két vagy több élőlény DNS-ét. A DNS-t bizonyos, bázisnak nevezett molekulák szekvenciái alkotják. A lehetséges bázisok: adenin, guanin, citozin és timin. A bázisokat a kezdőbetűjükkel jelöljük. A DNS szerkezete leírható a véges [A, C, G, T] halmazon vett sztringgel. Pl. az egyik élőlény DNS-e S 1 = ACCGGTCGAGTGCGCGGAAGCCGGCCGAA a másik élőlény DNS-e S 2 = GTCGTTCGGAATGCCGTTGCTCTGTAAA Két élőlény DNS-ét pl. abból a célból hasonlíthatjuk össze, hogy megállapítsuk, hogy mennyire hasonlóak. (valamilyen mérték szerint a két lény mennyire közel áll egymáshoz)
PM-07 p. 4/13 Összehasonlítás A hasonlóságot több féle képpen definiálhatjuk: két DNS szekvencia hasonló, ha az egyik a másiknak részsztringje, két szekvencia hasonló, ha kevés változtatással vihetők át egymásba, keresünk egy olyan S 3 szekvenciát, amelyik olyan bázisokból áll, amelyek ugyanabban a sorrendben, de nem feltétlenül egymás után fordulnak elő mind S 1 -ben, mind S 2 -ben; minél hosszabb, annál nagyobb a hasonlóság. A példában: a leghosszabb S3 szekvencia S 1 = ACCGGTCGAGTGCGCGGAAGCCGGCCGAA S 2 = GTCGTTCGGAATGCCGTTGCTCTGTAAA S 3 = GTCGTCGGAAGCCGGCCGAA
PM-07 p. 5/13 Részsorozat, közösrészsorozat Egy sorozat részsorozata egy olyan sorozat, amit az adott sorozatból néhány elem elhagyásával nyerünk. Formálisan: Ha az adott X = (x 1,x 2,...,x m ), akkor egy másik sorozat Z = (z 1,z 2,...,z k ) sorozat akkor részsorozata X-nek, ha létezik X indexeinek egy szigorúan növő (i 1,i 2,...,i k ) sorozata, hogy minden j = 1,2,...,k esetén x ij = z j. Adott két sorozat, X és Y. Azt mondjuk, hogy egy Z sorozat közös részsorozatuk, ha Z részsorozata X-nek is és Y -nak is.
PM-07 p. 6/13 A leghosszabb közös részsorozat=lkr 1. lépés: A leghosszabb közös részsorozat jellemzése A feladat megoldásának egyik módszere leszámolni X összes részsorozatát és mindegyiket ellenőrizni, hogy részsorozata-e Y -nak és tárolnunk kell a megtalált leghosszabb részsorozatot. X mindegyik részsorozata az 1,2,...,m indexhalmaz egy részhalmazának felel meg. Mivel X-nek 2 m részsorozata van, az eljárás exponenciális időt igényel. Az algoritmus használhatatlan. Ez a probléma azonban rendelkezik az optimális részstruktúra tulajdonsággal.
PM-07 p. 7/13 Az LKR optimális részstruktúrája Az X = (x 1,x 2,...,x m ) sorozat i-edik prefixe X i = (x 1,x 2,...,x i ) (i = 0,1,...,m). Tétel: (Az LKR optimális részstruktúrája.) Legyen X = (x 1,x 2,...,x m ) és Y = (y 1,y 2,...,y n ) két sorozat és Z = (z 1,z 2,...,z k ) ezek egy LKR-je. Ekkor igazak a következő állítások: 1. Ha x m = y n, akkor z k = x m = y n és Z k 1 az X m 1 és Y n 1 egy LKR-je. 2. Ha x m y n, akkor z k x m esetén Z az X m 1 és Y egy LKR-je. 3. Ha x m y n, akkor z k y n és Z az X és Y n 1 egy LKR-je.
PM-07 p. 8/13 Az LKR optimális részstruktúrája Bizonyítás: 1. Ha z k x m, akkor az x m = y n elemet hozzátehetnénk Z-hez, és így X és Y egy k + 1 hosszú közös részsorozatát kapnánk, ez ellentmond Z választásának. Ezért z k = x m = y n. Most a Z k 1 prefix hossza k 1 és közös részsorozata X m 1 -nek és Y n 1 -nek. Meg kell mutatni, hogy ezek egy LKR-je is. Ha W egy legalább k hosszú közös részsorozata X m 1 -nek és Y n 1 -nek, akkor ehhez hozzáfűzve az x m = y n elemet, X és Y k-nál hosszabb közös részsorozatát kapjuk, ami ellentmond Z választásának. 2. Ha z k x m, akkor Z közös részsorozata X m 1 -nek és Y -nak. Ha ezeknek volna egy W közös részsorozata, mely hosszabb, mint k, akkor ez közös részsorozata volna X m -nek és Y -nak, ami ismét ellentmond Z választásának. 3. A bizonyítás hasonlóan történik, mint 2-nél.
PM-07 p. 9/13 2. lépés: részfeladatok rekurzív megoldása A tétel azt mondja, hogy csak egy vagy kettő részfeladat van, amit meg kell vizsgálni X = (x 1, x 2,..., x m ) és Y = (y 1, y 2,..., y n ) LKR-jének megkeresésekor. Ha xm = y n, akkor X m 1 és Y n 1 LKR-jét kell megkeresni, amelyhez csatolva az x m = y n elemet kapjuk X és Y LKR-jét. Ha xm y n, akkor két részfeladatot kell megoldanunk: X m 1 és Y, illetve X és Y n 1 LKR-jét kell megkeresni. Akármelyik is a hosszabb, az az X és Y LKR-je. Mivel ezek az esetek kimerítik az összes lehetőséget, tudjuk, hogy egy részfeladat megoldása benne van X és Y LKR-jében. Látható, hogy az LKR problémában a részfeladatok átfedők. X és Y LKR-jének megkeresésekor szükségünk lehet X és Yn 1, illetve X m 1 és Y LKR-jének megkeresésére. Ezek közös részfeladata Xm 1 és Y n 1 LKR-jének meghatározása.
PM-07 p. 10/13 Rekurzív összefüggés Meghatározható egy rekurzív összefüggés: Legyen c[i,j] X i és Y i LKR-jének hossza. Ha akár i, akár j nulla, azaz a sorozatok legalább egyikének hossza 0, akkor természetesen az LKR hossza is 0. Az LKR optimális részstruktúra tulajdonságából a következő rekurzív képletet kapjuk: c[i, j] = 0, ha i = 0 vagyj = 0, c[i 1,j 1] + 1, ha i,j > 0 és x i = y j max(c[i,j 1],c[i 1,j]), ha i,j > 0 és x i y j
PM-07 p. 11/13 3. lépés: LKR hosszának kiszámítása Használhatjuk a dinamikus programozást. LKR-hossza(X, Y ) 1. m := hossz[x] 2. n := hossz[y ] 3. for i := 1 to m do c[i,0] := 0 4. for j := 1 to m do c[0, j] := 0 5. for i := 1 to m 6. do for j := 1 to n 7. do if x i = y j 8. then c[i, j] := c[i 1, j 1] + 1 9. b[i, j] := 10. else if c[i 1, j] c[i, j 1] 11. then c[i, j] := c[i 1, j] b[i, j] := 12. else c[i, j] := c[i, j 1] b[i, j] := 13. return c és b Az eljárás futási ideje O(m n), mivel minden tömbelem kiszámításának ideje O(1).
Eredmény mátrix PM-07 p. 12/13
PM-07 p. 13/13 4.lépés: Egy LKR megszerkesztése Az alábbi eljárás X és Y LKR-jének elemeit a helyes, eredeti sorrendben nyomtatja ki. LKR-nyomtat(b, X, i, j) 1. if i = 0 vagy j = 0 2. then return 3. if b[i,j] = 4. then LKR-nyomtat(b,X,i 1,j 1) 5. print x i 6. else if b[i,j] = 7. then LKR-nyomtat(b,X,i 1,j) 8. else LKR-nyomtat(b,X,i,j 1)