GRAFER Dagens plan: Avsluttende om grådige algoritmer Huffman-koding (Kapittel 10.1.2) Dynamisk programmering Floyds algoritme for korteste vei alle-til-alle (Kapittel 10.3.4) Ark 1 av 16 Forelesning 22.11.2004
Koding av tegn Det finnes mange standarder for koding av tegn. Noen av de viktigste er: Hollerith 12-bits hullkortkode Brukt på mekaniske datamaskiner før de elektroniske var oppfunnet BCD (Binary Coded Decimal) 4-bits kode brukt av IBM til koding av desimale sifre (BCD ble også brukt om 6-bits tegn på noen tidlige datamaskiner) EBCDIC (Extended Binary Coded Decimal Interchange Code) IBMs 8-bits utvidelse av BCD til fullt tegnsett ASCII (American (National) Standard Code for Information Interchange) 7-bits tegn. Unicode 16-bits tegn (Egentlig variabelt 8 32 bit) ISO-standard for alle tegn i alle språk Forelesning 22.11.2004 Ark 2 av 16
Huffman-koding Motivasjon Store tekstfiler tar stor plass, og det tar lang tid å overføre dem over nettet Tidligere var ASCII og EBCDIC de rådende standardene. Begge brukte 8-bits tegnkoder (ASCII brukte 7-bits kode + paritetsbit). Dette ble ansett som sløsing med plass Unicode bruker (stort sett) 16-bits koder, men 32-bits koder blir også brukt Unicode har 17 2 16 = 1 114 112 mulige tegn Selv i store tekstfiler blir bare noen få av disse brukt Unicode gir minst dobbelt så store filer som ASCII Konklusjon Det er behov for datakompresjon Forelesning 22.11.2004 Ark 3 av 16
Idé og regler Hovedidé: Ofte forekommende tegn skal ha korte koder, mens sjeldne tegn kan ha lange koder Regel 1: Hvert tegn som forekommer i filen, skal ha sin egen entydige kode Regel 2: Ingen kode er prefiks i en annen kode Eksempel på regel 2: Dersom 011001 er (binær)kode for et tegn, kan hverken 0, 01, 011, 0110 eller 01100 være kode for noe tegn Forelesning 22.11.2004 Ark 4 av 16
Algoritme Lag en frekvenstabell for alle tegn som forekommer i datafilen Betrakt hvert tegn som en node, og legg dem inn i en prioritetskø P med frekvensen som vekt Mens P har mer enn ett element Ta ut de to minste nodene fra P Gi dem en felles foreldrenode med vekt lik summen av de to nodenes vekter Legg foreldrenoden inn i P Huffmankoden til et tegn (bladnode) får vi ved å gå fra roten og gi en 0 når vi går til venstre og 1 når vi går til høyre Resultatfilen består av to deler: En tabell over Huffmankoder med tilhørende tegn Den Huffmankodede datafilen Forelesning 22.11.2004 Ark 5 av 16
Eksempel INF1020 Algoritmer og datastrukturer Initiell prioritetskø:! "# $ "% & ' ( ') * "% + "#, )' - "'. % /0 )' De to minste (sjeldnest forekommende) nodene er c og i. Disse taes ut og erstattes med T1:! "# $ "% ' () * "% 1" 2 1" + "#, )( - "( /0 )( &. Dernest erstattes T1 og a med T2:! "# % &' ( "# $' "0 $' ) "* + '&, "&./ '& $" - Forelesning 22.11.2004 Ark 6 av 16
Så går f og h ut. De erstattes av T3: ) &' " #$ % &' -$ &. -# ( $# +, $# -# $#! * Så erstattes b og e med T4: " #$ )$,- )* #+ )* % $# '( $# )# $#! & Dernest T2 og g med T5: # $"!) $+!( )*!( &' "$!$ "$!" % Forelesning 22.11.2004 Ark 7 av 16
Så byttes sp og T3 ut med T6: # $% &( $+ &' () &* (*!" &* &$ Så d og T4 med T7: "' #( "$ #$ "% $& "%! "# Så T5 og T6 med T8:!$ #%!& &$!&!"!# Endelig taes T7 og T8 ut av køen. Disse blir barn av rotnoden T9:!$!"!# Forelesning 22.11.2004 Ark 8 av 16
Det ferdige kodetreet ser slik ut:!$!#!"!%!& (!' -!, *+!). /!2 3 0 1 4 5 Det gir denne kodetabellen: a 1011 f 1110 b 010 g 100 c 10100 h 1111 d 00 i 10101 e 011 sp 110 Forelesning 22.11.2004 Ark 9 av 16
Eksempel: INF1020 Algoritmer og datastrukturer Kodetabell a 1011 f 1110 b 010 g 100 c 10100 h 1111 d 00 i 10101 e 011 sp 110 caddie hadde bad i hagebed har følgende Huffmankode: 10100101 10000101 01011110 11111011 00000111 10010001 10101011 10111110 11100011 01001100 Vi brukte altså 10 byte på 26 tegn med dette 10-tegns alfabetet. Dekodingstabell 00 d 10101 i 010 b 1011 a 011 e 110 sp 100 g 1110 f 10100 c 1111 h Forelesning 22.11.2004 Ark 10 av 16
Dynamisk programmering Brukes først og fremst når vi ønsker optimale løsninger Må kunne dele det globale problemet i delproblemer Disse løses typisk ikke-rekursivt ved å lagre del-løsningene i en tabell En optimal løsning på det globale problemet må være en sammensetning av optimale løsninger på (noen av) delproblemene Vi skal se på ett eksempel: Floyds algoritme for å finne korteste vei alle-til-alle i en rettet graf Forelesning 22.11.2004 Ark 11 av 16
Korteste vei alle-til-alle (Floyd) Vi ønsker å beregne den korteste veien mellom ethvert par av noder i en rettet, vektet graf Grunnleggende idé: Hvis det går en vei fra node i til node k med lengde ik, og en vei fra node k til node j med lengde kj, så går det en vei fra node i til node j med lengde ik + kj Floyds algoritme: Denne betraktningen gjentas på en systematisk måte for alle tripler i, k og j: Initielt: Avstanden fra node i til node k settes lik vekten på kanten fra i til k, uendelig hvis det ikke går noen kant fra i til k Trinn 0: Se etter mulige forbedringer ved å velge node 0 som mellomnode Etter trinn k: Avstanden mellom to noder er den korteste veien som bare bruker nodene 0, 1,..., k som mellomnoder Forelesning 22.11.2004 Ark 12 av 16
Floyds algoritme public static void kortesteveialletilalle(int[][] nabo, int[][] avstand, int[][] vei) { int n = avstand.length; // Forutsetning: // arrayene er kvadratiske // med samme dimensjon // Initialisering: for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { avstand[i][j] = nabo[i][j]; vei[i][j] = -1; // Ingen vei foreløpig for (int k = 0; k < n; k++) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (avstand[i][k] + avstand[k][j] < avstand[i][j]) { // Kortere vei fra i til j funnet via k avstand[i][j] = avstand[i][k] + avstand[k][j]; vei[i][j] = k; Tidsforbruket er åpenbart O(n 3 ) Forelesning 22.11.2004 Ark 13 av 16
Hvorfor virker Floyd? Floyd-invarianten: avstand[i][j] vil være lik lengden av den korteste veien fra node i til node j som har alle sine indre noder behandlet Behandlede noder 7 4 3 i 2 1 2 j 2 3 k FØR: A(i,j)=16 A(i,k)=5 A(k,j)=8 ETTER: A(i,j)=13...... Forelesning 22.11.2004 Ark 14 av 16
Hvordan tolke resultatet av Floyd Ved start inneholder nabo[i][j] lengden av kanten fra i til j, hvis det ikke er noen kant Floyd lar nabo være uendret og legger resultatet i avstand og vei avstand[i][j] er lengden av korteste vei fra i til j, hvis det ikke er noen vei vei sier hva som er den korteste veien k 1 = vei[i][j] er den største verdi av k slik at k ligger på den korteste veien fra i til j k 2 = vei[i][k 1 ] er den største verdi av k slik at k ligger på den korteste veien fra i til k 1 osv. Når vei[i][k m ]= 1,er(i,k m )den første kanten i korteste vei fra i til j Forelesning 22.11.2004 Ark 15 av 16
Hva gjør Floyd dynamisk? Hovedløkken i Floyd ser slik ut: for (int k = 0; k < n; k++) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (avstand[i][k] + avstand[k][j] < avstand[i][j]) { // Kortere vei fra i til j funnet via k avstand[i][j] = avstand[i][k] + avstand[k][j]; vei[i][j] = k; Algoritmeinvarianten forutsetter at i og j løkken fullføres før k verdien økes If-testen sikrer at for en gitt k kan hverken avstand[i][k] eller avstand[k][j] bli endret i i og j løkken Dermed er i og j løkkene uavhengig av hverandre og kan parallelliseres (de er delproblemer som kan løses uavhengig av hverandre) Dette er definisjonen på dynamisk programmering Forelesning 22.11.2004 Ark 16 av 16