Amortizációs költségelemzés Amennyiben műveleteknek egy M 1,...,M m sorozatának a futási idejét akarjuk meghatározni, akkor egy lehetőség, hogy külön-külön minden egyes művelet futási idejét kifejezzük és összegezzük. Ennél gyakran pontosabb és kedvezőbb eredményt kapunk, ha helyette a teljes műveletsor összesített költségét számítjuk, de az egyes műveletekét nem. Ekkor a műveletekre átlagolt T (n)/m költséget számítjuk, ezt az műveletek átlagolt költségének nevezzük. (Ez továbbra is lehet legrosszabb eset elemzés, itt az átlag nem az input eloszlásra, hanem a műveletekre vonatkozik.) Az átlagolt költség meghatározására a következő módszereket szokás használni. Összesítéses módszer. Könyvelési módszer. Potenciál módszer. Többszörös VeremBol művelet. Vegyünk egy módosított verem adattípust, amelyben a verem adattípus műveletei mellett definiálunk egy TOROL(V,k) műveletet, amely törli a legfelső k elemet, ha van k darab elem, egyébként pedig törli a verem összes elemét. Tudjuk, hogy az üres V veremből kiindulva végrehajtottunk m darab műveletet, amelyek mindegyike a Verembe(V,x), Torol(V),Verembol(V,x), Torol(V,k) valamelyike. Feladat az átlagolt költség meghatározása. Ha egyenként vesszük a műveleteket a Torol(V,k) művelet futási ideje O(k) a többié konstans, így csak O(k) értéket kapnánk az átlagolt költségre. Látjuk majd, hogy O(1) is teljesül. 2. Példa Bináris számláló növelése. Tekintsük az A[0..k-1] bitvektort, ami egy szám 2-es számrendszerbeli leirása, a tömb i-dik eleme az 2 i -es helyiértéken álló szám. A feladat a számláló növelése, kezdetben x = 0, azaz A[i] = 0 minden i-re. Az x := x+1 való növelésre a következő eljárást használjuk. Novel(A) i:=0 while(i<=k-1) and A[i]=1 A[i]:=0 i:=i+1 if i<=k-1 then A[i]:=1 A feladat a 2 k darab Novel eljárás átlagolt költségének (bitek változtatásának száma) meghatározása. Egy művelet költsége legrosszabb esetben O(k). Összesítéses módszer Az összesítéses módszernél a teljes műveletsor futási idejére közvetlenül bizonyítunk felső korlátot. Az összesített költség O(n), mivel legfeljebb n elemet tudunk a verembe berakni, és csak berakott elemen keletkezhet költség a törlésnél vagy kivételnél. 2. Példa Vizsgáljuk meg a 2 k darab művelet teljes költségét. Vizsgáljuk meg az egyes tömbelemek hányszor változnak. Észrevehetjük, hogy A[0] minden műveletnél változik, azaz 2 k esetben, A[1] minden második műveletnél változik, 1
azaz 2 k 1 esetben, és így tovább A[i] 2 k 1 esetben változik. Következésképpen a teljes költség k 1 i=0 2k i = 2 k+1 1, azaz az átlagos költség konstans. Könyvelési módszer A könyvelési módszer lényege az, hogy más amortizált ĉ i költséget könyvelünk el az i műveletre, mint a tényleges költsége. Ez a költség lehet kisebb is, mint a tényleges csak arra kell ügyelni, hogy minden j m esetén fennálljon a j ĉ i j c i egyenlőtlenség. Ha az egyenlőtlenség teljesül használhatjuk az amortizált költséget átlagolt költségnek. A könyvelt költség legyen 2 a Verembe(V,x) művelet esetén, 0 a TOROL(V), TOROL(V,k), VEREMBOL(V,x) műveletek esetén. Ekkor a kivétel költségét már az elem verembe behelyezésekor elkönyveltük, így az amortizált költségekre követelt feltétel teljesül. 2. Példa A tényleges költség annyi, ahány bitet megváltoztat a művelet a végrehajtása során. Számítsunk 2 amortizációs költséget, ha egy bitet 1-re állítunk be. Mivel ezt az értékadást minden lépésben egyszer hajtjuk végre, ezért minden lépésben 2 az amortizált költség. Viszont egy bitet csak azt követően állíthatunk 0-ra, ha előtte 1-re állítottuk, így szintén teljesül a megkövetelt egyenlőtlenség, tehát az átlagolt költség O(1). Potenciál módszer Ennél a módszernél az előre kifizetett költséget nem egy művelethez rendeljük hozzá, hanem a vizsgált adatszerkezet állapotához rendelt potenciális energiaként tartjuk számon. A kezdeti állapota az adatszerkezetnek, amelyen m műveletet hajtunk végre legyen D 0, az i-edik művelet végrehajtása utáni állapot legyen D i. Ekkor az amortizált költség Az m művelet amortizált költségét számolva m ĉ i = c i + Φ(D i ) Φ(D i 1 ). ĉ i = m c i + Φ(D m ) Φ(D 0 ). Következésképpen, ha Φ(D n ) Φ(D 0 ) (például Φ(D 0 ) = 0 és Φ nemnegatív), akkor teljesül az amortizált költségre megkövetelt egyenlőtlenség. Legyen Φ a veremben levő elemek száma, ez egy nemnegatív függvény, aminek kezdeti értéke 0. Vizsgáljuk az amortizált költségeket. Verembe(V,x): Φ értéke 1-el nő, tényleges költség 1, így az amortizált 2. Torol(V), Verembol(V,x): Φ értéke 1-el csökken, tényleges költség 1, így az amortizált 0. Torol(V,k): Legyen r a minimuma k-nak és a veremben levő elemek számának. Ekkor Φ értéke r-el csökken, tényleges költség r, így az amortizált 0. 2
2. Példa Legyen Φ a számlálót leíró tömbben levő egyesek száma, ez egy nemnegatív függvény, aminek kezdeti értéke 0. Vizsgáljuk az amortizált költséget. Tegyük fel, hogy t bit változott, azaz a tényleges költség t. Ekkor a t változtatás közül egy esetben 0 változott 1-re, a többi t 1 esetben 1 változott 0-ra, így a potenciálfüggvény értéke t-2-vel csökkent, azaz az amortizált költség 2. 8 királynő probléma Helyezzünk el az n n-es sakktáblán n királynőt, hogy egyik se üsse a másikat! Megoldástér: n darab mező, amiket a koordinátákkal adhatunk meg: {(x 1,y 1 ),...,(x n,y n )}, 1 x i,y i n. A királynők akkor és csak akkor nem ütik egymást, ha x i x j, ha i j y i y j, ha i j x i + y i x j + y j, ha i j x i y i x j y j, ha i j Az első feltétel alapján feltehetjük, hogy y i = i, így a királynők elhelyezését egy (x 1,...,x n ) 1 x i n vektorral írjuk le, ami annak az elhelyezkedésnek felel meg, hogy az i-edik sorban a királynő az x j oszlopban van. x i x j, ha i j x i + i x j + j, ha i j x i i x j j, ha i j Megoldástér ábrázolás Ekkor a bejárandó megoldáskezdemények (x 1,...,x k ) vektorok 1 x i n, k n. A megoldáskezdemények egy fában ábrázolhatóak, a fát leíró, a bejárásához használható függvények: EFiu(x 1,...,x k ) = (x 1,...,x k,1), ha k<n és Nil ha k=n. Testver(x 1,...,x k ) = (x 1,...,x k 1,x k + 1), ha x k < n és Nil egyébként. APA(x 1,...,x k ) = (x 1,...,x k 1 ), ha k 1 és Nil egyébként. Kimerítő keresés A kimerítő keresés vagy nyers erő algoritmusa során az egész megoldástért bejárjuk, megvalósítható egy fabejárással. Megoldás keresése visszalépéses algoritmussal (backtracking) Az alapötlet az, hogy ha a fabejárás során, az olyan részfákat, amelyekben nincs lehetséges megoldás nem járjuk be, ha ilyen fához jutunk visszalépünk. Ennek megvalósításához szükség van egy függvényre, amely megadja, ha nem tartalmaz lehetséges megoldást a részfa. A LehetMego függvénytől ezt várjuk el, ha a függvény hamis értéket ad, a részfa nem tartalmaz lehetséges megoldást, igaz válasz esetén nem feltétlenül kell, hogy tartalmazzon megoldást a részfa. A LehetMego függvény megadja, ha nem kiterjeszthető a megoldáskezdemény, mert két királynő már üti egymást. 3
Tehát LehetMego(x 1,...,x k ) = True, akkor és csak akkor ha minden i j esetén teljesül, hogy x i x j, x i + i x j + j, x i i x j j Használunk még egy Megoldas függvényt, amely akkor és csak akkor ad igaz értéket, ha az adott pont lehetséges megoldás. Esetünkben Megoldas(x 1,...,x k ) = True, akkor és csak akkor teljesül, ha LehetMego(x 1,...,x k ) és k = n. A keretalgoritmus A fenti függvények alapján az alábbi általános keretalgoritmust építjük fel. Egy adott probléma esetén meg kell adni a megoldástér fáját és a problémához tartozó függvényeket. Visszalep(F) If F=Nil Then return //Üres fa If Megoldas(F) Then Print "F" // Megoldas kiiratasa return Else p:=f.elsofiu While (p!=nil) // a gyerekeken sorbamenve If LehetMego(p) Then Visszalep(p) p:=p.testver A fenti változat egyetlen megoldást keres meg, de a harmadik negyedik sor változtatásával könnyen átírható az összes megoldás megkeresésére. A kiiratás helyett a megoldást bele kell tenni egy halmazba, a negyedik "return" sort törölni. Az összes megoldás megkeresése felhasználható optimalizálási feladatok megoldására is. Pénzváltási probléma Az pénzváltási problémában adottak p 1,..., p n pénzérmék és egy E összeg, a feladat kiválasztani az érmék egy S halmazát, amelyre teljesül, hogy i S p i = E. A megoldástér a pénzérmék összes lehetséges részhalmaza. Egy lehetséges reprezentáció egy n szintből álló teljes bináris fa, amely a halmaz bitvektorát tárolja. A részhalmazoknak a levelek felelnek meg. A levélhez tartozó halmazt a levélig a gyökérből vezető út alapján kapjuk meg. Ha az i-dik szinten balra lépünk az i-dik elemet nem választjuk be, ha jobbra, akkor az i-dik elemet beválasztjuk a halmazba. Egy másik reprezentációval a megoldástér pontjait (a pénzek részhalmazát) a pénzek indexeinek az S {1,...,n} halmazának X = (i 1,...,i k ) növekvő felsorolásáként reprezentáljuk. Ekkor a bejáráshoz használt függvények EFiu(i 1,...,i k ) = (i 1,...,i k,i k + 1), ha i k < n és Nil ha i k = n. Testver(i 1,...,i k 1,i k ) = (i 1,...,i k 1,i k + 1), ha i k < n és Nil ha i k = n. Apa(i 1,...,i k 1,i k ) = (i 1,...,i k 1 ), ha k 1 és Nil egyébként. A LehetMego és Megoldás függvényeket a következőképpen kaphatjuk meg. A LehetMego függvény megadja, ha az eddig meghozott döntéseket nem lehet kiegészíteni egy felbontássá, vagy azért mert már nagyobb összeget választottunk, vagy azért mert minden további elemet kiválasztva sem érhetjük el E-t. Tehát LehetMego(i 1,...,i k ) = True akkor és csak akkor, ha k p i j E j=1 k p i j + j=1 n p j E. j=i k +1 Továbbá Megoldas(i 1,...,i k ) = True akkor és csak akkor, ha k j=1 p i j = E Kiskérdések 4
könyvelési módszer (1 példa) potenciál módszer (1 példa) Visszalépéses keresés keretalgoritmusa n királynő feladat a felhasznált függvények specifikációja a pénzváltási problémánál felhasznált függvények specifikációja 5