Föreläsning 5 Val av algoritm och datastruktur Analys av algoritmer

Föreläsning 5
Val av algoritm och datastruktur
Innehåll
Algoritmer och effektivitet
Att bedöma och jämföra effektivitet för algoritmer
Det räcker inte med att en algoritm är korrekt – den måste även
producera resultat inom rimlig tid.
Begreppet tidskomplexitet
Både val av algoritm och datastruktur påverkar effektiviteten.
Undervisningsmoment:
föreläsning 5, övningsuppgifter 6
Vi måste kunna beräkna effektiviteten (tidsåtgången).
Avsnitt i läroboken:
2.4
I 2:a upplagan: 2.4
PFK (Föreläsning 5)
VT 2017
1 / 37
Analys av algoritmer
VT 2017
2 / 37
Tidsåtgång och problemets storlek
Effektivitet (tidsåtgång)
Tiden det tar att lösa ett problem är oftast en strängt växande
funktion av problemets storlek.
tidsskomplexitet - ett mått på hur exeveringstiden växer med
problemets storlek
Det tar längre tid att summera 100 000 tal än att summera 100 tal.
Minneskrav
Vissa enkla problem kan dock lösas på konstant tid oavsett storleken.
rumskomplexitet
Om vi t.ex. har en vektor med n tal kan vi alltid ta reda på det i:e talet
i vektorn på konstant tid oavsett hur stort n är.
Exempel:
int nbr = a[i];
Svårighetsgrad
Ju mer komplicerad desto svårare att översätta till program och
underhålla.
PFK (Föreläsning 5)
PFK (Föreläsning 5)
VT 2017
3 / 37
PFK (Föreläsning 5)
VT 2017
4 / 37
Tidskomplexitet
Beräkna tidskomplexitet
Räkna (ungefär) hur många basala operationer som utförs i
algoritmen.
Tidskomplexitet T(n) är ett mått på hur tidsåtgången växer med
problemets storlek n.
Med basal operation menas en operation som tar konstant tid på en
dator, t.ex. en aritimetisk operation eller en jämförelseoperation.
Tidskomplexiteten uttrycks som en funktion av problemets storlek.
Med tidskomplexitet kan vi jämföra olika algoritmers effektivitet.
Tidskomplexiteten uttrycks så att vi är oberoende av vilken dator som
exekverar algoritmen.
Vi behöver inte exekvera algoritmerna för att kunna jämföra deras
effektivitet.
låg tidskomplexitet () effektiv () billig, låg kostnad
hög tidskomplexitet () dålig effektivitet () dyr, hög kostnad
PFK (Föreläsning 5)
VT 2017
Exempel: Antag att vektorn a innehåller n tal som ska summeras.
for (int i = 0; i < a.length; i++) {
sum += a[i];
}
Satsen sum += a[i]; utförs n gånger och algoritmen får då
T (n) = n.
5 / 37
Tidskomplexitet och verklig tidsåtgång
PFK (Föreläsning 5)
VT 2017
6 / 37
Diskutera
Vilken tidskomplexitet får följande metod:
Vi har räknat ut att tidskomplexiteten T (n) för summeringen av n tal
är n (linjär tidskomplexitet).
Verklig tidsåtgång t(n) blir då c ⇤ n eller c ⇤ T (n), där c är
tidsåtgången för additionerna och tilldelningarna på den dator där
summeringen exekveras.
Exekveringstiden växer linjärt med antal element som ska summeras.
Vi har olika verklig tid på olika datorer, men tidskomplexiteten
beräknad på detta sätt är densamma för alla.
PFK (Föreläsning 5)
VT 2017
public static int max(int[] a) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < a.length; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
Antag att det vid en provkörning tog 17 ms att exekvera metoden med
en vektor med 1000000 tal. Uppskatta hur lång tid det skulle ta att
exekvera metoden om man fördubblar antal element i vektorn.
7 / 37
PFK (Föreläsning 5)
VT 2017
8 / 37
Beräkna tidskomplexitet
Diskutera
Exempel
Beräkna tidskomplexiteten för att summera talen i en kvadratisk matris m:
for (int i = 0; i < m.length; i++) {
for (int k = 0; k < m[i].length; k++) {
sum += m[i][k];
}
}
Metoden nedan undersöker om alla element i vektorn a är unika. Exempel:
Om a = {1, 4, 2, 9, 7} ska resultatet bli true.
Om a = {1, 4, 2, 9, 2} ska resultatet bli false.
public static boolean allUnique(int[] a) {
boolean unique = true;
for (int i = 0; i < a.length - 1; i++) {
for (int k = i + 1; k < a.length; k++) {
if (a[i] == a[k]) {
unique = false;
}
}
}
return unique;
}
Antag att matrisen innehåller n x n element.
De satser som utförs flest gånger är de som finns i den innersta
for-loopen.
Satsen sum += m[i]; utförs n * n gånger och algoritmen får då
T (n) = n2 (kvadratisk tidskomplexitet).
Tidskomplexitet?
PFK (Föreläsning 5)
VT 2017
9 / 37
PFK (Föreläsning 5)
Tidskomplexitet
Aritmetisk summa
Undersök om alla element är unika
Repetition från matematiken
i-värde för yttre for-loopen
0
1
...
n–4
n–3
n–2
Antal ggr satserna i inre for-loopen utförs
n-1
n-2
...
3
2
1
D.v.s. T (n) = n2 /2
PFK (Föreläsning 5)
1 = n(n
10 / 37
Antag att skillnaden mellan två på varandra följande tal i en följd är
konstant. Då kan vi beräkna summan av talen genom formeln:
a1 + a2 + a3 + . . . + an = n(a1 + an )/2
Då blir
1 + 2 + 3 + . . . + n = n(1 + n)/2
och
Totalt antal gånger satserna i inre for-satsen utförs är:
1 + 2 + 3 + ... + n
VT 2017
1 + 2 + 3 + ... + n
1)/2
1 = (n
1)(1 + n
1)/2 = (n
1)n/2
n/2 vilket är ⇡ n2 /2 för stora n-värden
VT 2017
11 / 37
PFK (Föreläsning 5)
VT 2017
12 / 37
Förbättrad algoritm
Tidskomplexitet
Undersök om alla element är unika
Undersök om alla element är unika
Vi bör avbryta algoritmen så fort vi hittat en dubblett:
För (den oförbättrade) algoritmen fick vi fram uttrycket
T (n) = n(n 1)/2 = n2 /2 n/2 för tidskomplexiteten.
public static boolean allUnique(int[] a) {
for (int i = 0; i < a.length - 1; i++) {
for (int k = i + 1; k < a.length; k++) {
if (a[i] == a[k]) {
return false;
}
}
}
return true;
}
För stora värden på n dominerar den kvadratiska termen.
T (n) ⇡ n2 /2 för stora värden på n.
Nu blir det svårare att beräkna exakt hur många gånger jämförelsen (a[i]
== a[k] utförs. Det beror på indata. Enklast är att räkna på värsta fallet
som här inträffar då inga dubbletter finns. Vi får samma tidskomplexitet
som för den oförbättrade algoritmen.
PFK (Föreläsning 5)
VT 2017
13 / 37
Uttrycka tidskomplexitet med hjälp av Ordo
T (n) = n(n
45
4 950
499 500
49 995 000
1)/2
n2 /2
50
5 000
500 000
50 000 000
T (n)/(n2 /2)
0.9
0.99
0.999
0.9999
PFK (Föreläsning 5)
VT 2017
14 / 37
Ordobegreppet
För att uttrycka hur en algoritms tidskomplexitet uppför sig vid stora
värden på n kan vi använda Ordo-notation.
Beräkna tidskomplexiteten genom att räkna hur många gånger de
dominerande operationerna utförs.
Exempel: T (n) = n2 /2 n/2
|T (n)|  cf (n) för alla n > n0
För alla polynom T(n) av grad k:
Detta ger en övre gräns för tidskomplexiteten. T (n) = O(n2 ) betyder
att för stora värden på n understiger den verkliga exekveringstiden cn2 .
VT 2017
Ordo-begeppet från matematiken ger oss möjlighet att begränsa en
funktion ”uppåt”.
En funktion T (n) är O(f (n)) (uttalas ”T är ordo f”) om det finns
konstanter n0 och c så att
Stryk sedan allt utom den dominerande termen. Stryk även ev.
konstanter på den dominerande termen.
Exempel: T (n) = O(n2 )
PFK (Föreläsning 5)
n
10
100
1 000
10 000
15 / 37
T (n) = ak ⇤ nk + ak
1
⇤ nk
1
+ ...
gäller att de är O(nk ).
PFK (Föreläsning 5)
VT 2017
16 / 37
Tidskomplexitet
Ordobegreppet
Vi har tidigare räknat fram T (n) genom att beräkna hur många
gånger de ”dominerande” operationerna utförs.
I komplexitetsberäkningar förutsätts att man inte ger en större övre gräns
än nödvändigt:
För att ange storleksordningen på T (n) med ordonotation kan allt
utom den dominerande termen strykas.
Om T (n) = 2n2 + n är T (n) = O(n2 )
T (n) = dominant term + mindre termer =) T (n) = O(dominant
term).
T (n) = 2n2 + n =) T (n) = O(2n2 )
men det är även sant att T (n) = O(n3 ) ellerO(n4 ) eller . . .
Eventuell konstant på den dominerande termen kan också strykas.
T (n) = O(2n2 ) =) T (n) = O(n2 )
PFK (Föreläsning 5)
VT 2017
17 / 37
Asymptotisk tidskomplexitet
PFK (Föreläsning 5)
VT 2017
18 / 37
Diskutera – tidskomplexitet och verklig tidsåtgång
För stora värden på n gäller att verklig tidsåtgång t(n) = c ⇤ T (n)
När man i stället för att ange ett exakt uttryck på tidskomplexiteten
använder ordonotation brukar man tala om algoritmens asymptotiska
tidskomplexitet.
Problem: En algoritm har tidskomplexiteten T (n) = O(n2 ). Antag att då
man undersöker om elementen i en vektor med 10000 element är unika får
en exekveringstid på 20 ms. Ungefär hur lång tid tar det att exekvera
programmet för n = 1000000?
Man anger ju hur den asymptotiskt uppför sig för stora värden på n
(problemets storlek).
PFK (Föreläsning 5)
VT 2017
19 / 37
PFK (Föreläsning 5)
VT 2017
20 / 37
Storleksordning för funktioner
Tidskomplexitet för några algoritmer
När en algoritm har tidskomplexiteten T(n) = O(1), så säger man att
tidskomplexiteten är konstant. På samma sätt används namn på vanligt
förekommande storleksordningar för T(n) = O(f(n)), enligt tabellen:
f(n)
1
logn
n
nlogn
n2
n3
2n
Sätta in ett element först i en lista med n element: O(1)
Söka med binärsökning bland n sorterade element: O(logn)
Söka bland n osorterade element: O(n)
namn
konstant
logaritmisk
linjär
log-linjär
kvadratisk
kubisk
exponentiell
PFK (Föreläsning 5)
Sortera med bättre metoder som Mergesort, Heapsort och Quicksort
(som kommer senare i kursen): O(nlogn)
Sortera n tal med urvalssortering, bubbelsortering eller
insättningssortering: O(n2 )
Multiplicera två n x n-matriser med den vanliga metoden: O(n3 )
Vissa dekrypteringsalgoritmer (n=antal bitar i nyckeln): O(2n )
VT 2017
21 / 37
Tidskomplexitet – olika storleksordningar i praktiken
PFK (Föreläsning 5)
t = 1 sekund
1 000 000
62 746
1 000
100
19
t = 24 timmar
86.4 ⇤ 109
2.8 ⇤ 109
293 938
4 421
36
t = 1 år
31.5 ⇤ 1012
8 ⇤ 1011
5.6 ⇤ 106
31 593
44
VT 2017
VT 2017
22 / 37
Tidskomplexitet kan bero på indata
Största värde på n för exekveringstiden t om den dominerande operationen
tar 1/1 000 000 sek:
T(n)
n
nlogn
n2
n3
2n
PFK (Föreläsning 5)
/** Undersök om talet x finns i heltalsvektorn a. */
public static boolean contains(int[] a, int x) {
for (int i = 0; i < a.length; i++) {
if (a[i] == x) {
return true;
}
}
return false; // kommer vi hit fanns ej x i vektorn
}
Det som görs inuti for-satsen dominerar.
Komplikation: Går inte att säga exakt hur många gånger den utförs.
Det beror på indata.
23 / 37
PFK (Föreläsning 5)
VT 2017
24 / 37
Tidskomplexitet – värsta fall och medelfall
Värsta fall och medelfall – formella definitioner
För algoritmer vars tidskomplexitet beror på indata brukar man ställa
två frågor:
Om det finns k olika instanser av storlek n och tidskomplexiteten för den i:e
instansen är Ti (n) och sannolikheten för denna instans är Pi gäller:
Vad är den i värsta fall för instanser av storlek n?
Vad är den i medeltal för alla instanser av storlek n?
W (n) = maxTi (n)
X
A(n) =
Pi ⇤ Ti (n)
Tidskomplexitet i värsta fall för instanser av storlek n brukar
betecknas W (n). W står för Worst case.
W (n) är ofta lika lätt att beräkna som T (n).
Tidskomplexitet i medelfall för instanser av storlek n brukar betecknas
A(n). A står för Average.
PFK (Föreläsning 5)
VT 2017
A(n) ofta svårare. För en del algoritmer okänt på grund av svårigheten
att ge värden åt sannolikheter för de olika instanserna av storlek n.
25 / 37
Värsta fall för linjärsökning
PFK (Föreläsning 5)
VT 2017
26 / 37
Medelfall för linjärsökning
Komplikation: Det finns ett oändligt antal instanser av storlek n.
Om vi antar följande villkor kan vi enkelt beräkna medelfallstiden:
Problemets storlek = vektorns längd. Kalla denna n, d.v.s.
n = a.length.
1
x finns i vektorn.
I värsta fall blir jämförelsen alltid false och for-satsen utförs då för alla
i-värden från 0 till och med n–1.
2
Det är lika sannolikt att x finns på var och en av platserna.
D.v.s. sannolikheten är 1/n att x finns på plats i för i = 0, 1, . . . , n–1.
Satserna inuti for-satsen utförs då n gånger.
3
Alla elementen i vektorn är olika.
Alltså: W (n) = n = O(n).
PFK (Föreläsning 5)
Vi har då minskat antal möjliga instanser av problemet till n:
x finns på plats 0, x finns på plats 1, ..., x finns på plats n-1.
VT 2017
27 / 37
PFK (Föreläsning 5)
VT 2017
28 / 37
Medelfall för linjärsökning
Tidskomplexitet för algoritmer som anropar operationer
De flesta algoritmer uttrycker sig inte enbart med basala O(1)-operationer
som +, – , . . . utan unyttjar mera kraftfulla operationer.
i
0
1
...
i
...
n–1
A(n) =
X
instans
x == a[0]
x == a[1]
...
x == a[i]
...
x == a[n–1]
pi
1/n
1/n
...
1/n
...
1/n
Ti (n)
1
2
...
i+1
...
n
Exempel: Sortera en vektor och undersök sedan om alla element är unika.
pi ⇤ Ti (n) = (1 + 2 + ... + n) ⇤ 1/n = (n + 1)/2 = O(n)
public static boolean allUnique(int[] a) {
Arrays.sort(a);
for (int i = 0; i < a.length - 1; i++) {
if (a[i] == a[i + 1]) {
return false;
}
}
return true;
}
Tidskomplexitet?
PFK (Föreläsning 5)
VT 2017
29 / 37
Tidskomplexitet för algoritmer som anropar operationer
Metoden sort innehåller en effektiv sorteringsalgoritm med
T (n) = O(nlogn).
1) ⇤ O(1) = O(n).
Sammanlagd kostnad för algoritmen blir O(nlogn) + O(n).
T (n) = O(nlogn)
VT 2017
anropar operationerna o1 , o2 , ..., ok .
Operationen oi anropas mi gånger.
Operationen oi har värstafalltidskomplexiteten Wi (n).
Då blir kostnaden för algoritmen i värsta fall:
m1 ⇤ W1 (n) + m2 ⇤ W2 (n) + ... + mk ⇤ Wk (n)
D.v.s. den operation som anropas minst antal gånger dominerar i detta fall
och bestämmer tidskomplexiteten.
PFK (Föreläsning 5)
30 / 37
Antag att en algoritm
Algoritmen gör ett anrop av metoden sort som inte ärO(1) och gör i
värsta fall n 1 jämförelser som är O(1).
1 jämförelserna kostar sammanlagt (n
VT 2017
Tidskomplexitet för algoritmer som anropar operationer
Antag att vektorn innehåller n element.
De n
PFK (Föreläsning 5)
31 / 37
Deltermer som inte dominerar detta uttryck kan sedan strykas om man
bara vill ha en ordo-uppskattning.
PFK (Föreläsning 5)
VT 2017
32 / 37
Tidskomplexitet för algoritmer som anropar operationer
Tidskomplexitet för algoritmer som anropar operationer
Exempel: Antag att en vektor a innehåller n tal. I följande algoritm läggs
alla unika tal från a in i en lista.
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < a.length; i++) {
if (! list.contains(a[i])) {
list.add(a[i]);
}
}
for (int i = 0; i < a.length; i++) {
if (! list.contains(a[i])) {
list.add(a[i]);
}
}
Antag att n = a.length. I värsta fall innehåller listan då n element.
Inuti contains söks i värsta fall alla element igenom. Denna sökning
har tidskomplexiteten O(n). De n anropen av contains kostar då
O(n2 ).
I add läggs elementet till sist. Denna operation har tidskomplexiteten
O(1). I värsta fall anropas add n gånger vilket kostar O(n).
Vad får algoritmen för tidskomplexitet?
Sammanlagd kostnad för algoritmen blir O(n2 ) + O(n) vilket innebär
att T (n) = O(n2 ).
PFK (Föreläsning 5)
VT 2017
33 / 37
Bättre datastruktur för att lagra en samling unika element
PFK (Föreläsning 5)
VT 2017
34 / 37
Diskutera
Vad får följande algoritm för tidskomplexitet?
En mängd (eng. Set) passar bättre än en lista om man ska lagra unika
element.
List<Integer> list = new LinkedList<Integer>();
...
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
Set<Integer> list = new HashSet<Integer>();
for (int i = 0; i < a.length; i++) {
list.add(a[i]);
}
Ovanstående sätt att iterera genom en länkad lista är ineffektivt. Istället
ska man använda en iterator. Vilken tidskomplexitet blir det då?
Dubblettkontroll sker inuti add. Metoden add i klassen HashSet har i
medelfall (och i praktiken) tidskomplexiteten O(1).
for (int nbr : list) {
System.out.println(nbr);
}
Mängder och klassen HashSet behandlas senare under kursen.
PFK (Föreläsning 5)
VT 2017
35 / 37
PFK (Föreläsning 5)
VT 2017
36 / 37
Algoritmer och effektivitet
Exempel på vad du ska kunna
Förklara begreppet tidskomplexitet.
Beräkna tidskomplexiteten T (n) för enkla algoritmer (av den
svårighetsgrad som behandlats på föreläsningar och övningar).
Uttrycka tidskomplexitet med O (ordo) och förklara vad det innebär
att tidskomplexiteten är O(f (n)).
Förstå vad olika storleksordningar på tidskomplexiteten innebär för
algoritmers användbarhet (konstant, logaritmisk, linjär, kvadratisk,
exponentiell,...).
Förstå och kunna använda sambandet mellan verklig tidsåtgång och
teoretiskt beräknad tidskomplexitet, t(n) ⇡ c ⇤ T (n).
Avgöra när en algoritms tidskomplexitet beror på indata och i sådana
fall kunna beräkna tidskomplexitet för värsta fall, W (n).
Kunna förklara vad som menas med tidskomplexitet i medelfall, A(n),
för algoritmer vars tidskomplexitet beror på indata.
PFK (Föreläsning 5)
VT 2017
37 / 37