SEN2211 · 1. hafta · Introduction to Data Structures and Algorithms, Complexity Analysis

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.

algorithmrunning timetime complexityBig-OBig-OmegaBig-Thetagrowth rateworst casebest caselogarithmiclinearithmicquadratic

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:

  1. Sabitleri at. 3n ile n aynı sınıftadır: O(n).
  2. Baskın terimi tut. n² + 1000n için cevap O(n²).
  3. Art arda döngüler toplanır, iç içe döngüler çarpılır.
  4. Sayaç ikiye katlanıyor ya da yarıya iniyorsa tur sayısı log n’dir.
  5. İç 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österimOkunuşuAnlamıBenzetme
T(n)=O(f(n))T(n) = O(f(n))Big-OT, f’den hızlı büyümez (üst sınır)≤\le
T(n)=Ω(f(n))T(n) = \Omega(f(n))Big-OmegaT, f’den yavaş büyümez (alt sınır)≥\ge
T(n)=Θ(f(n))T(n) = \Theta(f(n))Big-ThetaT, f ile aynı hızda büyür==

Resmi tanım: Bir c>0c > 0 sabiti ve bir n0n_0 varsa, öyle ki her n≥n0n \ge n_0 için T(n)≤c⋅f(n)T(n) \le c \cdot f(n) ise T(n)=O(f(n))T(n) = O(f(n)).

Örnek: T(n)=3n+5T(n) = 3n + 5. c=4c = 4, n0=5n_0 = 5 seçersen n≥5n \ge 5 için 3n+5≤4n3n + 5 \le 4n olur. O halde T(n)=O(n)T(n) = O(n). Aynı zamanda 3n+5≥3n3n + 5 \ge 3n olduğundan Ω(n)\Omega(n), ikisi birden olduğundan Θ(n)\Theta(n).

TUZAK · Big-O sadece üst sınırdır. 3n+5=O(n2)3n + 5 = O(n^2) 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ıfAdıTipik örnekn = 1000 için yaklaşık iş
O(1)sabit (constant)dizide a[i] erişimi, listenin başına ekleme1
O(log n)logaritmikikili arama (binary search)10
O(n)doğrusal (linear)diziyi bir kez gezmek1000
O(n log n)linearitmikmerge sort~10 000
O(n²)karesel (quadratic)iç içe iki döngü, bubble sort1 000 000
O(n³)kübik (cubic)üç iç içe döngü10⁹
O(2ⁿ)üstel (exponential)tüm alt kümeleri denemekastronomik

Sıralama: 1<log⁡n<n<n<nlog⁡n<n2<n3<2n<n!1 < \log n < \sqrt{n} < n < n\log n < n^2 < n^3 < 2^n < n!

Bilgisayar bilimlerinde log tabanı belirtilmezse 2’dir. Big-O içinde taban fark etmez, çünkü log⁡2n\log_2 n ile log⁡10n\log_{10} n 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? 2k≥n2^k \ge n ise k=log⁡2nk = \log_2 n. 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 log⁡216+1\log_2 16 + 1.

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

n(n−1)2=n22−n2\frac{n(n-1)}{2} = \frac{n^2}{2} - \frac{n}{2}. Baskın terim n22\frac{n^2}{2}, 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 n+n2n + n^2, 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 1+2+4+⋯+n=2n−11 + 2 + 4 + \dots + n = 2n - 1. 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üşünceDoğ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)n(n−1)/2n(n-1)/2 → O(n²)
Art arda iki döngü n · n²Toplanır: n + n² = O(n²)
5n2+3n5n^2 + 3n 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 verirAksi söylenmedikçe en kötü durum
3n=O(n2)3n = O(n^2) yanlıştırDoğrudur ama en sıkı sınır değildir; sınavda O(n) yaz
log tabanı önemliBig-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ç.

Soru 1kolay

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
O(n^2)

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ş).

Soru 2kolay

What is the time complexity of this fragment?

int count = 0;
for (int i = 1; i < n; i *= 2)
    count++;
Cevabı göster
O(log n)

i her turda ikiye katlanıyor: 1, 2, 4, 8, ... Kaç katlamada n'ye ulaşırsın? Yaklaşık log⁡2n\log_2 n kez. n = 1000 için 10 tur, n = 1 000 000 için sadece 20 tur.

Soru 3orta

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
45, O(n^2)

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 n(n−1)2\frac{n(n-1)}{2}, baskın terim n22\frac{n^2}{2}, sabiti atınca O(n²).

Soru 4orta

For n = 16, how many times does the loop body run?

for (int i = n; i > 0; i /= 2)
    count++;
Cevabı göster
5

i değerleri: 16, 8, 4, 2, 1. Sonra i = 0 olur ve döngü biter. 5 tur. Genel olarak ⌊log⁡2n⌋+1\lfloor \log_2 n \rfloor + 1 tur, yani O(log n).

Soru 5orta

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
O(n log n)

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ı.

Soru 6kolay

Give the tightest Big-O for f(n)=3n2+100nlog⁡n+5000f(n) = 3n^2 + 100n\log n + 5000.

Cevabı göster
O(n^2)

Büyük n'de en hızlı büyüyen terim kazanır. n2n^2, nlog⁡nn\log n'den hızlı büyür; sabitler (3, 100, 5000) Big-O'da atılır. Cevap O(n²).

Soru 7orta

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
O(n^2)

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ı.

Soru 8kolay

Order these growth rates from slowest to fastest: n2n^2, log⁡n\log n, 2n2^n, nn, nlog⁡nn\log n, 11, n3n^3.

Cevabı göster
1, log n, n, n log n, n^2, n^3, 2^n

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⁹. 2n2^n ise 300 basamaklı bir sayı.

Soru 9orta

An O(n2)O(n^2) algorithm takes 2 seconds for n=1000n = 1000. Approximately how long will it take for n=4000n = 4000?

Cevabı göster
32 seconds

n 4 katına çıktı. Karesel algoritmada süre 42=164^2 = 16 katına çıkar. 2 · 16 = 32 saniye.

Soru 10kolay

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
O(n)

İç 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.

Soru 11zor

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
O(sqrt(n)), 7

Koşul i2≤ni^2 \le n, yani i≤ni \le \sqrt{n}. 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).

Soru 12zor

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
O(n log n)

i = 1 iken iç döngü n kez, i = 2 iken n/2 kez, i = 3 iken n/3 kez... Toplam n(1+12+13+⋯+1n)n(1 + \frac{1}{2} + \frac{1}{3} + \dots + \frac{1}{n}). 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.

Deftere not kartı

Big-O = üst sınır (en kötü durum için kullanılır), Ω = alt sınır, Θ = ikisi birden (tam büyüme) Kural 1: sabitleri at 5n → O(n) n²/2 → O(n²) Kural 2: baskın terimi tut n² + n log n + 1000 → O(n²) Art arda döngü: TOPLA (n + n² = O(n²)) İç içe döngü: ÇARP (n · n = O(n²)) i++ ile n'ye kadar → n tur i *= 2 veya i /= 2 → log n tur i * i <= n → √n tur iç döngü j < i → n(n-1)/2 → O(n²) iç döngü sabit (j < 5) → O(n), n² DEĞİL j += i (harmonik) → O(n log n) Sıra: 1 < log n < √n < n < n log n < n² < n³ < 2ⁿ < n! Süre tahmini: O(n²) ve n x4 → süre x16; O(n) → x4; O(log n) → +2 adım n iki katına çıkınca: O(n) x2, O(n²) x4, O(n³) x8, O(log n) +1, O(n log n) ~x2.1