Karmaşıklık analizi: Big-O, Ω, Θ
Bir kod parçasının kaç işlem yaptığını saymayı ve sonucu Big-O ile yazmayı öğrenirsin. Vizede her yıl gelen “What is the running time?” sorularının temeli.
Veri yapıları dersinin ilk sorusu hep aynı: bu kod ne kadar hızlı? Saniye ile ölçmeyiz, çünkü saniye bilgisayara göre değişir. Onun yerine girdinin boyutu n büyüdükçe yapılan işlem sayısının nasıl büyüdüğüne bakarız. Sınavda bir kod parçası verilir ve “What is the time complexity?” diye sorulur. Cevap tek satır ama gerekçesi döngüleri doğru saymaya dayanır.
Altın kurallar:
- Sabitleri at. 3n ile n aynı sınıftadır: O(n).
- Baskın terimi tut. n² + 1000n için cevap O(n²).
- Art arda döngüler toplanır, iç içe döngüler çarpılır.
- Sayaç ikiye katlanıyor ya da yarıya iniyorsa tur sayısı log n’dir.
- İç döngünün sınırına bak. n’ye bağlı değilse iç döngü sabittir.
Algoritma ve çalışma süresi
Algoritma (algorithm), bir problemi çözen sonlu adımlar dizisidir. Veri yapısı (data structure) ise verinin bellekte nasıl düzenlendiğidir: dizi, bağlı liste, yığın, kuyruk. Aynı problemi farklı veri yapılarıyla çözebilirsin ve hız ciddi şekilde değişebilir. Bu ders boyunca her yapı için “ekleme kaç adım, silme kaç adım, arama kaç adım” sorusunu soracağız.
Çalışma süresini (running time) n’nin fonksiyonu olarak yazarız: T(n). Bir dizinin elemanlarını toplayan şu metodu düşün:
int sum = 0; // 1 işlem
for (int i = 0; i < n; i++) { // her turda 1 karşılaştırma
sum += a[i]; // her turda 1 toplama
}
return sum; // 1 işlem
Kabaca T(n) = 2n + 2 işlem. n = 10 için 22, n = 100 için 202. Burada tam sayının önemi yok. Önemli olan: n on katına çıkınca iş de yaklaşık on katına çıkıyor. Bu doğrusal (linear) büyümedir ve O(n) yazılır.
Big-O, Big-Omega, Big-Theta
Üç gösterim var ve üçü de “büyük n için fonksiyon hangi hızla büyüyor” sorusunu cevaplar.
| Gösterim | Okunuşu | Anlamı | Benzetme |
|---|---|---|---|
| Big-O | T, f’den hızlı büyümez (üst sınır) | ||
| Big-Omega | T, f’den yavaş büyümez (alt sınır) | ||
| Big-Theta | T, f ile aynı hızda büyür |
Resmi tanım: Bir sabiti ve bir varsa, öyle ki her için ise .
Örnek: . , seçersen için olur. O halde . Aynı zamanda olduğundan , ikisi birden olduğundan .
TUZAK · Big-O sadece üst sınırdır. ifadesi de matematiksel olarak doğrudur, çünkü n² daha hızlı büyür. Ama sınavda “tightest bound” (en sıkı sınır) istenir. Cevabın O(n) olmalı.
Pratikte sınavlarda Big-O sorulur ve en sıkı cevap beklenir. Big-Theta ile aynı şeyi yazıyorsun gibi düşünebilirsin.
En iyi, en kötü, ortalama durum
Bazı algoritmalarda süre girdinin içeriğine de bağlıdır. Dizide doğrusal arama (linear search) yaparken aradığın eleman ilk sıradaysa 1 karşılaştırma yeter (best case, O(1)). Hiç yoksa n karşılaştırma gerekir (worst case, O(n)). Aksi belirtilmedikçe Big-O en kötü durum (worst case) için verilir.
Yaygın büyüme hızları
| Sınıf | Adı | Tipik örnek | n = 1000 için yaklaşık iş |
|---|---|---|---|
| O(1) | sabit (constant) | dizide a[i] erişimi, listenin başına ekleme | 1 |
| O(log n) | logaritmik | ikili arama (binary search) | 10 |
| O(n) | doğrusal (linear) | diziyi bir kez gezmek | 1000 |
| O(n log n) | linearitmik | merge sort | ~10 000 |
| O(n²) | karesel (quadratic) | iç içe iki döngü, bubble sort | 1 000 000 |
| O(n³) | kübik (cubic) | üç iç içe döngü | 10⁹ |
| O(2ⁿ) | üstel (exponential) | tüm alt kümeleri denemek | astronomik |
Sıralama:
Bilgisayar bilimlerinde log tabanı belirtilmezse 2’dir. Big-O içinde taban fark etmez, çünkü ile arasında sadece sabit bir çarpan var.
İPUCU · Süre tahmini. “O(n²) algoritma n = 1000 için 2 saniye sürüyor, n = 4000 için ne kadar?” sorusunda oranı kullan: n 4 kat → süre 4² = 16 kat → 32 saniye. O(n) olsaydı 4 kat (8 saniye), O(n³) olsaydı 64 kat olurdu.
Döngülerde işlem sayma
Sınavın gerçek konusu bu. Her döngü için “kaç kez döner?” diye sor, sonra birleştir.
Tek döngü
for (int i = 0; i < n; i++) // n tur → O(n)
count++;
for (int i = 0; i < n; i += 3) // n/3 tur → hâlâ O(n)
count++;
Adım büyüklüğü sabit olduğu sürece (1, 2, 3…) döngü doğrusaldır.
Çarpan / bölen döngü
for (int i = 1; i < n; i *= 2) // 1, 2, 4, 8, ... → log n tur
count++;
Kaç kez ikiye katlarsan n’yi geçersin? ise . Aynısı i /= 2 ile aşağı inen döngü için de geçerli. n = 16 için i: 16, 8, 4, 2, 1. Beş tur, yani .
Sayaçla doğrulama (n iki katına çıkıyor):
i *= 2 döngüsü: n=1000 → 10 tur n=2000 → 11 n=4000 → 12 n=8000 → 13
n iki katına çıkınca sadece bir tur ekleniyor. Logaritmik büyümenin işareti bu.
İç içe döngüler
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
count++; // n · n = n² → O(n²)
İç döngünün sınırı dış döngünün değişkenine bağlıysa toplamı açık yaz:
for (int i = 0; i < n; i++)
for (int j = 0; j < i; j++)
count++;
i = 0 → iç döngü 0 kez
i = 1 → 1 kez
i = 2 → 2 kez
...
i = n-1 → n-1 kez
Toplam = 0 + 1 + ... + (n-1) = n(n-1)/2
n = 10 → 45
. Baskın terim , sabiti at: O(n²). Yarısı kadar iş yapıyor ama sınıfı değişmiyor.
Karışık: doğrusal × logaritmik
for (int i = 0; i < n; i++) // n kez
for (int j = 1; j < n; j *= 2) // her seferinde log n kez
count++;
Toplam n log n. Sayaç sonuçları:
n=1000 → 10000 n=2000 → 22000 (x2.20) n=4000 → 48000 (x2.18) n=8000 → 104000 (x2.17)
O(n) olsaydı oran tam 2 olurdu, O(n²) olsaydı 4. 2’nin biraz üstü: n log n.
Art arda döngüler
for (int i = 0; i < n; i++) count++; // n
for (int j = 0; j < n * n; j++) count++; // n²
Toplam , baskın terim n²: O(n²). Toplanır, çarpılmaz.
Bağımlı ama logaritmik dış döngü
Zor bir örnek: dış döngü katlanıyor, iç döngü i kadar dönüyor.
for (int i = 1; i <= n; i *= 2)
for (int j = 0; j < i; j++)
count++;
İç döngü sırasıyla 1, 2, 4, …, n kez döner. Toplam . Yani O(n). n = 16 için 31 tur, n = 1024 için 2047 tur. “İki döngü var, n log n’dir” diye refleksle cevap verme; topla.
Adım adım çözüm kalıbı
Sınavda her kod parçası için şunu yap:
1. En içteki ifade kaç kez çalışıyor? Her döngü için tur sayısını yaz.
2. İç içe ise tur sayılarını çarp (iç döngü dışa bağlıysa toplamı aç).
3. Art arda ise topla.
4. Baskın terimi al, sabitleri at.
5. Emin değilsen n = 8 veya n = 16 için elle say, sonra n'yi iki katına çıkar ve oranı kontrol et.
Beşinci adım hayat kurtarır. n = 8 → 24 işlem, n = 16 → 64 işlem gibi bir sonuç bulduysan oran yaklaşık 2.7, bu n log n’ye işaret eder (8·3 = 24, 16·4 = 64).
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| İki iç içe döngü her zaman O(n²) | İç döngü sabit sayıda dönüyorsa O(n); logaritmikse O(n log n) |
j < i iç döngüsü O(n log n) | → O(n²) |
| Art arda iki döngü n · n² | Toplanır: n + n² = O(n²) |
| için cevap O(5n²) | Sabit atılır: O(n²) |
i *= 2 döngüsü O(n/2) | Tur sayısı log n: O(log n) |
| Big-O en iyi durumu verir | Aksi söylenmedikçe en kötü durum |
| yanlıştır | Doğrudur ama en sıkı sınır değildir; sınavda O(n) yaz |
| log tabanı önemli | Big-O’da taban fark etmez, sabit çarpan kadar fark eder |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is the time complexity of this fragment in Big-O notation?
int count = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
count++;Cevabı göster
Dış döngü n kez, her turda iç döngü n kez döner. Toplam n · n = n² kez count++. Sayaçla kontrol: n = 1000 için 1 000 000, n = 2000 için 4 000 000 (iki kat n, dört kat iş).
What is the time complexity of this fragment?
int count = 0;
for (int i = 1; i < n; i *= 2)
count++;Cevabı göster
i her turda ikiye katlanıyor: 1, 2, 4, 8, ... Kaç katlamada n'ye ulaşırsın? Yaklaşık kez. n = 1000 için 10 tur, n = 1 000 000 için sadece 20 tur.
For n = 10, how many times is count++ executed? What is the Big-O?
for (int i = 0; i < n; i++)
for (int j = 0; j < i; j++)
count++;Cevabı göster
i = 0 iken iç döngü 0 kez, i = 1 iken 1 kez, ..., i = 9 iken 9 kez. Toplam 0 + 1 + ... + 9 = 45. Genel formül , baskın terim , sabiti atınca O(n²).
For n = 16, how many times does the loop body run?
for (int i = n; i > 0; i /= 2)
count++;Cevabı göster
i değerleri: 16, 8, 4, 2, 1. Sonra i = 0 olur ve döngü biter. 5 tur. Genel olarak tur, yani O(log n).
What is the time complexity of this fragment?
for (int i = 0; i < n; i++)
for (int j = 1; j < n; j *= 2)
count++;Cevabı göster
Dış döngü n kez. İç döngü j'yi katladığı için her seferinde log n kez. Çarpım n log n. Sayaç: n = 8 için 8 · 3 = 24. n iki katına çıkınca iş 2'den biraz fazla artıyor (x2.2), bu n log n'nin imzası.
Give the tightest Big-O for .
Cevabı göster
Büyük n'de en hızlı büyüyen terim kazanır. , 'den hızlı büyür; sabitler (3, 100, 5000) Big-O'da atılır. Cevap O(n²).
What is the time complexity of these two sequential loops?
for (int i = 0; i < n; i++)
count++;
for (int j = 0; j < n * n; j++)
count++;Cevabı göster
Art arda gelen döngülerin süreleri toplanır: n + n². Toplamda baskın terim n², yani O(n²). İç içe olsaydı çarpılırdı.
Order these growth rates from slowest to fastest: , , , , , , .
Cevabı göster
Sabit < logaritmik < doğrusal < n log n < karesel < kübik < üstel. n = 1000 için: log n ≈ 10, n log n ≈ 9966, n² = 10⁶, n³ = 10⁹. ise 300 basamaklı bir sayı.
An algorithm takes 2 seconds for . Approximately how long will it take for ?
Cevabı göster
n 4 katına çıktı. Karesel algoritmada süre katına çıkar. 2 · 16 = 32 saniye.
What is the time complexity of this fragment?
for (int i = 0; i < n; i++)
for (int j = 0; j < 5; j++)
count++;Cevabı göster
İç döngü n'ye bağlı değil, her zaman 5 kez döner. Toplam 5n işlem. Sabit 5 atılır: O(n). İç içe döngü gördüğün her yere n² yazma.
What is the time complexity of this fragment? For n = 50, how many times does the body run?
for (int i = 1; i * i <= n; i++)
count++;Cevabı göster
Koşul , yani . n = 50 için i = 1..7 (7² = 49 ≤ 50, 8² = 64 > 50), 7 tur. n 4 katına çıkınca tur sayısı 2 katına çıkıyor (n = 10000 → 100, n = 40000 → 200): O(√n).
What is the time complexity of this fragment? (Hint: the inner loop step depends on i.)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j += i)
count++;Cevabı göster
i = 1 iken iç döngü n kez, i = 2 iken n/2 kez, i = 3 iken n/3 kez... Toplam . Parantez içi harmonik seri, yaklaşık ln n. Sonuç O(n log n). Sayaçla kontrol: n iki katına çıkınca iş x2.10 artıyor, n² olsaydı x4 olurdu.
Doğrulama: Her kod parçasının işlem sayısı Java ile sayıldı (verify/SEN2211/W01.java); n = 1000, 2000, 4000, 8000 için sayaçlar karşılaştırıldı, n iki katına çıkınca oranın (x2, x4, +1, x2.1) iddia edilen büyüme hızıyla uyuştuğu kontrol edildi. Küçük n için kesin sayılar aynı programdan.