Bölüm 1.2: Algoritma Karmaşıklığı

Algoritmalar
Zaman ve bellekEn iyi / en kötü / ortalamaBüyük-O, Ω, ΘBüyüme sınıfları Döngü analiziÖzyineleme bağıntılarıMaster Teoremf1–f6 örnekleri
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

Kavramlar

Zaman ve bellek karmaşıklığını, en iyi, en kötü ve ortalama durum analizini birbirinden ayırmak.

Notasyon

Büyük-O, Ω ve Θ tanımlarını yazmak; bir fonksiyon için c ve n₀ sabitlerini bularak sınır kanıtlamak.

Döngü analizi

Tek, iç içe ve çarpımsal döngülerin adım sayısını toplam formülleriyle hesaplamak (f1, f2, f6).

Özyineleme

Özyinelemeli kod için bağıntı kurmak; açma, özyineleme ağacı ve Master Teorem ile çözmek (f3, f4, f5).

Bölüm boyunca altı kısa örnek fonksiyon (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.
Zaman karmaşıklığı (time complexity)Algoritmanın yaptığı temel işlem sayısı: atama, karşılaştırma, aritmetik işlem, dizi erişimi.
Bellek karmaşıklığı (space complexity)Algoritmanın girdiye ek olarak kullandığı bellek: değişkenler, yardımcı diziler, özyineleme yığını.
Temel soru: Girdi boyutu iki katına çıktığında çalışma süresi ne olur? Aynı mı kalır, iki katına mı, dört katına mı çıkar, yoksa karesi mi alınır?

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.
Deneysel ölçümGerçek süreyi verir ama makineye bağlıdır; kodun yazılmış olması gerekir.
Asimptotik analizAdım sayısını n'nin fonksiyonu olarak verir; makineden bağımsızdır, kod yazmadan yapılabilir.

Karmaşıklık Kavramı

Adım Sayma: dogrusalArama

for (int i = 0; i < a.length; i++) {
  if (a[i] == hedef)
    return i;
}
return -1;
İşlemEn kötü sayı
i = 01
i < a.lengthn + 1
a[i] == hedefn
i++n
return -11
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 (best case)En elverişli girdide en az adım. Bir alt sınır verir; tek başına pek bilgi taşımaz.
En kötü durum (worst case)Her girdi için geçerli bir garanti: algoritma bundan uzun sürmez.
Ortalama durum (average case)Girdilerin bir olasılık dağılımı üzerinden beklenen adım sayısı.
703192438415hedef 7 → 1 karşılaştırma

En iyi durum: hedef ilk elemandır, tek karşılaştırma yeterlidir → Θ(1).

703192438415hedef 5 → 6 karşılaştırma, bulunamadı

En kötü durum: hedef dizide yok (ya da en sonda); n = 6 karşılaştırma → Θ(n).

703192438415hedef 4 → 4 karşılaştırma

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.

KodEk bellekBellekZaman
f1: tek döngüyalnızca x ve iΘ(1)Θ(n)
new int[n] oluşturmakn 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)
Dikkat: Zaman ve bellek farklı büyüyebilir. f3 üstel sayıda çağrı yapar, ama çağrılar sırayla bittiği için yığında aynı anda en çok n çerçeve bulunur.

Asimptotik Notasyon

Büyük-O (Big-O) Notasyonu

f(n) = O(g(n)) ⇔ ∃ c > 0, n₀ :
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²).
02468100100200300400nn₀ = 6f(n)4n²
Dikkat: "Büyük-O en kötü durumu temsil eder" ifadesi tam doğru değildir. Büyük-O bir üst sınır türüdür; en kötü, en iyi ya da ortalama durumun her biri için kullanılabilir. Genellikle en kötü durum süresi için verildiğinden bu ikisi karıştırılır.

Asimptotik Notasyon

Büyük-Omega (Ω) ve Büyük-Teta (Θ)

NotasyonAnlamıTanım (∃ c, n₀ > 0, her n ≥ n₀ için)Benzetme
f = O(g)üst sınırf(n) ≤ c · g(n)f ≤ g
f = Ω(g)alt sınırf(n) ≥ c · g(n)f ≥ g
f = Θ(g)sıkı (kesin) sınırc₁ · 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).
02468100100200300400nn₀ = 6f(n)4n²3n²

Asimptotik Notasyon

Örnek: 3n² + 5n + 2 = Θ(n²)

Üst sınır: 3n² + 5n + 2 ≤ c₂ n²

c₂ = 4 deneyelim: 3n² + 5n + 2 ≤ 4n² ⇔ n² − 5n − 2 ≥ 0 ⇔ n ≥ 5,37. Demek ki n₀ = 6.

Kontrol: n = 5 → 102 > 100 ✗
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).

Alt sınır: 3n² + 5n + 2 ≥ 3n²

c₁ = 3 seçilirse eşitsizlik her n ≥ 1 için doğrudur, çünkü 5n + 2 > 0.

3n² ≤ f(n) ≤ 4n², n ≥ 6

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.

02468100100200300400nn₀ = 6f(n)4n²3n²

Asimptotik Notasyon

Asimptotik Hesap Kuralları

Sabit çarpanları atc · f(n) = Θ(f(n)): 5n = Θ(n), n²/3 = Θ(n²).
Düşük dereceli terimleri atPolinomda en büyük derece kalır: 4n³ + 100n² + 7 = Θ(n³).
Toplam kuralı (ardışık kod)O(f) + O(g) = O(max(f, g)): Θ(n) döngü + Θ(n²) döngü = Θ(n²).
Çarpım kuralı (iç içe kod)n kez çalışan döngü içinde O(g) iş: O(n · g). İç işin her turda aynı olduğunu kontrol edin!
Logaritma tabanı önemsizloga n = logb n / logb a, sabit çarpan farkı: O(log₂ n) = O(log₁₀ n) = O(log n).
Üslü ifadelerde taban önemli3ⁿ = O(2ⁿ) yanlıştır, çünkü 3ⁿ/2ⁿ = 1,5ⁿ → ∞. Logaritmanın aksine üstel tabanlar sabit çarpanla değişmez.
Büyüme sırası: 1 < log n < √n < n < n log n < n² < n³ < 2ⁿ < n!

Asimptotik Notasyon

Yaygın Büyüme Sınıfları

SınıfAdıTipik örnekBu bölümde
O(1)Sabit (constant)dizinin ilk elemanını yazdırmak—
O(log n)Logaritmikikili arama; her adımda problemi yarıya indirmekf6 (ü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 Fibonaccif3
O(n!)Faktöriyeltü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

051015200255075100nadımlog₂ nnn log₂ nn²2ⁿ
  • 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 = 10n = 100n = 1000n = 10⁶10⁶ için süre*
log₂ n3,36,6102020 ns
n10100100010⁶1 ms
n log₂ n336649 9662 · 10⁷20 ms
n²10010⁴10⁶10¹²≈ 17 dakika
n³100010⁶10⁹10¹⁸≈ 32 yıl
2ⁿ10241,3 · 10³⁰≈ 10³⁰¹—hesaplanamaz

* Saniyede 10⁹ temel işlem yapan bir makine varsayımıyla.

Ders: Daha hızlı bir bilgisayar sabit bir çarpan kazandırır; daha iyi bir algoritma ise büyüme sınıfını değiştirir. n = 10⁶ için n² yerine n log n kullanmak 17 dakikayı 20 milisaniyeye indirir.

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).
T(n) = O(1) + n · O(1) = O(n)
Döngü n kez çalışır ve gövdesi sabit zamanlıdır; toplam zaman karmaşıklığı Θ(n) olur. Fonksiyon 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

i=0i=1i=2i=3x++ sayısı: 0

x sıfırlanır. Henüz döngü başlamadı.

i=0i=1i=2i=3x++ sayısı: 0

i = 0: koşul 0 < 4 doğru → gövdeye girilir.

x++i=0i=1i=2i=3x++ sayısı: 1

x++ → x = 1. Bu, 1. döngü adımı.

x++i=0i=1i=2i=3x++ sayısı: 1

i = 1: koşul 1 < 4 doğru → gövdeye girilir.

x++i=0x++i=1i=2i=3x++ sayısı: 2

x++ → x = 2. Bu, 2. döngü adımı.

x++i=0x++i=1i=2i=3x++ sayısı: 2

i = 2: koşul 2 < 4 doğru → gövdeye girilir.

x++i=0x++i=1x++i=2i=3x++ sayısı: 3

x++ → x = 3. Bu, 3. döngü adımı.

x++i=0x++i=1x++i=2i=3x++ sayısı: 3

i = 3: koşul 3 < 4 doğru → gövdeye girilir.

x++i=0x++i=1x++i=2x++i=3x++ sayısı: 4

x++ → x = 4. Bu, 4. döngü adımı.

x++i=0x++i=1x++i=2x++i=3x++ sayısı: 4

i = 4: koşul 4 < 4 yanlış → döngü biter. Koşul n + 1 = 5 kez denetlendi.

x++i=0x++i=1x++i=2x++i=3x++ sayısı: 4

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.
Tuzak: İç içe iki döngü görünce otomatik olarak O(n²) demek yanlıştır. Burada iç döngünün sınırı i² olduğundan sonuç n³ mertebesindedir.

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.

0² = 0i=0i=1i=2i=3i=4toplam x++ = 0

i = 0 iken iç döngü 0 kez çalışır (0 × 0 = 0).

0² = 0i=01² = 1i=1i=2i=3i=4toplam x++ = 1

i = 1 iken iç döngü 1 kez çalışır (1 × 1 = 1).

0² = 0i=01² = 1i=12² = 4i=2i=3i=4toplam x++ = 5

i = 2 iken iç döngü 4 kez çalışır (2 × 2 = 4).

0² = 0i=01² = 1i=12² = 4i=23² = 9i=3i=4toplam x++ = 14

i = 3 iken iç döngü 9 kez çalışır (3 × 3 = 9).

0² = 0i=01² = 1i=12² = 4i=23² = 9i=34² = 16i=4toplam x++ = 30

i = 4 iken iç döngü 16 kez çalışır. Genel olarak her i için iç döngü i² kez çalışır.

0² = 0i=01² = 1i=12² = 4i=23² = 9i=34² = 16i=4toplam x++ = 30

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):

Σi=0n−1 i² = (n − 1) · n · (2n − 1) / 6
= (2n³ − 3n² + n) / 6 ≈ n³ / 3

Baskın terim n³/3'tür; sabit 1/3 ve düşük dereceli terimler atılır: T(n) = Θ(n³).

nf2(n)n³/3
41421
53042
161 2401 365
3210 41610 923
Kontrol: n iki katına çıkınca (16 → 32) adım sayısı 10 416 / 1 240 ≈ 8,4 katına çıkar; 2³ = 8'e yakındır. Kübik büyümenin işareti budur.

Döngü Analizi

Döngü Kalıpları ve Toplam Formülleri

KalıpTur 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 < nn · (⌊log₂ n⌋ + 1)Θ(n log n)
for (i = 1; i < n; i *= 2) içinde for j < i1 + 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 < n her turda i'nin n'den küçük olduğunu denetler.
  • Güncelleme: i = 1 << i 1'i i bit sola kaydırır; sonuç 2i'dir ve sabit zamanda hesaplanır.
  • Dikkat: güncelleme i = 2 * i değ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

??????i değerleri (n = 20); i ← 1 << i

x sıfırlanır.

0?????i değerleri (n = 20); i ← 1 << ikoşul: 0 < 20 → doğru

i = 0 ile başlanır; koşul 0 < 20 doğru.

0?????i değerleri (n = 20); i ← 1 << ikoşul: 0 < 20 → doğru

x++ → x = 1.

01????i değerleri (n = 20); i ← 1 << ikoşul: 1 < 20 → doğru

i = 1 << 0 = 1; koşul 1 < 20 doğru.

01????i değerleri (n = 20); i ← 1 << ikoşul: 1 < 20 → doğru

x++ → x = 2.

012???i değerleri (n = 20); i ← 1 << ikoşul: 2 < 20 → doğru

i = 1 << 1 = 2; koşul 2 < 20 doğru.

012???i değerleri (n = 20); i ← 1 << ikoşul: 2 < 20 → doğru

x++ → x = 3.

0124??i değerleri (n = 20); i ← 1 << ikoşul: 4 < 20 → doğru

i = 1 << 2 = 4; koşul 4 < 20 doğru.

0124??i değerleri (n = 20); i ← 1 << ikoşul: 4 < 20 → doğru

x++ → x = 4.

012416?i değerleri (n = 20); i ← 1 << ikoşul: 16 < 20 → doğru

i = 1 << 4 = 16; koşul 16 < 20 doğru.

012416?i değerleri (n = 20); i ← 1 << ikoşul: 16 < 20 → doğru

x++ → x = 5.

01241665536i değerleri (n = 20); i ← 1 << ikoşul: 65536 < 20 → yanlış, döngü biter

i = 1 << 16 = 65536; koşul 65536 < 20 yanlış → döngü biter.

01241665536i değerleri (n = 20); i ← 1 << ikoş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).

nf6(n)⌈log₂ n⌉
432
1644
3255
1000510
65536516
Java'da tuzak: 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:

T(n) = (alt çağrıların süreleri) + (çağrı dışında yapılan iş),   T(taban) = Θ(1)
Açma (yerine koyma)Bağıntıyı kendi içine tekrar tekrar koyup bir örüntü bulmak; taban duruma ulaşınca toplamak. (f3)
Özyineleme ağacıHer çağrıyı bir düğüm olarak çizip her seviyedeki işi toplamak. (f3, f4, f5)
Master TeoremT(n) = aT(n/b) + f(n) biçimindeki böl-ve-fethet bağıntılarını doğrudan çözmek. (f4, f5)
Bağıntı kurarken sorular: Kaç özyinelemeli çağrı var? Her çağrının girdisi ne kadar küçülüyor (n − 1 mi, n/2 mi)? Çağrılar dışında ne kadar iş yapılıyor?

Ö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).
T(n) = 2T(n − 1) + c,   n > 1     T(1) = c
Girdi her çağrıda yalnızca 1 azalır ama çağrı sayısı her seviyede ikiye katlanır. Bu bağıntı T(n/b) biçiminde olmadığı için Master Teorem uygulanamaz; açma yöntemiyle çözülür.

Özyinelemeli Analiz

f3(3): Çağrıları Adım Adım İzle

f3(3)f3(2)f3(2)f3(1)f3(1)f3(1)f3(1)

f3(3) çağrıldı: 3 > 1, temel durum değil.

f3(3)f3(2)f3(2)f3(1)f3(1)f3(1)f3(1)

Satır 5: ilk f3(2) çağrısı. f3(3) yığında bekliyor.

f3(3)f3(2)f3(2)f3(1)= 1f3(1)f3(1)f3(1)

f3(1): n ≤ 1 → temel durum, 1 döner.

f3(3)f3(2)f3(2)f3(1)= 1f3(1)= 1f3(1)f3(1)

f3(2) ikinci çağrısını yapar: yine f3(1) = 1.

f3(3)f3(2)= 2f3(2)f3(1)= 1f3(1)= 1f3(1)f3(1)

f3(2) = 1 + 1 = 2 döner. Sol alt ağaç bitti.

f3(3)f3(2)= 2f3(2)f3(1)= 1f3(1)= 1f3(1)f3(1)

f3(3), ikinci f3(2)'yi çağırır: aynı iş baştan yapılır.

f3(3)f3(2)= 2f3(2)f3(1)= 1f3(1)= 1f3(1)= 1f3(1)

f3(1) = 1.

f3(3)f3(2)= 2f3(2)f3(1)= 1f3(1)= 1f3(1)= 1f3(1)= 1

f3(1) = 1.

f3(3)f3(2)= 2f3(2)= 2f3(1)= 1f3(1)= 1f3(1)= 1f3(1)= 1

İkinci f3(2) = 2 döner.

f3(3)= 4f3(2)= 2f3(2)= 2f3(1)= 1f3(1)= 1f3(1)= 1f3(1)= 1

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

T(n) = 2T(n − 1) + c

Bağıntının kendisi: iki alt çağrı, sabit ek iş.

= 2[2T(n − 2) + c] + c = 4T(n − 2) + 3c

T(n − 1) yerine kendi tanımını koyarız.

= 8T(n − 3) + 7c

Bir kez daha açınca örüntü belirir: katsayılar 2, 4, 8 …

= 2ᵏ T(n − k) + (2ᵏ − 1)c

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.

k = n − 1: T(n) = 2ⁿ⁻¹c + (2ⁿ⁻¹ − 1)c = (2ⁿ − 1)c

Taban durum T(1) = c'ye n − k = 1 iken ulaşılır. Sonuç: T(n) = Θ(2ⁿ).

4332222111111111 · c2 · c4 · c8 · c

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:

T(n) = a · T(n/b) + f(n),   a ≥ 1, b > 1

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.

f(n)f(n/b)f(n/b)…f(n/b)a çocuk· ·· ·· ·⋮⋮Θ(1)Θ(1)Θ(1)Θ(1)Θ(1)Θ(1)Θ(1)Θ(1)…f(n)a · f(n/b)a² · f(n/b²)nlog b a · Θ(1)derinlik: logb n

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:

DurumKoşulSonuçSezgi
1f(n) = O(nc), c < logb aT(n) = Θ(nlogb a)yapraklar baskın
2f(n) = Θ(nc), c = logb aT(n) = Θ(nlogb a · log n)seviyeler dengeli
3f(n) = Ω(nc), c > logb a ve a · f(n/b) ≤ k · f(n), k < 1T(n) = Θ(f(n))kök baskın
Dikkat: Üç durumda da yalnızca f(n) = O(nᶜ) yazmak yetmez. Örneğin f(n) = 1 de O(n²)'dir, ama T(n) = O(1) olamaz. Bu yüzden Durum 2'de f = Θ(nᶜ), Durum 3'te f = Ω(nᶜ) ve düzenlilik koşulu istenir; sonuçlar Θ ile sıkıdır.

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.

4T(n/2) + nj=0j=1j=2j=3r = 2 > 1: artar

Durum 1: son seviye (yapraklar) baskın → Θ(n²)

2T(n/2) + nj=0j=1j=2j=3r = 1: sabit

Durum 2: log n seviye × n iş → Θ(n log n)

2T(n/2) + n²j=0j=1j=2j=3r = 1/2 < 1: azalır

Durum 3: kök baskın → Θ(n²)

r > 1 ⇔ bᶜ < a ⇔ c < logb a. Yani Master Teoremin üç durumu, geometrik serinin artan, sabit ya da azalan olmasına karşılık gelir.

Master Teorem

Örnek: T(n) = 2T(n/2) + O(n)

a = 2,   b = 2,   f(n) = O(n)

Parametreleri okuyalım: iki alt problem, her biri n/2 boyutunda, doğrusal birleştirme işi.

logb a = log₂ 2 = 1

Yaprak işinin üssü 1'dir: nlog₂ 2 = n.

f(n) = Θ(n¹) → c = 1

f(n)'nin derecesi c = 1.

c = logb a → Durum 2

Üsler eşit: her seviye aynı (n) işi yapar.

T(n) = Θ(n log n)

Durum 2: T(n) = Θ(nlogb a · log n) = Θ(n log n).

Bu bağıntı nerede çıkar?
  • Birleştirme sıralaması (merge sort): diziyi ikiye böl, iki yarıyı sırala, Θ(n)'de birleştir.
  • Bölümdeki f5 fonksiyonu: f1(n) + iki f5(n/2) çağrısı.
  • Hızlı sıralamanın (quicksort) dengeli bölme durumu.

Master Teorem

Master Teorem: Örnekler

Bağıntıa, blogb acDurumSonuçAlgoritma
T(n/2) + 11, 2002Θ(log n)ikili arama
2T(n/2) + 12, 2101Θ(n)f4, ağaç gezme
2T(n/2) + n2, 2112Θ(n log n)f5, birleştirme sır.
3T(n/2) + n3, 2≈ 1,58511Θ(n1,585)Karatsuba çarpma
4T(n/2) + n4, 2211Θ(n²)—
7T(n/2) + n²7, 2≈ 2,80721Θ(n2,807)Strassen
2T(n/2) + n²2, 2123Θ(n²)—
T(n/2) + n1, 2013Θ(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?

Çıkarmalı bağıntılarT(n) = 2T(n − 1) + c (f3) ya da T(n) = T(n − 1) + n: alt problem n/b değil, n − 1. Açma yöntemi kullanılır.
Polinom olmayan farkT(n) = 2T(n/2) + n / log n: f(n), n'den yalnızca log çarpanı kadar küçük; hiçbir duruma girmez.
Eşit olmayan bölmeT(n) = T(n/3) + T(2n/3) + n: alt problemler farklı boyutta. Özyineleme ağacı (ya da Akra–Bazzi) ile Θ(n log n).
Sabit olmayan a ya da a < 1T(n) = 2ⁿ T(n/2) + n ya da T(n) = ½ T(n/2) + n: teoremin a ≥ 1 sabit varsayımı bozulur.
Temel biçimin bir genişlemesi: f(n) = Θ(nlogb a · logᵏ n) ise T(n) = Θ(nlogb a · logᵏ⁺¹ n). Örneğin T(n) = 2T(n/2) + n log n = Θ(n log² n).

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ş).
T(n) = 2T(n/2) + O(1),   T(1) = O(1)
f3 ile tek fark girdinin n − 1 yerine n/2 olmasıdır; ama bu, sonucu Θ(2ⁿ)'den Θ(n)'ye indirir. İki çağrı her zaman üstel süre demek değildir: girdinin ne kadar küçüldüğü belirleyicidir.

Uygulama: f4 ve f5

f4(8): Özyineleme Ağacı

8442222111111111 · c

Kök: f4(8) yalnızca sabit iş (c) yapar ve iki çağrı başlatır.

8442222111111111 · c2 · c

Seviye 1: iki f4(4) çağrısı, toplam 2c.

8442222111111111 · c2 · c4 · c

Seviye 2: dört f4(2) çağrısı, toplam 4c. Seviye işi her adımda ikiye katlanıyor.

8442222111111111 · c2 · c4 · c8 · c

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

T(n) = 2T(n/2) + O(1)

Genel biçim T(n) = aT(n/b) + f(n) ile eşleştirelim.

a = 2, b = 2, f(n) = O(1)

Örnekte a = 2, b = 2, f(n) = O(1).

logb a = log₂ 2 = 1

Yaprak işi nlog₂ 2 = n¹ = n.

f(n) = O(n⁰) → c = 0 < 1

f(n) = O(1), c = 0'a karşılık gelir; c, logb a = 1'den küçüktür.

Durum 1: T(n) = Θ(nlog₂ 2) = Θ(n)

Master Teoreme göre f(n) = O(nᶜ), c < logb a ise T(n) = Θ(nlogb a). Sonuç: Θ(n).

Ağaç ile aynı sonuçÖzyineleme ağacında 2n − 1 düğüm vardı. Master Teorem bu toplamı hesaplamadan aynı sonucu verir: yapraklar baskı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 kez f5(n/2).
T(n) = 2T(n/2) + f1(n) = 2T(n/2) + O(n)
f4'ten tek farkı her çağrıda yapılan doğrusal iştir. Bu, birleştirme sıralamasının bağıntısıyla aynıdır. Sonuç "O(n + log n)" değil, Θ(n log n)'dir.

Uygulama: f4 ve f5

f5(8): Özyineleme Ağacı

8442222111111118

Kök: f5(8) içinde f1(8) 8 adım yapar.

84422221111111184 + 4 = 8

Seviye 1: iki f5(4), her biri f1(4) = 4 adım → toplam 8.

84422221111111184 + 4 = 84 · 2 = 8

Seviye 2: dört f5(2), her biri 2 adım → yine 8. Her seviye n iş yapar.

84422221111111184 + 4 = 84 · 2 = 88 · 1 = 8

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) = 2T(n/2) + O(n)

T(n) = aT(n/b) + f(n) biçiminde.

a = 2, b = 2, f(n) = O(n)

Parametreler f4 ile aynı, yalnızca f(n) değişti.

logb a = log₂ 2 = 1,   c = 1

f(n) = Θ(n) → c = 1 = logb a.

Durum 2: T(n) = Θ(nlogb a log n) = Θ(n log n)

Üsler eşit: Θ(n log n). Bölümün başındaki örnekle aynı bağıntı.

f4f5
Çağrı2 × n/22 × n/2
Ek iş f(n)O(1)O(n)
c vs logb a0 < 11 = 1
Durum12
SonuçΘ(n)Θ(n log n)

Karşılaştırma

f1–f6: Java ile Ölçülen Adım Sayıları

FonksiyonBağıntı / toplamn = 16n = 32OranSonuç
f1 dönüşΣ 1 = n1632× 2Θ(n)
f2 dönüşΣ i²1 24010 416× 8,4Θ(n³)
f3 çağrı2T(n − 1) + c65 5354 294 967 295× 65 537Θ(2ⁿ)
f4 çağrı2T(n/2) + c3163× 2Θ(n)
f5 dönüş2T(n/2) + n80192× 2,4Θ(n log n)
f6 dönüşi ← 2i45+ 1Θ(log* n)
Okuma ipucu: n iki katına çıkınca oran, büyüme sınıfını ele verir: Θ(n) → ×2, Θ(n log n) → ×2'den biraz fazla, Θ(n³) → ×8, Θ(2ⁿ) → ×2ⁿ.

Karşılaştırma

Deneyin: f1–f6 Adım Sayacı

FonksiyonAdı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.
112233145516171899toplam = 24 · ortalama = 2,67

Kapasite 1'den başlayarak 9 ekleme; turuncu çubuklar kapasite artırma (kopyalama) içerir.

Tek bir eklemenin en kötü durumu Θ(n) olsa da, dizinin tamamı için 24 < 3 · 9 = 27 birim harcanır. Ayrıntı: toplama, hesap (accounting) ve potansiyel yöntemleri.

Özet

Özet

KonuHatırlanacaklar
Karmaşıklıkzaman = temel işlem sayısı T(n); bellek = ek bellek (yığın dahil); en iyi / en kötü / ortalama
NotasyonO: üst, Ω: alt, Θ: sıkı sınır; c ve n₀ ile kanıtlanır; sabitler ve düşük terimler atılır
Döngülertur sayılarını topla: f1 Σ1 = n → Θ(n); f2 Σi² → Θ(n³); f6 i ← 2i → Θ(log* n) (O(log n) sıkı değil)
Özyinelemebağıntı kur: çağrı sayısı, küçülme biçimi, ek iş; açma veya özyineleme ağacı ile çöz
f32T(n − 1) + c = (2ⁿ − 1)c → Θ(2ⁿ); Master uygulanamaz
Master TeoremaT(n/b) + f(n): f(n)'yi nlogb a ile karşılaştır; yapraklar / dengeli / kök baskın
f4, f52T(n/2) + 1 → Durum 1, Θ(n); 2T(n/2) + n → Durum 2, Θ(n log n)

Özet

Etkileşimli Simülatörler

Asimptotik Büyüme Gezginilog n, n, n log n, n², n³ ve 2ⁿ'yi aynı n'de karşılaştırın; iki fonksiyonun kesişme (crossover) noktasını bulun.
Özyineleme ve Master Teorema, b ve f(n) = nᵏ girerek T(n) = aT(n/b) + f(n) için özyineleme ağacının seviyelerini çizdirin ve durumu sınıflandırın.
Amortize AnalizDinamik diziye eleman ekleyip kapasite artışlarını, gerçek maliyeti ve ekleme başına ortalama maliyeti izleyin.

Özet

Sık Yapılan Hatalar

Büyük-O = en kötü durum sanmakO bir üst sınırdır; en kötü durum ise bir girdi türüdür. "En iyi durum O(1)" da geçerli bir cümledir.
Sabitleri ve küçük terimleri yazmakO(3n + 3), O(n²/3 + n) yazılmaz: O(n) ve O(n²) yeterlidir.
"İç içe döngü = n²"İç döngünün sınırına bakın: f2'de i² olduğu için Θ(n³); i *= 2 döngüsü ise log n tur döner.
"İki çağrı = üstel"f3 (n − 1) Θ(2ⁿ), f4 (n/2) Θ(n)'dir. Küçülme biçimi her şeyi değiştirir.
Master'ı her bağıntıya uygulamakT(n − 1) içeren, eşit bölmeyen ya da f(n) = n/log n olan bağıntılarda teorem geçerli değildir.
Durum 3'te düzenliliği unutmakc > logb a yetmez; a · f(n/b) ≤ k · f(n) (k < 1) de sağlanmalıdır.

Özet

Örnek Problemler

  1. 2n + 10 = O(n) olduğunu c ve n₀ bularak gösterin.
  2. T(n) = 3T(n/3) + n bağıntısını çözün.
  3. T(n) = 9T(n/3) + n bağıntısını çözün.
  4. T(n) = T(n/2) + n bağıntısını çözün (düzenliliği kontrol edin).
  5. for (i = 1; i < n; i *= 2) for (j = 0; j < i; j++) x++; kaç kez x++ yapar?
  6. for (i = n; i > 0; i /= 2) for (j = 0; j < n; j++) x++; karmaşıklığı nedir?
Cevapları göster
  1. 2n + 10 ≤ 3n ⇔ n ≥ 10: c = 3, n₀ = 10 (ya da c = 12, n₀ = 1).
  2. a = 3, b = 3, log₃ 3 = 1 = c → Durum 2 → Θ(n log n).
  3. log₃ 9 = 2 > c = 1 → Durum 1 → Θ(n²).
  4. log₂ 1 = 0 < c = 1; 1 · (n/2) ≤ ½ · n (k = ½) → Durum 3 → Θ(n).
  5. i = 1, 2, 4, …, 2ᵏ < n: toplam 1 + 2 + … + 2ᵏ = 2ᵏ⁺¹ − 1 < 2n → Θ(n) (n = 16: 15).
  6. Dış döngü ⌊log₂ n⌋ + 1 tur, her turda n iş → Θ(n log n).

Özet

Alıştırmalar

  1. f2'de iç döngü koşulu j < i olsaydı karmaşıklık ne olurdu? f2(5) ne döndürürdü?
  2. 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.
  3. f3'ün son satırı return 2 * f3(n - 1); olsaydı dönüş değeri ve süresi nasıl değişirdi?
  4. 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?
  5. n² ≠ O(n) olduğunu çelişki ile kanıtlayın.
  6. 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ı
  1. Σ i = n(n − 1)/2 → Θ(n²); f2(5) = 0 + 1 + 2 + 3 + 4 = 10.
  2. T(n) = 4T(n/2) + c; log₂ 4 = 2 > 0 → Durum 1 → Θ(n²).
  3. Dönüş değeri aynı (2ⁿ⁻¹); ama tek çağrı: T(n) = T(n − 1) + c → Θ(n).
  4. log₂ 8 = 3 > 2 → Θ(n³) (böl-ve-fethet matris çarpımı); log₂ 7 ≈ 2,807 → Θ(n2,807) (Strassen).
  5. n² ≤ c·n, her n ≥ n₀ için olsun ⇒ n ≤ c. n = max(n₀, ⌈c⌉ + 1) seçilince çelişki.
  6. 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.

Son

Bölüm 1.2: Algoritma Karmaşıklığı
Sonraki bölüm: Bölüm 2: Sıralama Algoritmaları
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.