Algoritmusok és adatszerkezetek 2 2015/16 tavaszi félév Előadó: Dr. Ásványi Tibor Készítették: Koruhely Gábor (Koru) Szalay Richárd (Whisperity) Lektorálta: Dr. Ásványi Tibor Frissítve: 2016. 06. 18.
12 EA első blokk: 10:15 - 11:00 szünet: 11:00 – 11:15 második blokk: 11:15 - 12:00
Rendezés lineáris időben Edényrendezés (bucket sort) Tételezzük fel, hogy a kulcsok: [0,1) intervallumba esnek és egyenletesen oszlanak el. Példa számok: 0,32; 0,81; 0,89; 0,17; 0,53 (n = 5) a „k” kulcsot ⌊k ∗ n⌋ edénybe tesszük 0.edény: 0,17 1.edény: 0,32 2.edény: 0,53 3.edény: 4.edény: 0,81; 0,89 az eredmény: 0,17; 0,32; 0,53; 0,81; 0,89 Bucket_sort(&S; n) E[0..n-1] létrehozása üresen nincs vége S-nek x:= S következő eleme Θ(n) i:= ⌊x. kulcs ∗ n⌋ x-et beszúrjuk E[i]-be i = 0..n – 1 mT(n) ∈ Θ(n) Pl.: beszúró rendezések E[i] rendezése MT(n) ∈ Θ(n2 ) S := <> i = 0..n – 1 Θ(n) S végéhez hozzáfűzzük E[i] tartalmát azonban hiába mehet n2 -ig a műveletigény, a legtöbb esetben (a legtöbb inputra) marad az átlagos n-es műveletigény
1
Leszámláló rendezés (Counting sort) Most tegyük fel, hogy a kulcsok [0..k]-ba esnek, k ∈ Ο(n) (azaz k legfeljebb n-nel lineáris) φ: kulcsfüggvény φ: T ⟶ [0;k]
A[1..n]: T C[0..k]: 0..n
négyes számrendszer-beli számok
Példa: (k = 3), a számok: 312, 213, 021, 222, 323, 331 (a 0. lépés az init) C[] adottodik elemkor h 0. 1. 2. 3. 4. 5. 6. végén 0 0 1 1 1 0 0 ennyi ilyen kulcsú 2 0 1 2 2 elem van φ szerint 3 0 1 2 3 3 Counting_sort(A[1..n], k, φ, B[1..n])
most már a bal szélső számjegy szerint is rendezett a tömb; a többi számjegy szerinti, most már másodlagos rendezettséget megtartotta.
2
Radix (számjegypozíciós) rendezés Radix(n elem, d számjegy, (szjegyek 0..k)) i := 1..d rendezzük az elemeket stabil rendezéssel hátulról i-edik számjegyük szerint Stabil rendezés: amely az elemeknek, egyenlő kulcsok esetén, az egymáshoz viszonyított sorrendjét nem változtatja meg (pl Counting_sort ilyen).
Tradix ∈ Θ( d ∗ Tstabil (n))
TRadix rendezés tömbökre ∈ Θ(d ∗ (n + k)) = Θ(n)
Tcounting (n, k) ∈ Θ(n + k)
ha d konstans és 𝑘 ∈ Ο(𝑛) volt Szétfűz sorrendtartó módon
Radix rendezés láncolt listákra, stabil edényrendezéssel, jobbról az i.-edik számjegy szerint (i:= 1,..,d)
Szétfűz sorrendtartó módon összefűzés után sorban:
összefűzés után sorban: T𝑟𝑎𝑑𝑖𝑥/𝑒𝑑é𝑛𝑦𝑟𝑒𝑛𝑑𝑒𝑧é𝑠 (n, k) ∈ Θ(d ∗ (n + k)) = Θ(n) ha d konstans és 𝑘 ∈ Ο(𝑛)
3
102 EA tárgy követelményei: Legalább elégséges gyakorlati jegy kell a vizsgázáshoz, vizsga 3szor lehet maximum vizsgázni egy vizsgaidőszakban, vizsga ugyanolyan felépítésű, mint előző félévben az algo 1 tematika: A tárgy honlapján megtalálható a tárgy tematikája: http://aszt.inf.elte.hu/~asvanyi/ad/ad2programok.pdf 8tétel nem összehasonlító rendezésekről lesz szó, hasítótáblák gráf algók o legrövidebb út o minimális feszítőfa veszteségmentes tömörítés mintaillesztés
Hasítótáblák (Hash tables) Hasonlóan a kiegyensúlyozott fákhoz cél lenne a 3 alapművelet (keresés, beszúrás, törlés) optimálisan fusson le. átlagos esetben Θ(1), legrosszabb esetben Θ(n). Direkt – címzés (Direct addressing): a lehetséges kulcsok az U = {0,...,m-1} (m nem túl nagy pozitív egész) univerzumba esnek műveletei: beszúr; keres; töröl Beszúr, (T[0..m-1], p)
fv.Keres (T[0..m-1], k)
Töröl (T[0..m-1], p)
T[p->kulcs] := p
return(T[k])
T[p->kulcs] := ∅
A tárigény miatt problémás a módszer, pl. ha a személyi szám szerint így tárolnánk a magyarokat, akkor a <10millió embernek 75 millió hely kellene, nagyon sok az üres hely. (n a tárolt adatok száma)
h: kulcsuniverzumból résekre (slotokra) képez le. (|U| >> m /*>> := sokkal nagyobb*/), (m ∈ Ο(n))
h: U[0..m-1] h hasító függvény
A baj az, hogy hiába tesszük a hash szerinti helyre a kulcsokat, előbb, utóbb lesz két külön kulcs, aminél h(k) = h(k’). Ekkor kulcsütközés következik be, ez célszerűen láncolt listával elkerülhető. Láncolt listás esetben viszont MTkeres(n) ∈ Θ(n); mTkeres (n) ∈ Θ(1); ATkeres (n) ∈ Θ(1 + α), a törlés műveletigénye ugyanaz, mint a keres egyes eseteiben. a minimális akkor lehet, ha a réshez tartozó lista első elemét keressük, vagy a lista üres. a listák átlagos hossza a hashtábla kitöltöttségi hányadosa, α =
n m
Ha nem ellenőrizzük az esetleges duplikált kulcsokat, a beszúrás igénye Θ(1). Ha nem engedjük meg a duplikált kulcsokat, ugyanaz, mint a keresés-nél. Egyszerű egyenletes hasítás: minden slotra ugyanakkora a h leképezési valószínűsége. Ilyen hash-ek megadhatók, de elég komoly matematika van mögötte. Osztómódszer: h(k) = k mod m, ahol m olyan prím amely messze van a 2-hatványoktól Ha a kulcsok a [0,1) intervallumon egyenletesen oszlanak el, akkor a h(k) = ⌊k ∗ m⌋ is jó hashfüggvény Szorzómódszer:
0 < A < 1 konstans h(k) = ⌊{k ∗ A} ∗ m⌋
({x} a törtrész fgv.)
(√5−1)
(különböző vizsgálatok szerint A = ≈ 0,618 …. jól szórja szét a kulcsokat. 2 Ezt a kiszámítást azonban nehéz elvégezni a lebegőpontos aritmetika miatt. 4
m = 2p a hashtábla mérete w = 32 k ∈ 0 … 2w − 1; legyen, 0 < p < w
bitjének kiválasztása
A kulcsütközések feloldására a láncolt lista helyett vannak más módszerek is. Nyílt címzés: h: U × [0 … m − 1] ⟶ [0 … m − 1] Most az egyszerűség kedvéért nem ellenőrizzük a duplikált kulcsokat. k kulcshoz m darab hash-érték fog tartozni: próbasorozat A műveletek a próbasorozaton mennek végig: szabad
Pl: beszúrásnál ha h(k,0)-hoz van foglalt elem akkor h(k,1)-re szúr be, feltéve ha az már ⏞ üres vagy törölt fv.Beszúr(T[0..m-1],p) i := 0 i<m j := h(p.kulcs,i) T[j] ∈ {⊘, ⨂} T[j] := p i := i+1 return (j) return ∅ ahol:
A próbasorozatnak a <0,1,… ,m-1> permutáltjának kell lennie A próbasorozatok egyik előállítási módja pl. a lineáris próbálás: h(k,i) = (h’(k) + i) mod m
fv.Keres(T[0..m-1],k) i :=0 l:= igaz l j:= h(k,i) T[j].kulcs = k return(j) i := i+1 l:= ( i < m ∧ T[j] ≠ ⊘) return ∅ Feltesszük, hogy ⊘.kulcs, ⨂.kulcs ∉ U
k∈U
5
112 EA Hasítótáblák Hasítótáblák tulajdonságai: egy szótárt valósítanak meg kulcs alapján lehet benne keresni törölni is lehet benne átlagos műveleti igény konstans a tábla (ism.): egy tömbnek lehet tekinteni; 0..m-1-ig indexelve van speciálisan a nyílt címzésnél az adatokat slot-okban tároljuk; megkülönböztetjük a törölt (⨂) illetve az üres (⊘) slotokat sikertelen a keresés, ha üres slothoz ér, vagy ha túl sokat próbálkozik (m db próba után megáll) valójában nem egy hash függvény van, hanem m db hash függvény van
Tipikusan foglalt rések hosszú összefüggő szakaszain alakulnak ki T[0..m-1]-ben elsődleges csomósodások.
feltehetjük, hogy van m db hash függvény h: ( . , i) : U 0..m-1 ( i ∈ 0..m-1) perm <0, … , m-1> lineáris próba: h(k,i) = (h’(k) + i) mod m négyzetes próba: h(k,i) = (h’(k) + c1 ∗ 𝑖 + c2 ∗ 𝑖 2 ) mod m /*ahol h’(k) = h(k,0)*/ 1 ahol jól bevált ajánlás, hogy m 2-hatvány, c1 , c2 pedig (m = 2𝑝 , 0 < 𝑝 ∈ ℤ) i∗(i+1)
j∗(j+1)
2
i ≠j: (h(k,i) – h(k,j)) mod m = [ – ] (mod m ) ≠ 0 ⟺ 2 2 2m ∤ i ∗ (i + 1) – j ∗ (j + 1) = i2 + i – j2 – j = i2 – j2 + (i – j) = (i – j)(i + j + 1) 2p+1 ∤ (i − j)(i + j + 1) ezek párossága különbözik a) 2 | (i – j) ⇒ 2 ∤ ( i + j + 1) de 2m ∤ (i – j) hiszen i - j < 2m-1 b) 2 | ( i + j + 1) ⇒ 2 ∤ (i – j) de 2m ∤ (i + j + 1)-nek, mivel (i + j + 1) < 2m – 1 azaz 2-hatvány tábla esetén a négyzetes próba szépen lefedi az egész táblát (i + 1) ∗ (i + 2) i ∗ (i + 1) (h(k, i + 1) − h(k, i)) mod m = [ − ] mod m = (i + 1) mod m 2 2 azaz: h(k, i + 1) = (h(k, i ) + (i + 1)) mod m és h(k,0) = h’(k) feltesszük: törölt(⨂) slot kulcsa és az üres(⊘) slot kulcsa nem eleme az U-nak fv.beszúr(T[0..m-1], k) j:= h’(k) i := 0 i<m T[j] ∈ {⊘,⨂} T[j] := k return(j) i := i + 1 j := ( j + i) mod m return -1 nyílt címzés, négyzetes próba és ⊘.kulcs és ⨂.kulcs ∉ kulcsuniverzum az általános négyzetes próbánál (ahol c1 és c2 tetszőleges) ha h(k1 , 0) = h(k 2 , 0), akkor ∀i − re h(k1 , i) = h(k 2 , i)
fv.keres(T[0..m-1], k) j:= h’(k); i := 0; b:= true b T[j].kulcs = k return j i := i + 1 b := T[j] ≠⊘ és i < m j:= (j + i) mod m return -1 Megjegyzés: ha T[j] NIL, akkor T[j].kulcs olyan extremális érték, amely bármilyen összehasonlításra hamisat ad
Másodlagos csomósodás Tehát csak m különböző próbasorozat van a lehetséges m! közül, így a (másodlagos) csomósodás fellép, de ez jobb, mint a lineáris próba elsődleges csomósodása, ahol a sorozatok menet közben gabalyodhatnak össze… 6
Kettős hasítás h(k, i) = [ h1 (k) + i ∗ h2 (k)] mod m a próbasorozatok száma itt már Θ(n2 ) szemben Θ(n)-nel. ez – tapasztalat alapján – már majdnem ideális tud lenni ha lnko(h2 (k), m) = 1 ⇒ perm <0, … , m-1>-nak hiszen i ≠ j ⇒ (h(k,i) – h(k,j) mod m ≠ 0 0 ≠ (h(k,i) – h(k,j)) mod m ⟺ (i – j) * h2 (k) mod m ≠ 0 ⟺ m ∤ (i – j) * h2 (k) mivel |i – j| < m, h2 (k) pedig relatív prím (ez feltétel volt, hogy lehet biztosítani, hogy a h2 (k) és az m relatív prímek legyenek). a) m legyen prím, akkor h1 (k) ∶= k mod m h2 (k)=1 + (k mod m’) ahol: m’= m- 1 v m’= m – 2 például, azaz m-nél picit kisebb p (k) b) m = 2 és 2 ∤ h2 Hashelés Ideális esete: Egyenletes hasítás ideális lenne, ha <0..m-1> összes permutáció azonos valószínűséggel fordulna elő 0 < α telítettségi együttható < 1 esetén 1 sikertelen keresés várható hossza ≤
beszúrás várható hossza ≤
1 1−α
1−α
Feltéve, hogy nincs a hasító táblában törölt rés (slot). 1 α
1 ∗ ln ( ) 1−α
sikeres keresés várható hossza ≤ Ha sokat használjuk a hashtáblát, akkor feltöredezik, elfogynak az üres slotok, ekkor frissítést kell végrehajtani: beszúrásokkal új táblát létrehozni a mostani elemekből.
Gráf algoritmusok Szomszédosági mátrix (C) G= (V,E) E ⊆ V × V V = 1..n C[1..n,1..n] 1 ⟺ (i,j) ∈ E C[i,j] = 0 ⟺ (i,j) ∉ E ha számít az él költsége w(i,j) ⟺ (i,j) ∈ E C[i,j] = 0⟺i=j +∞ különben
w: E ℝ (i ≠ j)
Azonban a mátrix tárolás miatt a műveletigény Θ(n2 ), ami az irányítatlan esetben a szimmetria kihasználásával javítható (majdnem felezhető), de Θ(n2 ) marad (csak alsó háromszög mátrixot tároljuk).
Szomszédsági éllistás reprezentáció: Memóriaigény n csúcs és e él esetén: Θ(n + e)
7
1002 EA Elemi gráf algoritmusok Szélességi keresés Meghatározzuk a start csúcsból a további csúcsokba a legkevesebb élet tartalmazó utat. d – hány élen keresztül jutunk a csúcsba π – melyik csúcsból jutunk a csúcsba Q = 1. lépés: Q= /*b szomszédai*/ 2. lépés: Q= /*c rákövetkezője e, de ott már voltunk*/ 3. lépés: Q= <e, a> /*„a” d szomszédjai*/ 4. lépés: Q= /*e szomszédja csak a d, de ott már voltunk*/ 5. lépés: Q=<> /*a sor kiürült, a bejárás véget ért