Bölüm 1.2: Algoritma Karmaşıklığı
Giriş
Öğrenme Çıktıları
Zaman ve bellek karmaşıklığını, en iyi, en kötü ve ortalama durum analizini birbirinden ayırmak.
Büyük-O, Ω ve Θ tanımlarını yazmak; bir fonksiyon için c ve n₀ sabitlerini bularak sınır kanıtlamak.
Tek, iç içe ve çarpımsal döngülerin adım sayısını toplam formülleriyle hesaplamak (f1, f2, f6).
Özyinelemeli kod için bağıntı kurmak; açma, özyineleme ağacı ve Master Teorem ile çözmek (f3, f4, f5).
f1–f6) üzerinde çalışacağız: her biri farklı bir büyüme sınıfını (n, n³, 2ⁿ, n, n log n, log* n) temsil eder.Karmaşıklık Kavramı
Algoritma Karmaşıklığı
- Karmaşıklık teorisi (complexity theory), bir algoritmanın kaynak kullanımını ölçer: zaman ve bellek.
- Kaynak kullanımı, girdi boyutu n'nin bir fonksiyonu olarak yazılır: T(n), S(n).
- Amaç saniye ölçmek değil, girdi büyüdükçe kaynak ihtiyacının nasıl büyüdüğünü görmektir.
Karmaşıklık Kavramı
Süre Ölçümü Neden Yetmez?
long baslangic = System.currentTimeMillis(); dogrusalArama(dizi, 10001); // 10^8 eleman long bitis = System.currentTimeMillis(); System.out.println("Geçen süre: " + (bitis - baslangic) + " ms");
- Bu kod 10⁸ elemanlı bir dizide doğrusal aramanın süresini ölçer.
- Sonuç makineye, işlemciye, JIT derleyicisine, önbelleğe ve o anki yüke göre değişir.
- Tek bir ölçüm, girdi büyüyünce ne olacağını söylemez.
Karmaşıklık Kavramı
Adım Sayma: dogrusalArama
for (int i = 0; i < a.length; i++) { if (a[i] == hedef) return i; } return -1;
| İşlem | En kötü sayı |
|---|---|
i = 0 | 1 |
i < a.length | n + 1 |
a[i] == hedef | n |
i++ | n |
return -1 | 1 |
| Toplam T(n) | 3n + 3 |
- Her temel işlem 1 birim sayılır (RAM modeli). En kötü durum: hedef dizide yok.
- 3n + 3'teki 3 katsayısı ve +3 makineye göre değişir; değişmeyen şey n ile doğrusal büyümedir → T(n) = Θ(n).
Karmaşıklık Kavramı
En İyi, En Kötü ve Ortalama Durum
En iyi durum: hedef ilk elemandır, tek karşılaştırma yeterlidir → Θ(1).
En kötü durum: hedef dizide yok (ya da en sonda); n = 6 karşılaştırma → Θ(n).
Ortalama durum: hedef her konumda eşit olasılıkla ise ortalama (1 + 2 + … + n)/n = (n + 1)/2 = 3,5 karşılaştırma → yine Θ(n).
Karmaşıklık Kavramı
Bellek Karmaşıklığı
Bellek karmaşıklığı genellikle ek bellek (auxiliary space) için verilir: girdinin kendisi sayılmaz. Özyinelemede her açık çağrı yığında bir çerçeve (stack frame) tutar.
| Kod | Ek bellek | Bellek | Zaman |
|---|---|---|---|
f1: tek döngü | yalnızca x ve i | Θ(1) | Θ(n) |
new int[n] oluşturmak | n elemanlı yardımcı dizi | Θ(n) | Θ(n) |
f3: f3(n−1) + f3(n−1) | yığın derinliği n (aynı anda en çok n çerçeve) | Θ(n) | Θ(2ⁿ) |
f4: f4(n/2) + f4(n/2) | yığın derinliği ≈ log₂ n | Θ(log n) | Θ(n) |
| Birleştirme sıralaması | birleştirme için yardımcı dizi | Θ(n) | Θ(n log n) |
Asimptotik Notasyon
Büyük-O (Big-O) Notasyonu
0 ≤ f(n) ≤ c · g(n), her n ≥ n₀ için
- Büyük n'ler için f, g'nin bir sabit katını aşmaz: g bir üst sınırdır.
- Küçük n'ler (n < n₀) önemsizdir; sabit çarpanlar c'ye gömülür.
- Örnek: 3n² + 5n + 2 ≤ 4n², her n ≥ 6 için → O(n²).
Asimptotik Notasyon
Büyük-Omega (Ω) ve Büyük-Teta (Θ)
| Notasyon | Anlamı | Tanım (∃ c, n₀ > 0, her n ≥ n₀ için) | Benzetme |
|---|---|---|---|
| f = O(g) | üst sınır | f(n) ≤ c · g(n) | f ≤ g |
| f = Ω(g) | alt sınır | f(n) ≥ c · g(n) | f ≥ g |
| f = Θ(g) | sıkı (kesin) sınır | c₁ · g(n) ≤ f(n) ≤ c₂ · g(n) | f = g |
- f = Θ(g) ⇔ f = O(g) ve f = Ω(g).
- 3n² + 5n + 2 hem O(n²) hem O(n³)'tür; ama yalnızca n² için Θ'dır.
- Doğrusal arama: en kötü durum Θ(n), en iyi durum Θ(1); tüm girdiler için O(n) ve Ω(1).
Asimptotik Notasyon
Örnek: 3n² + 5n + 2 = Θ(n²)
c₂ = 4 deneyelim: 3n² + 5n + 2 ≤ 4n² ⇔ n² − 5n − 2 ≥ 0 ⇔ n ≥ 5,37. Demek ki n₀ = 6.
n = 6 → 140 ≤ 144 ✓
n = 5'te eşitsizlik bozulur, n = 6'dan itibaren sağlanır (sol taraf 6n + 8 artar, sağ taraf 8n + 4 artar).
c₁ = 3 seçilirse eşitsizlik her n ≥ 1 için doğrudur, çünkü 5n + 2 > 0.
c₁ = 3, c₂ = 4, n₀ = 6 ile tanım sağlanır: f(n) = Θ(n²). Sabitler tek değildir; c₂ = 10, n₀ = 1 de çalışır.
Asimptotik Notasyon
Asimptotik Hesap Kuralları
Asimptotik Notasyon
Yaygın Büyüme Sınıfları
| Sınıf | Adı | Tipik örnek | Bu bölümde |
|---|---|---|---|
| O(1) | Sabit (constant) | dizinin ilk elemanını yazdırmak | — |
| O(log n) | Logaritmik | ikili arama; her adımda problemi yarıya indirmek | f6 (üst sınır) |
| O(n) | Doğrusal (linear) | doğrusal arama, dizinin toplamı | f1, f4 |
| O(n log n) | Log-doğrusal (linearithmic) | birleştirme sıralaması, yığın sıralaması | f5 |
| O(n²) | Karesel (quadratic) | kabarcık, seçmeli, eklemeli sıralama | — |
| O(n³) | Kübik (cubic) | klasik matris çarpımı | f2 |
| O(2ⁿ) | Üstel (exponential) | tüm alt kümeler, saf özyinelemeli Fibonacci | f3 |
| O(n!) | Faktöriyel | tüm permütasyonlar | — |
Sınıflar yukarıdan aşağı yavaşlar. f6'nın gerçek sınıfı log n'den de yavaş büyüyen log* n'dir (ileride).
Asimptotik Notasyon
Büyüme Eğrileri
- Küçük n'de eğriler birbirine yakındır; n = 4'te n² ile 2ⁿ eşittir (16).
- 2ⁿ n ≈ 6,6'da, n² n = 10'dan sonra 100 adımı aşar.
- n log n doğrusala yakın kalır; log n neredeyse düzdür.
- Asimptotik analiz, eğrilerin büyük n'deki sıralamasıyla ilgilenir.
Asimptotik Notasyon
Sayılarla Büyüme
| f(n) | n = 10 | n = 100 | n = 1000 | n = 10⁶ | 10⁶ için süre* |
|---|---|---|---|---|---|
| log₂ n | 3,3 | 6,6 | 10 | 20 | 20 ns |
| n | 10 | 100 | 1000 | 10⁶ | 1 ms |
| n log₂ n | 33 | 664 | 9 966 | 2 · 10⁷ | 20 ms |
| n² | 100 | 10⁴ | 10⁶ | 10¹² | ≈ 17 dakika |
| n³ | 1000 | 10⁶ | 10⁹ | 10¹⁸ | ≈ 32 yıl |
| 2ⁿ | 1024 | 1,3 · 10³⁰ | ≈ 10³⁰¹ | — | hesaplanamaz |
* Saniyede 10⁹ temel işlem yapan bir makine varsayımıyla.
Döngü Analizi
f1: Tek Döngü → O(n)
public int f1(int n) { int x = 0; for (int i = 0; i < n; i++) { x++; } return x; }
- İlklendirme:
int x = 0;x'e 0 atar; sabit zamanlı işlem, O(1). - Döngü:
for (int i = 0; i < n; i++)i, 0'dan n − 1'e kadar ilerler: n kez çalışır. - Artırma:
x++;her döngü adımında bir kez çalışır; O(1).
x = n döndürür, yani dönüş değeri adım sayısının kendisidir: f1(16) = 16, f1(32) = 32.Döngü Analizi
f1(4): Kodu Adım Adım İzle
x sıfırlanır. Henüz döngü başlamadı.
i = 0: koşul 0 < 4 doğru → gövdeye girilir.
x++ → x = 1. Bu, 1. döngü adımı.
i = 1: koşul 1 < 4 doğru → gövdeye girilir.
x++ → x = 2. Bu, 2. döngü adımı.
i = 2: koşul 2 < 4 doğru → gövdeye girilir.
x++ → x = 3. Bu, 3. döngü adımı.
i = 3: koşul 3 < 4 doğru → gövdeye girilir.
x++ → x = 4. Bu, 4. döngü adımı.
i = 4: koşul 4 < 4 yanlış → döngü biter. Koşul n + 1 = 5 kez denetlendi.
return x → 4. Gövde tam n = 4 kez çalıştı: Θ(n).
public int f1(int n) { int x = 0; for (int i = 0; i < n; i++) { x++; } return x; }
Döngü Analizi
f2: İç İçe Döngü → O(n³)
public int f2(int n) { int x = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < i * i; j++) { x++; } } return x; }
- İlklendirme:
int x = 0;sabit zamanlı, O(1). - Dış döngü: i, 0'dan n − 1'e kadar: n kez çalışır.
- İç döngü: j, 0'dan i · i − 1'e kadar: i² kez çalışır.
- İç döngünün tur sayısı i'nin güncel değerine bağlıdır; bu yüzden basitçe "n · n" denemez, tur sayılarını toplamak gerekir.
Döngü Analizi
f2(5): İç Döngü Kaç Kez Çalışır?
Her çubuk, dış döngünün bir turunda (sabit i) iç döngünün kaç kez çalıştığını gösterir. Çubukların toplam alanı f2'nin yaptığı x++ sayısıdır.
i = 0 iken iç döngü 0 kez çalışır (0 × 0 = 0).
i = 1 iken iç döngü 1 kez çalışır (1 × 1 = 1).
i = 2 iken iç döngü 4 kez çalışır (2 × 2 = 4).
i = 3 iken iç döngü 9 kez çalışır (3 × 3 = 9).
i = 4 iken iç döngü 16 kez çalışır. Genel olarak her i için iç döngü i² kez çalışır.
Toplam: 0 + 1 + 4 + 9 + 16 = 30 = (n − 1) · n · (2n − 1) / 6 = 4 · 5 · 9 / 6. f2(5) = 30 döner.
Döngü Analizi
f2: Toplamı Hesaplamak
İç döngünün toplam tur sayısı (i = 0'dan n − 1'e):
Baskın terim n³/3'tür; sabit 1/3 ve düşük dereceli terimler atılır: T(n) = Θ(n³).
| n | f2(n) | n³/3 |
|---|---|---|
| 4 | 14 | 21 |
| 5 | 30 | 42 |
| 16 | 1 240 | 1 365 |
| 32 | 10 416 | 10 923 |
Döngü Analizi
Döngü Kalıpları ve Toplam Formülleri
| Kalıp | Tur sayısı | Sonuç |
|---|---|---|
for (i = 0; i < n; i++) | Σ 1 = n | Θ(n) |
for i < n içinde for (j = 0; j < n; j++) | n · n | Θ(n²) |
for i < n içinde for (j = 0; j < i; j++) | Σ i = n(n − 1)/2 | Θ(n²) |
for i < n içinde for (j = 0; j < i * i; j++) | Σ i² = (n − 1)n(2n − 1)/6 | Θ(n³) |
for (i = 1; i < n; i *= 2) | ⌈log₂ n⌉ | Θ(log n) |
for (i = n; i > 0; i /= 2) içinde for j < n | n · (⌊log₂ n⌋ + 1) | Θ(n log n) |
for (i = 1; i < n; i *= 2) içinde for j < i | 1 + 2 + 4 + … < 2n | Θ(n) |
for (i = 0; i < n; i = 1 << i) (f6) | log* n + 1 kadar | Θ(log* n) |
Yöntem: her dış tur için iç döngünün tur sayısını yazın, sonra bunları toplayın. Toplam bilinen bir seriye (aritmetik, kareler, geometrik) indirgenir.
Döngü Analizi
f6: Kaydırmalı Döngü
public static int f6(int n) { int x = 0; // 1 << i, 2^i ile aynıdır (taşma yok sayılır) for (int i = 0; i < n; i = 1 << i) { x++; } return x; }
- İlklendirme:
int i = 0;i'ye 0 atar; sabit zamanlı, O(1). - Koşul:
i < nher turda i'nin n'den küçük olduğunu denetler. - Güncelleme:
i = 1 << i1'i i bit sola kaydırır; sonuç 2i'dir ve sabit zamanda hesaplanır. - Dikkat: güncelleme
i = 2 * ideğil,i = 2^i'dir. i'nin kendisi üs olur; bu da i'yi çok daha hızlı büyütür.
Döngü Analizi
f6(20): Kodu Adım Adım İzle
x sıfırlanır.
i = 0 ile başlanır; koşul 0 < 20 doğru.
x++ → x = 1.
i = 1 << 0 = 1; koşul 1 < 20 doğru.
x++ → x = 2.
i = 1 << 1 = 2; koşul 2 < 20 doğru.
x++ → x = 3.
i = 1 << 2 = 4; koşul 4 < 20 doğru.
x++ → x = 4.
i = 1 << 4 = 16; koşul 16 < 20 doğru.
x++ → x = 5.
i = 1 << 16 = 65536; koşul 65536 < 20 yanlış → döngü biter.
return x → 5. i değerleri 0, 1, 2, 4, 16: yalnızca 5 tur.
public static int f6(int n) { int x = 0; // 1 << i == 2^i for (int i = 0; i < n; i = 1 << i) { x++; } return x; }
Döngü Analizi
f6: Gerçekte Kaç Tur Döner?
i değerleri: 0 → 2⁰ = 1 → 2¹ = 2 → 2² = 4 → 2⁴ = 16 → 2¹⁶ = 65536. Her tur i'yi üs yapar (2'nin kuvvet kulesi).
Sık görülen akıl yürütme: "k adım sonra 2ᵏ ≥ n, öyleyse k ≥ log₂ n" — bu, i = 2 * i döngüsü için doğrudur. f6'da ise k tur sonra i, k − 1 katlı bir kule olur; tur sayısı log* n (yinelenmiş logaritma) kadardır.
O(log n) bir üst sınır olarak doğrudur, ama sıkı değildir: Θ(log* n).
| n | f6(n) | ⌈log₂ n⌉ |
|---|---|---|
| 4 | 3 | 2 |
| 16 | 4 | 4 |
| 32 | 5 | 5 |
| 1000 | 5 | 10 |
| 65536 | 5 | 16 |
int kaydırma miktarı 32'ye göre mod alınır: 1 << 65536 = 1. Bu yüzden n > 65536 için i 1, 2, 4, 16, 65536 döngüsüne girer ve f6 hiç bitmez. "Taşmayı yok say" varsayımı burada önemlidir.Özyinelemeli Analiz
Özyineleme Bağıntısı (Recurrence)
Özyinelemeli bir fonksiyonun süresi, kendi daha küçük girdilerdeki süresi cinsinden yazılır:
Özyinelemeli Analiz
f3: İki Çağrı, n − 1 → O(2ⁿ)
public long f3(int n) { if (n <= 1) { return 1; } return f3(n - 1) + f3(n - 1); }
- Temel durum (base case): n ≤ 1 iken 1 döner; sabit zamanlı, O(1).
- Özyinelemeli durum (recursive case): n > 1 iken iki kez
f3(n − 1)çağrılır ve sonuçlar toplanır (sabit iş c).
Özyinelemeli Analiz
f3(3): Çağrıları Adım Adım İzle
f3(3) çağrıldı: 3 > 1, temel durum değil.
Satır 5: ilk f3(2) çağrısı. f3(3) yığında bekliyor.
f3(1): n ≤ 1 → temel durum, 1 döner.
f3(2) ikinci çağrısını yapar: yine f3(1) = 1.
f3(2) = 1 + 1 = 2 döner. Sol alt ağaç bitti.
f3(3), ikinci f3(2)'yi çağırır: aynı iş baştan yapılır.
f3(1) = 1.
f3(1) = 1.
İkinci f3(2) = 2 döner.
f3(3) = 2 + 2 = 4 döner. Toplam 7 = 2³ − 1 çağrı; yığın derinliği en çok 3.
public long f3(int n) { if (n <= 1) { return 1; } return f3(n - 1) + f3(n - 1); }
Özyinelemeli Analiz
f3: Bağıntıyı Açma
Bağıntının kendisi: iki alt çağrı, sabit ek iş.
T(n − 1) yerine kendi tanımını koyarız.
Bir kez daha açınca örüntü belirir: katsayılar 2, 4, 8 …
k adım sonra genel biçim. Ek iş (c) ihmal edilirse T(n) = 2ᵏ T(n − k) yazılır; büyüme sınıfı değişmez.
Taban durum T(1) = c'ye n − k = 1 iken ulaşılır. Sonuç: T(n) = Θ(2ⁿ).
Java ile sayıldığında: f3(16) 65 535, f3(32) 4 294 967 295 çağrı yapar (2ⁿ − 1).
Master Teorem
Master Teorem: Genel Biçim
Böl ve fethet (divide and conquer) algoritmalarının bağıntılarını doğrudan çözmek için kullanılır:
a: alt problem (çağrı) sayısı · b: küçülme oranı, alt problem boyutu n/b · f(n): bölme ve birleştirme işi. Derinlik logb n, yaprak sayısı alogb n = nlogb a.
Master Teorem
Master Teorem: Üç Durum
f(n), yaprakların toplam işi olan nlogb a ile karşılaştırılır. c = f(n)'nin polinom derecesi:
| Durum | Koşul | Sonuç | Sezgi |
|---|---|---|---|
| 1 | f(n) = O(nc), c < logb a | T(n) = Θ(nlogb a) | yapraklar baskın |
| 2 | f(n) = Θ(nc), c = logb a | T(n) = Θ(nlogb a · log n) | seviyeler dengeli |
| 3 | f(n) = Ω(nc), c > logb a ve a · f(n/b) ≤ k · f(n), k < 1 | T(n) = Θ(f(n)) | kök baskın |
Düzenlilik koşulu (regularity), f(n) = nᶜ gibi polinomlar için kendiliğinden sağlanır: a(n/b)ᶜ = (a/bᶜ) nᶜ ve a/bᶜ < 1.
Master Teorem
Neden Üç Durum? Seviye Toplamları
f(n) = nᶜ ise ağacın j. seviyesinde aʲ düğüm, her biri (n/bʲ)ᶜ iş yapar: seviye toplamı = nᶜ · (a/bᶜ)ʲ. Toplam, oranı r = a/bᶜ olan bir geometrik seridir.
Durum 1: son seviye (yapraklar) baskın → Θ(n²)
Durum 2: log n seviye × n iş → Θ(n log n)
Durum 3: kök baskın → Θ(n²)
Master Teorem
Örnek: T(n) = 2T(n/2) + O(n)
Parametreleri okuyalım: iki alt problem, her biri n/2 boyutunda, doğrusal birleştirme işi.
Yaprak işinin üssü 1'dir: nlog₂ 2 = n.
f(n)'nin derecesi c = 1.
Üsler eşit: her seviye aynı (n) işi yapar.
Durum 2: T(n) = Θ(nlogb a · log n) = Θ(n log n).
- Birleştirme sıralaması (merge sort): diziyi ikiye böl, iki yarıyı sırala, Θ(n)'de birleştir.
- Bölümdeki
f5fonksiyonu:f1(n)+ ikif5(n/2)çağrısı. - Hızlı sıralamanın (quicksort) dengeli bölme durumu.
Master Teorem
Master Teorem: Örnekler
| Bağıntı | a, b | logb a | c | Durum | Sonuç | Algoritma |
|---|---|---|---|---|---|---|
| T(n/2) + 1 | 1, 2 | 0 | 0 | 2 | Θ(log n) | ikili arama |
| 2T(n/2) + 1 | 2, 2 | 1 | 0 | 1 | Θ(n) | f4, ağaç gezme |
| 2T(n/2) + n | 2, 2 | 1 | 1 | 2 | Θ(n log n) | f5, birleştirme sır. |
| 3T(n/2) + n | 3, 2 | ≈ 1,585 | 1 | 1 | Θ(n1,585) | Karatsuba çarpma |
| 4T(n/2) + n | 4, 2 | 2 | 1 | 1 | Θ(n²) | — |
| 7T(n/2) + n² | 7, 2 | ≈ 2,807 | 2 | 1 | Θ(n2,807) | Strassen |
| 2T(n/2) + n² | 2, 2 | 1 | 2 | 3 | Θ(n²) | — |
| T(n/2) + n | 1, 2 | 0 | 1 | 3 | Θ(n) | hızlı seçme (ortalama) |
Durum 3 satırlarında düzenlilik: 2(n/2)² = n²/2 ≤ ½ n² ve 1 · (n/2) ≤ ½ n, k = ½ ile sağlanır.
Master Teorem
Master Teorem Ne Zaman Uygulanamaz?
Uygulama: f4 ve f5
f4: İki Çağrı, n/2 → O(n)
public int f4(int n) { if (n <= 1) { return 1; } return f4(n / 2) + f4(n / 2); }
- Temel durum: n ≤ 1 iken 1 döner; O(1).
- Özyinelemeli durum: n > 1 iken iki kez
f4(n/2)çağrılır, sonuçlar toplanır (sabit iş).
Uygulama: f4 ve f5
f4(8): Özyineleme Ağacı
Kök: f4(8) yalnızca sabit iş (c) yapar ve iki çağrı başlatır.
Seviye 1: iki f4(4) çağrısı, toplam 2c.
Seviye 2: dört f4(2) çağrısı, toplam 4c. Seviye işi her adımda ikiye katlanıyor.
Yapraklar: sekiz f4(1), toplam 8c = n · c. Toplam c(1 + 2 + 4 + 8) = 15c = (2n − 1)c → Θ(n).
Seviye toplamları geometrik olarak artar; toplamı son terim (yapraklar) belirler. Java ile: f4(16) 31, f4(32) 63 çağrı yapar (2n − 1).
Uygulama: f4 ve f5
f4: Master Teorem ile Çözüm
Genel biçim T(n) = aT(n/b) + f(n) ile eşleştirelim.
Örnekte a = 2, b = 2, f(n) = O(1).
Yaprak işi nlog₂ 2 = n¹ = n.
f(n) = O(1), c = 0'a karşılık gelir; c, logb a = 1'den küçüktür.
Master Teoreme göre f(n) = O(nᶜ), c < logb a ise T(n) = Θ(nlogb a). Sonuç: Θ(n).
Not: f4 için "O(log n)" demek sık yapılan bir hatadır; doğrusu Θ(n)'dir. log n, yalnızca yığın derinliğidir (bellek).
Uygulama: f4 ve f5
f5: Doğrusal İş + İki Çağrı → O(n log n)
public int f5(int n) { if (n <= 1) { return 1; } return f1(n) + f5(n / 2) + f5(n / 2); }
- Temel durum: n ≤ 1 iken 1 döner; O(1).
- Özyinelemeli durum: n > 1 iken önce
f1(n)çağrılır (Θ(n) iş), sonra iki kezf5(n/2).
Uygulama: f4 ve f5
f5(8): Özyineleme Ağacı
Kök: f5(8) içinde f1(8) 8 adım yapar.
Seviye 1: iki f5(4), her biri f1(4) = 4 adım → toplam 8.
Seviye 2: dört f5(2), her biri 2 adım → yine 8. Her seviye n iş yapar.
Yapraklar 8 × 1. Toplam 3 · 8 + 8 = 32 = n log₂ n + n → Θ(n log n). Java: f5(8) = 32.
log₂ n seviyenin her biri n iş yapar. Java ile: f5(16) = 80 = 16 · 4 + 16, f5(32) = 192 = 32 · 5 + 32.
Uygulama: f4 ve f5
f5: Master Teorem ile Çözüm
T(n) = aT(n/b) + f(n) biçiminde.
Parametreler f4 ile aynı, yalnızca f(n) değişti.
f(n) = Θ(n) → c = 1 = logb a.
Üsler eşit: Θ(n log n). Bölümün başındaki örnekle aynı bağıntı.
| f4 | f5 | |
|---|---|---|
| Çağrı | 2 × n/2 | 2 × n/2 |
| Ek iş f(n) | O(1) | O(n) |
| c vs logb a | 0 < 1 | 1 = 1 |
| Durum | 1 | 2 |
| Sonuç | Θ(n) | Θ(n log n) |
Karşılaştırma
f1–f6: Java ile Ölçülen Adım Sayıları
| Fonksiyon | Bağıntı / toplam | n = 16 | n = 32 | Oran | Sonuç |
|---|---|---|---|---|---|
f1 dönüş | Σ 1 = n | 16 | 32 | × 2 | Θ(n) |
f2 dönüş | Σ i² | 1 240 | 10 416 | × 8,4 | Θ(n³) |
f3 çağrı | 2T(n − 1) + c | 65 535 | 4 294 967 295 | × 65 537 | Θ(2ⁿ) |
f4 çağrı | 2T(n/2) + c | 31 | 63 | × 2 | Θ(n) |
f5 dönüş | 2T(n/2) + n | 80 | 192 | × 2,4 | Θ(n log n) |
f6 dönüş | i ← 2i | 4 | 5 | + 1 | Θ(log* n) |
Karşılaştırma
Deneyin: f1–f6 Adım Sayacı
| Fonksiyon | Adım sayısı | Göreli büyüklük (log ölçek) |
|---|
Adım: f1, f2, f6 için x++ sayısı; f3, f4 için çağrı sayısı; f5 için çağrı + f1 döngü adımları. Değerler kodun mantığıyla hesaplanır (f3 için BigInt).
Karşılaştırma
Kısa Bakış: Amortize Analiz
- Amortize analiz (amortized analysis), bir işlem dizisinin toplam maliyetini işlem sayısına böler.
- Dinamik dizi: dolunca kapasite ikiye katlanır ve tüm elemanlar kopyalanır (pahalı); diğer eklemeler 1 birimdir.
- n eklemede kopyalar 1 + 2 + 4 + … < 2n → toplam < 3n → ekleme başına amortize Θ(1).
- Ortalama durumdan farklıdır: olasılık yoktur, her işlem dizisi için garanti verir.
Kapasite 1'den başlayarak 9 ekleme; turuncu çubuklar kapasite artırma (kopyalama) içerir.
Özet
Özet
| Konu | Hatırlanacaklar |
|---|---|
| Karmaşıklık | zaman = temel işlem sayısı T(n); bellek = ek bellek (yığın dahil); en iyi / en kötü / ortalama |
| Notasyon | O: üst, Ω: alt, Θ: sıkı sınır; c ve n₀ ile kanıtlanır; sabitler ve düşük terimler atılır |
| Döngüler | tur sayılarını topla: f1 Σ1 = n → Θ(n); f2 Σi² → Θ(n³); f6 i ← 2i → Θ(log* n) (O(log n) sıkı değil) |
| Özyineleme | bağıntı kur: çağrı sayısı, küçülme biçimi, ek iş; açma veya özyineleme ağacı ile çöz |
| f3 | 2T(n − 1) + c = (2ⁿ − 1)c → Θ(2ⁿ); Master uygulanamaz |
| Master Teorem | aT(n/b) + f(n): f(n)'yi nlogb a ile karşılaştır; yapraklar / dengeli / kök baskın |
| f4, f5 | 2T(n/2) + 1 → Durum 1, Θ(n); 2T(n/2) + n → Durum 2, Θ(n log n) |
Özet
Etkileşimli Simülatörler
Özet
Sık Yapılan Hatalar
i *= 2 döngüsü ise log n tur döner.Özet
Örnek Problemler
- 2n + 10 = O(n) olduğunu c ve n₀ bularak gösterin.
- T(n) = 3T(n/3) + n bağıntısını çözün.
- T(n) = 9T(n/3) + n bağıntısını çözün.
- T(n) = T(n/2) + n bağıntısını çözün (düzenliliği kontrol edin).
for (i = 1; i < n; i *= 2) for (j = 0; j < i; j++) x++;kaç kezx++yapar?for (i = n; i > 0; i /= 2) for (j = 0; j < n; j++) x++;karmaşıklığı nedir?
Cevapları göster
- 2n + 10 ≤ 3n ⇔ n ≥ 10: c = 3, n₀ = 10 (ya da c = 12, n₀ = 1).
- a = 3, b = 3, log₃ 3 = 1 = c → Durum 2 → Θ(n log n).
- log₃ 9 = 2 > c = 1 → Durum 1 → Θ(n²).
- log₂ 1 = 0 < c = 1; 1 · (n/2) ≤ ½ · n (k = ½) → Durum 3 → Θ(n).
- i = 1, 2, 4, …, 2ᵏ < n: toplam 1 + 2 + … + 2ᵏ = 2ᵏ⁺¹ − 1 < 2n → Θ(n) (n = 16: 15).
- Dış döngü ⌊log₂ n⌋ + 1 tur, her turda n iş → Θ(n log n).
Özet
Alıştırmalar
- f2'de iç döngü koşulu
j < iolsaydı karmaşıklık ne olurdu? f2(5) ne döndürürdü? return g(n/2) + g(n/2) + g(n/2) + g(n/2);(taban n ≤ 1) için bağıntıyı yazıp çözün.- f3'ün son satırı
return 2 * f3(n - 1);olsaydı dönüş değeri ve süresi nasıl değişirdi? - T(n) = 8T(n/2) + n² ve T(n) = 7T(n/2) + n² bağıntılarını çözün; hangi algoritmalara karşılık gelirler?
- n² ≠ O(n) olduğunu çelişki ile kanıtlayın.
- f6'yı
for (i = 1; i < n; i *= 2)yaparsak n = 1000 için kaç tur döner? f6(1000) ile karşılaştırın.
Cevaplar / ipuçları
- Σ i = n(n − 1)/2 → Θ(n²); f2(5) = 0 + 1 + 2 + 3 + 4 = 10.
- T(n) = 4T(n/2) + c; log₂ 4 = 2 > 0 → Durum 1 → Θ(n²).
- Dönüş değeri aynı (2ⁿ⁻¹); ama tek çağrı: T(n) = T(n − 1) + c → Θ(n).
- log₂ 8 = 3 > 2 → Θ(n³) (böl-ve-fethet matris çarpımı); log₂ 7 ≈ 2,807 → Θ(n2,807) (Strassen).
- n² ≤ c·n, her n ≥ n₀ için olsun ⇒ n ≤ c. n = max(n₀, ⌈c⌉ + 1) seçilince çelişki.
- i = 1, 2, 4, …, 512 → 10 tur (⌈log₂ 1000⌉); f6(1000) ise yalnızca 5 tur döner.
Özet
Kaynaklar
- Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, MIT Press, 2022 (Bölüm 3 ve 4).
- Sedgewick ve Wayne, Algorithms, 4. baskı, Addison-Wesley, 2011 (Bölüm 1.4: Analysis of Algorithms).
- Kleinberg ve Tardos, Algorithm Design, Pearson, 2005 (Bölüm 2 ve 5).
- D. E. Knuth, "Big Omicron and Big Omega and Big Theta", SIGACT News 8(2), 1976.
- J. L. Bentley, D. Haken ve J. B. Saxe, "A General Method for Solving Divide-and-Conquer Recurrences", SIGACT News 12(3), 1980.