Algoritma Karmaşıklığı Analizi
Veri Yapıları
İTÜ, BLG221 Veri Yapıları
Veri Yapıları ve Algoritma Analizi
• Veri Yapıları:
– Verinin bellekte en etkin nasıl depolanacağı, erişileceği ve yönetileceği konularını inceler.
– Hangi veri yapısının kullanılacağı, bir algoritmanın performansını etkiler.
• Algoritma Analizi:
– Bir algoritmanın performansını tahmin eder.
– Bir problem için farklı algoritmaların nasıl karşılaştırılacağı konularını inceler.
2
Algoritma Performansı
• Performans değerlendirme metrikleri:
– Gerekli bellek alanı – Çalışma süresi
• Çalışma süresi en çok kullanılan standartdır.
– Ölçülebilir ve karşılaştırması kolay
– Genellikle performans, girdi sayısının bir fonksiyonudur:
• Örnek: Veri elemanı sayısı
3
Çalışma Süresi Karmaşıklığının Ölçülmesi
• Genellikle çalışma süresi tahmini, tam olarak zaman birimi cinsinden yapılmaz.
(örn. Bir algoritmanın toplam kaç mili saniyede tamamlanacağı)
• Bunun yerine, bir algoritmanın hızının problemin girdi sayısına göre değişimi tahmin edilir.
(örn. İşlem sayısı).
• Problemin boyutuna (N) bağlı olan bir f(N) fonksiyonu
tanımlanır, bu fonksiyon algoritmanın çalışma süresi ile doğru orantılıdır.
4
• Aşağıdaki üç algoritmanın (pseudo code) her biri için, toplam işlem sayılarını, N’in bir fonksiyonu olarak hesaplayalım.
Örnek : T(N) hesaplanması
• Toplam işlem sayılarının grafikleri:
B Algoritması, çalışma süresi açısından en fazla zamanı harcar.
Algoritma T(N) O( f(N) )
A 2N+1 O(N)
B N2+N+1 O(N2)
C 4 O(1)
•T(N) algoritma Çalışma Süresi’dir.
•O(f(N)) ise, Asimptotik Algoritma Karmaşıklığıdır.
• Aşağıdaki C fonksiyonu için toplam işlem sayısını hesaplayalım.
Örnek : C kodunundan T(N) hesaplanması
float Avg(int A[], int N) {
float ort, tot=0;
int i;
for (i=0; i < N; i++) tot = tot + A[i];
if (N > 0)
ort = (float)tot/N;
return ort;
} 1
2 3 4 5 6
Satır İşlem Maliyet Tekrarlar
1 tot=0 1 1
2 i=0 1 1
2 i < N 1 N+1
2 i++ 1 N
3 tot = tot + A[i] 1 N
4 N > 0 1 1
5 (float) tot 1 1
5 … / N 1 1
6 return ort 1 1
Toplam T(N) = 3N+7
Algoritma Karmaşıklığı:
Büyük-O Notasyonu
• Bir algoritmanın karmaşıklığı (Asimptotik Karmaşıklık) genellikle, bir fonksiyonun icrası için gerekli işlem
sayısının çarpanlarından oluşan terimler halinde ifade edilir.
– Büyük O harfi ile gösterilir (Order of )
– Parantez içinde ise problem boyutu N’a bağlı bir ifade yazılır.
• Büyük-O bir algoritmanın nümerik olarak kaç mili saniye süreceğini göstermez.
– Girdi sayısı (N)’ e bağlı olarak değişim oranını gösterir.
T(N) ve O(f(N)) Örnekleri
Çalışma süresi T(N)
Asimptotik Karmaşıklık O(f(N))
4 N log N O(N log N)
7N2 log N + 5N2 + N O(N2 log N) 10 N3 + N2 + 40N + 800 O(N3)
2N + 1000N O(2N)
9
Analiz Kuralları
• Aşağıdaki işlemler sabit çalışma süresine sahiptir O(1):
– Aritmetik (topla, çıkar, çarp, vb.) – Veri hareketi (oku, yaz, ata)
– Kontrol (dallanma, alt program çağrısı, return)
• Döngüler sabit olmayan çalışma süresine sahiptir, O(N), O(N2), vb.
– Döngü sayısı ile, döngü içindeki işlemlerin toplam çalışma süresi çarpılır.
• Ardışık komutlar
– Ardışık olarak birçok komut varsa, çalışma süresi en yüksek maliyete sahip olan komutlar O()’yu belirler.
Sadeleştirme Kuralları
• Mümkünse sadece bir terime indirgeyin.
– En hızlı büyüyen terim, toplam çalışma süresini belirleyicidir.
– N sayısı arttıkça, asimptotik sonuç tamamen derecesi en büyük olan terim tarafından belirlenir.
• Sabit katsayılar atılır.
O(aNk + bNk-1 + … + yN + z) ≈ O(Nk) O(N) + O (N) = 2 O(N) ≈ O(N) O(N) + O (M) ≈ O(N) Eğer N > M
O(N) * O (N) = O(N2)
O(N) + O (logN) ≈ O(N) Çünkü O(N) > O(logN)
O(N) * O (logN) ≈ O(N logN)
11
Matematik İfadeler (1)
• Üsler
XA XB = XA+B XA / XB = XA-B (XA)B = XAB
XN+XN = 2XN 2N+2N = 2N+1
• Logaritmalar
Tanım: XA = B ise logX B = A log AB = log A + log B
log A/B = log A – log B log (AB) = B log A
Matematik İfadeler(2)
• Seriler
1 2
2
10
N N ii
2 2
) 1
(
21
N N
i N
N
i
1
1
1
0
Na a a
N ii
3 6
) 1 2
)(
1
(
31
2
N N N N
i
N
i
13
Karmaşıklık Sıralaması
Sabit O(1)
Logaritmik O(log N) Doğrusal O(N)
Kare O(N2)
Küp O(N3)
Polinom O(Na)
Üslü O(aN)
Faktoriyel O(N!)
• Bir algoritma aşağıdaki kategorilerden birine girebilir.
Hızlıdan yavaşa
doğru sıralı.
1 10 100 1000 10000 100000
n
n2
n log n
n
log n n3
2n
Karmaşıklık Sıralaması
Çalışma süresi (saniye)
Girdi boyutu (N)
15
Çalışma süresi örnekleri
N Sabit
O(1)
Logaritmik O(logN)
Doğrusal O(N)
Kare O(N2)
1000 10 ns 100 ns 10 μs 10 ms
1 milyon 10 ns 200 ns 10 ms 3 saat
1 milyar 10 ns 300 ns 10 sec 300 yıl
1 saniye = 103 mili san = 106 mikro san = 109 nano san
Bazı Veri Yapısı İşlemlerinin Algoritma Karmaşıklığı
Ekleme Silme Arama
Sırasız dizi O(1) O(n) O(n)
Sıralı dizi O(log2n + n) O(log2n+ n) O(log2n) Bağlantılı
Liste O(n) O(n) O(n)
İkili Arama
Ağacı O(log2n) O(log2n) O(log2n)
17
Örnek1: Tek döngü
for (i = 1; i <= N; i++) {
myfunc( );
}
N
i
N
1
1
Karmaşıklık O(N)
• myfunc() fonksiyonu kaç defa çağrılır?
Örnek2: Tek döngü
for (i = 1; i <= N; i *= 2) {
myfunc( );
}
N
i
N
,..
16 , 8 , 4 , 2 , 1
log
21
Karmaşıklık O(log2N)
19
Örnek3: İç içe iki döngü
for (i = 1; i <= N; i++) {
for (j = 1; j <= M; j++) {
myfunc( );
} }
N
i
N
i M
j
NM M
1 1 1
1
Karmaşıklık O(NM) ≈ O(N2)
Örnek4: İç içe iki döngü
for (i = 1; i <= N; i++) {
for (j = 1; j <= i; j++) {
myfunc( );
} }
N
i
N
i i
j
N N N
i
1 1 1
2
) 1 ... (
3 2
1 1
Karmaşıklık O(N2/2 + N/2) ≈ O(N2)
21
Örnek5: İç içe üç döngü
for (i = 1; i <= N; i++) {
for (j = 1; j <= M; j++) {
for (k = 1; k <= L; k++) { myfunc( );
} }
}
N
i
N
i N
i
M
j M
j
L
k
NML L
M L
1 1 1 1 1 1
1
Karmaşıklık O(NML) ≈ O(N3)
Örnek6: Ard arda iki döngü
for (i = 1; i <= N; i++) {
myfunc( );
}
for (i = 1; i <= N; i *= 2) {
myfunc( );
}
N
log
2N
Karmaşıklık O(N + log2N) ≈ O(N)
23
Örnek7: Ard arda ve iç içe döngüler
for (i = 1; i <= N; i++) {
for (j = 1; j <= i; j++) {
myfunc( );
} }
for (i = 1; i <= N; i++) {
for (j = 1; j <= M; j++) {
myfunc( );
} }
N(N + 1)/2
NM
Karmaşıklık O(N2/2 + N/2) + O(N2) ≈ 2 O(N2) ≈ O(N2)
Örnek8: İç içe iki döngü
for (i = 1; i <= N; i++) {
for (j = 1; j <= M; j *= 2) {
myfunc( );
} }
N
i
N
i M
j
M N
M
1
2 1
2 ,...
4 , 2 , 1
log log
1
Karmaşıklık O(N log2M) ≈ N O(logN)
log
2N N
25
Örnek9: İç içe iki döngü
for (i = 1; i <= N * N; i++) {
for (j = 1; j <= N * N * N; j++) {
myfunc( );
} }
2 3 2
1
5 1
3 2
3 1
1
N
i
N
i N
j
N N
N N
Karmaşıklık O(N5)
N
3N
2Örnek10: Faktoriyel
// Rekürsif çözüm int factorial (int n) {
if (n<=1) return 1;
else
return n * factorial(n-1);
}
Her ikisi için karmaşıklık O(N) // İteratif çözüm
int factorial(int n) {
int i, fact;
if (n<=1) return 1;
else {
fact = 1;
for (i=2;i<=n;i++) fact *= i;
return fact;
}
27 }
Rekürsif Faktoriyel
factorial (n)
n * factorial(n-1)
n-1 * factorial(n-2)
n-2 * factorial(n-3)
… …
2 *factorial(1)
Rekürsif Faktoriyel
• T(n) rekürsif olarak T(k) cinsinden tanımlanır, k < n
• Rekürsiyon, T(n)’nin temel durumlara (T(0) veya T(1)) biriktirilmesini sağlar.
• T(n) = T(n-1) + d
= T(n-2) + d + d
= T(n-3) + d + d + d = . . .
= T(1) + (n-1)*d = c + (n-1)*d
= c + nd - d ≈ O(n)
29
Örnek11: Rekürsif bölme
void divide(int N) {
if (N <= 2) return;
divide(N / 2);
}
T(0) = T(1) = T(2) = 1 T(n) = 1 + T(n/2) if n > 2 T(n) = 1 + (1 + T(n/4))
= 2 + T(n/4)
= 2 + (1 + T(n/8))
= 3 + T(n/8)
= 3 + (1 + T(n/16))
= 4 + T(n/16)
…
≈ O(log2 n) Karmaşıklık O(logN)
• Amaç: N adet diski A kulesinden C kulesine taşımak.
• Kurallar:
Bir defada tek bir disk taşınır
Büyük bir disk, küçük bir diskin üzerine gelemez
• Rekürsif çözüm:
N - 1 diski A’dan B’ye taşı
A’daki en büyük diski (tek) C’ye taşı
N - 1 diski B’den C’ye taşı
• Toplam hareket sayısı:
O(2N)
Örnek12: Hanoi Kuleleri
31
Rekürsif
çözüm
Rekürsif Hanoi Kuleleri
void hanoi(int N, char source, char target, char tmp) {
if (N == 1)
printf("Move disk from %c to %c \n", source, target);
else {
hanoi(N-1, source, tmp, target);
printf("Move disk from %c to %c \n", source, target);
hanoi(N-1, tmp, target, source);
} }
int main() {
move(4, 'A', 'B', 'C');
return 0;
} Karmaşıklık O(2N)
33
Hanoi(n,A,B,C)
Hanoi(n-1,A,C,B) Hanoi(n-1,C,B,A)
Hanoi(n-2,A,B,C) Hanoi(n-2,B,C,A) Hanoi(n-2,C,A,B) Hanoi(n-2,A,B,C)
Rekürsif Hanoi Kuleleri
T(N) = 2 * T(N-1)
= 2 * (2 * T(N-2) )
= 2 * (2 * (2 * T(N-3) )) = . . .
= 2N-1 * T(1) ≈ O(2N)
34
Rekürsif Hanoi Kuleleri
• Rekürsiyon bağıntısı:
T(n) = 2 T(n - 1) + 1 T(1) = 1
• Bağıntıyı açarak çözüm:
T(n) = 2 (2 T(n - 2) + 1) + 1 = = 4 T(n - 2) + 2 + 1 =
= 4 (2 T(n - 3) + 1) + 2 + 1 = = 8 T(n - 3) + 4 + 2 + 1 = ...
= 2i T(n - i) + 2i-1 +2i-2 +...+21 +20
• i = n – 1 olduğunda açılım sonlanır.
T(n) = 2n – 1 + 2n – 2 + 2n – 3 + ... + 21 + 20
• Sonuç geometrik toplamdır.
– T(N) = 2N - 1 = O(2N) – Bu algoritma üslüdür.
35
Örnek13: Dizi sıralama
/* Sort an array in ascending order */
void BubbleSort(int a[ ], int N) { int pass, i, hold;
/* loop to control number of passes */
for ( pass = 1; pass < N; pass++ ) {
/* loop to control number of comparisons per pass */
for ( i = 0; i < N - 1; i++ ) {
/* compare adjacent elements and swap them if first element is greater than second element */
if ( a[ i ] > a[ i + 1 ] ) { hold = a[ i ];
a[ i ] = a[ i + 1 ];
a[ i + 1 ] = hold;
} /* end if */
} /* end inner for */
} /* end outer for */
}
40 10 90 30 20 60
Başlangıç
10 20 30 40 60 90
Sıralı dizi
Karmaşıklık O(N2)
Dizi sıralama
• En iyi durum: Sıralama öncesi elemanlar zaten sıralı ise.
Çalışma süresi doğrusaldır. O(N)
• En kötü durum: Sıralama öncesi tüm elemanlar tamamen ters sıralı ise.
Çalışma süresi kareseldir. O(N2)
• Ortalama durum: Sıralama öncesi elemanlar kısmen sıralı, kısmen sırasız iseise.
Çalışma süresi kareseldir. O(N2/2) ≈ O(N2)
37
Program çalışma süresinin C’de ölçülmesi
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
clock_t baslama;
double sure, i;
baslama = clock(); // Capture the start time
for (i=1; i <= 1E5; i++)
printf("Sayac = %g \r", i);
sure = (clock() - baslama) / (double) CLOCKS_PER_SEC;
printf(“Geçen süre : %g saniye \n", sure);
system(“pause");
return 0;
Örnek14: Dizide İkili Arama
• İkili arama
– Sadece sıralı bir dizide uygulanabilir.
– Bu nedenle, dizi arama işlemi öncesinde sıralı hale getirilmelidir.
– Ortadaki elemanı arama anahtarı ile karşılaştır
• Eşitseler, aranan bulundu
• Eğer key < middle ise, dizinin ilk yarısında aramaya devam et.
• Eğer key > middle ise, dizinin ikinci yarısında aramaya devam et.
• Yeni middle’ı hesapla ve üstteki adımları tekrarla.
– Çok hızlı; en fazla x adımda çözüm bulunur. ( 2x > Eleman sayısı )
• Örnek: 30 elemanlı dizide arama işlemi en fazla 5 adımda biter.
( 25 > 30 )
Karmaşıklık O(log2N)
39
İkili Arama örneği:
Aranan hedef = 22
2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37
2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37
2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37
2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37
low mid high
low mid high
low mid high
low=mid=high Hedef bulundu!
40
1 /* Fig. 6.19: fig06_19.c
2 Binary search of an array */
3 #include <stdio.h>
4 #define SIZE 15 5
6 /* function prototypes */
7 int binarySearch( const int b[ ], int searchKey, int low, int high );
8 void printHeader( void );
9 void printRow( const int b[ ], int low, int mid, int high );
10
11 /* function main begins program execution */
12 int main() 13 {
14 int a[ SIZE ]; /* create array a */
15 int i; /* counter */
16 int key; /* value to locate in array a */
17 int result; /* variable to hold location of key or -1 */
18
19 /* create data */
20 for ( i = 0; i < SIZE; i++ ) { 21 a[ i ] = 2 * i;
22 } /* end for */
23
24 printf( "Enter a number between 0 and 28: " );
25 scanf( "%d", &key );
26
Örnek: Dizide ikili arama
41
fig06_19.c (Part 2 of 5)
27 printHeader();
28
29 /* search for key in array a */
30 result = binarySearch( a, key, 0, SIZE - 1 );
31
32 /* display results */
33 if ( result != -1 ) {
34 printf( "\n%d found in array element %d\n", key, result );
35 } /* end if */
36 else {
37 printf( "\n%d not found\n", key );
38 } /* end else */
39
40 return 0; /* indicates successful termination */
41
42 } /* end main */
43
44 /* function to perform binary search of an array */
45 int binarySearch( const int b[ ], int searchKey, int low, int high ) 46 {
47 int middle; /* variable to hold middle element of array */
48
49 /* loop until low subscript is greater than high subscript */
50 while ( low <= high ) { 51
52 /* determine middle element of subarray being searched */
53 middle = ( low + high ) / 2;
54
55 /* display subarray used in this loop iteration */
56 printRow( b, low, middle, high );
57
58 /* if searchKey matched middle element, return middle */
59 if ( searchKey == b[ middle ] ) { 60 return middle;
61 } /* end if */
62
63 /* if searchKey less than middle element, set new high */
64 else if ( searchKey < b[ middle ] ) {
65 high = middle - 1; /* search low end of array */
66 } /* end else if */
67
68 /* if searchKey greater than middle element, set new low */
69 else {
70 low = middle + 1; /* search high end of array */
71 } /* end else */
72
73 } /* end while */
74
43
fig06_19.c (Part 4 of 5)
75 return -1; /* searchKey not found */
76
77 } /* end function binarySearch */
78
79 /* Print a header for the output */
80 void printHeader( void ) 81 {
82 int i; /* counter */
83
84 printf( "\nSubscripts:\n" );
85
86 /* output column head */
87 for ( i = 0; i < SIZE; i++ ) { 88 printf( "%3d ", i );
89 } /* end for */
90
91 printf( "\n" ); /* start new line of output */
92
93 /* output line of - characters */
94 for ( i = 1; i <= 4 * SIZE; i++ ) { 95 printf( "-" );
96 } /* end for */
97
98 printf( "\n" ); /* start new line of output */
99 } /* end function printHeader */
100
fig06_19.c (Part 5 of 5)
101 /* Print one row of output showing the current 102 part of the array being processed. */
103 void printRow( const int b[ ], int low, int mid, int high ) 104 {
105 int i; /* counter */
106
107 /* loop through entire array */
108 for ( i = 0; i < SIZE; i++ ) { 109
110 /* display spaces if outside current subarray range */
111 if ( i < low || i > high ) { 112 printf( " " );
113 } /* end if */
114 else if ( i == mid ) { /* display middle element */
115 printf( "%3d*", b[ i ] ); /* mark middle value */
116 } /* end else if */
117 else { /* display other elements in subarray */
118 printf( "%3d ", b[ i ] );
119 } /* end else */
120
121 } /* end for */
122
123 printf( "\n" ); /* start new line of output */
124 } /* end function printRow */
45
Enter a number between 0 and 28: 25
Subscripts:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 --- 0 2 4 6 8 10 12 14* 16 18 20 22 24 26 28 16 18 20 22* 24 26 28 24 26* 28 24*
25 not found
Enter a number between 0 and 28: 8
Subscripts:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 --- 0 2 4 6 8 10 12 14* 16 18 20 22 24 26 28 0 2 4 6* 8 10 12
8 10* 12 8*
8 found in array element 4
Program çıktısı