İçeriğe geç
Educora
Üniversite25 dk46 / 59

Algoritma karmaşıklığı ve Büyük O gösterimi

İşlemleri saymayı, O, Ω ve Θ gösterimlerini, yaygın karmaşıklık sınıflarını ve yineleme bağıntılarını ana teoremle çözmeyi öğren.

Kendini test et
Bu derste öğreneceklerin
  • Bir kod parçasının işlemlerini sayıp büyüme sınıfını bulmak
  • O, Ω ve Θ gösterimlerinin biçimsel tanımlarını uygulamak
  • Özyinelemeli bir algoritmanın çalışma süresini ana teoremle tahmin etmek

Leyla çevrim içi bir kütüphane için arama özelliği yazdı: 1000 kitapta anında yanıt veriyor. Bir yıl sonra katalogda 10 milyon kitap var ve aynı özellik dakikalarca “düşünüyor”. Bilgisayar yavaşlamadı; algoritmanın çalışma süresi verinin boyutuyla çok hızlı büyüyor. Bu derste bu büyümeyi ölçmeyi ve önceden tahmin etmeyi öğreneceğiz.

İşlemleri saymak

Saniyeler makineye, dile ve arka plandaki işlemlere bağlıdır; bu yüzden bir algoritmayı saniyeyle değil, temel işlem sayısıyla değerlendiririz: karşılaştırma, atama, aritmetik işlem, dizi elemanına erişim. Bu sayı, girdi boyutu n'nin bir fonksiyonudur ve genellikle en kötü durum için hesaplanır; kullanıcıya verebileceğimiz garanti budur.

Örnek 1: iç içe döngüler

Bir fonksiyonda dış döngü i = 0 … n − 1, iç döngü j = i + 1 … n − 1 üzerinde döner; iç gövdede bir çarpma vardır (a[i] * a[j]). Gövde kaç kez çalışır ve bu hangi büyüme sınıfıdır?

Çözümü göster
i = 0 iken iç döngü n − 1 kez, i = 1 iken n − 2 kez, …, i = n − 1 iken 0 kez çalışır.
Toplam: (n − 1) + (n − 2) + … + 1 + 0 = n(n − 1)/2.
n = 1000 için bu 499.500 çarpmadır.
n(n − 1)/2 = 0,5n² − 0,5n; düşük dereceli terimi ve sabiti atarız → Θ(n²), karesel bir algoritma.
1 + 2 + … + (n − 1) = n(n − 1) / 21 + 2 + … + (n − 1) = n(n − 1) / 2
burada:
  • ngirdi boyutu (eleman sayısı)

Gauss toplamı: “her eleman diğer her elemanla bir kez” türündeki iç içe döngülerin işlem sayısı.

Asimptotik gösterimler: O, Ω, Θ

Tanım
Büyük O (üst sınır)

f(n) = O(g(n)) yazımı, yeterince büyük n için f(n) fonksiyonunun g(n)'nin sabit bir katıyla yukarıdan sınırlandığı anlamına gelir: f, g'den daha hızlı büyümez. Ω alt sınırı, Θ ise ikisini birlikte, yani tam büyüme hızını verir.

f(n) = O(g(n)) ⇔ ∃ c > 0, ∃ n₀: 0 ≤ f(n) ≤ c · g(n) ∀ n ≥ n₀
burada:
  • f(n)algoritmanın işlem sayısı
  • g(n)karşılaştırma fonksiyonu (n, n², n log n …)
  • cpozitif bir sabit
  • n₀eşitsizliğin sağlandığı başlangıç boyutu
f(n) = Ω(g(n)) ⇔ ∃ c > 0, n₀: f(n) ≥ c · g(n) ∀ n ≥ n₀; f(n) = Θ(g(n)) ⇔ f = O(g) ve f = Ω(g)

Ω alt sınırdır; Θ hem üst hem alt sınırdır (iki sabit arasında “sıkışma”).

Örnek 2: tanımdan ispat

f(n) = 3n² + 5n + 2 veriliyor. c ve n₀ sabitlerini göstererek f(n) = Θ(n²) olduğunu ispatla.

Çözümü göster
Üst sınır: n ≥ 1 için n ≤ n² ve 1 ≤ n² olduğundan
3n² + 5n + 2 ≤ 3n² + 5n² + 2n² = 10n² → c = 10, n₀ = 1; demek ki f = O(n²).
Alt sınır: n ≥ 1 için 5n + 2 > 0, dolayısıyla 3n² + 5n + 2 ≥ 3n² → c = 3; demek ki f = Ω(n²).
İkisi birlikte: 3n² ≤ f(n) ≤ 10n² → f(n) = Θ(n²).

Yaygın karmaşıklık sınıfları

SınıfAdıTipik algoritman = 10n = 1000
O(1)sabitdizide indeksle erişim11
O(log n)logaritmikikili arama≈ 3≈ 10
O(n)doğrusaldoğrusal arama, en büyüğü bulma1010³
O(n log n)doğrusal-logaritmikbirleştirmeli sıralama≈ 33≈ 10⁴
O(n²)kareselkabarcık sıralaması, tüm çiftleri denemek10010⁶
O(2ⁿ)üsteltüm alt kümeleri denemek1024≈ 10³⁰¹
O(n!)faktöriyeltüm permütasyonları denemek3.628.800≈ 10²⁵⁶⁷
Her sınıf için işlem sayısı (log, 2 tabanlı logaritmadır). Tablo yukarıdan aşağıya yavaşlar.
Python
def binary_steps(n):
    steps, lo, hi = 0, 0, n - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        steps += 1
        lo = mid + 1
    return steps

def pair_steps(n):
    count = 0
    for i in range(n):
        for j in range(i + 1, n):
            count += 1
    return count

print('n', 'log', 'linear', 'pairs')
for n in [10, 100, 1000, 2000]:
    print(n, binary_steps(n), n, pair_steps(n))
▸ Beklenen çıktı
n log linear pairs
10 4 10 45
100 7 100 4950
1000 10 1000 499500
2000 11 2000 1999000
Adımları gerçekten sayıyoruz: ikili aramanın en kötü durumu, doğrusal bir geçiş ve tüm çiftler. n 1000'den 2000'e iki katına çıktığında log sütunu yalnızca 1 artar, doğrusal sütun iki katına, çift sayısı ise yaklaşık 4 katına çıkar.
Karmaşıklık≈ 1 saniyede işlenebilen en büyük n
O(n)≈ 10⁸
O(n log n)≈ 5 · 10⁶
O(n²)≈ 10⁴
O(n³)≈ 450
O(2ⁿ)≈ 26
O(n!)≈ 11
Kaba tahmin: derlenen bir dilde saniyede ≈ 10⁸ basit işlem. Python'da sınırlar yaklaşık 10 kat daha küçüktür.
Etkileşimli
Simülasyon yükleniyor…
İkili arama her adımda arama aralığını yarıya böler; O(log n) sınıfının canlı bir örneği. Adımları say ve log₂ n ile karşılaştır.

Yineleme bağıntıları ve ana teorem

Özyinelemeli bir algoritmanın çalışma süresi de özyinelemeli yazılır. İkili arama yarılardan birinde devam eder: T(n) = T(n/2) + c. Birleştirmeli sıralama diziyi iki yarıya böler, her yarıyı sıralar ve doğrusal sürede birleştirir: T(n) = 2T(n/2) + cn. Böyle bir denkleme yineleme bağıntısı denir.

T(n) = 2T(n/2) + cn ⇒ T(n) = cn · log₂ n + n · T(1) = Θ(n log n)T(n) = 2T(n/2) + cn ⇒ T(n) = cn · log₂ n + n · T(1) = Θ(n log n)
burada:
  • T(n)n elemanlı girdide çalışma süresi
  • 2T(n/2)2T(n/2)iki yarının özyinelemeli işlenmesi
  • cnbölme ve birleştirme işi (doğrusal)

Özyineleme ağacı: k. düzeyde n/2ᵏ boyutlu 2ᵏ alt problem vardır; düzeyin toplam işi 2ᵏ · c · n/2ᵏ = cn olur. log₂ n düzey olduğundan toplam cn · log₂ n'dir.

T(n) = a · T(n/b) + Θ(nᵈ)T(n) = a · T(n/b) + Θ(nᵈ)
burada:
  • aözyinelemeli çağrı sayısı (a ≥ 1)
  • bboyutun kaç kat küçüldüğü (b > 1)
  • dbölme ve birleştirme işinin üssü (d ≥ 0)

Ana teorem (sadeleştirilmiş biçim): a ile bᵈ'yi karşılaştır. a < bᵈ → Θ(nᵈ); a = bᵈ → Θ(nᵈ · log n); a > bᵈ → Θ(nᵖ), burada p = log a / log b.

Sezgi şudur: a/bᵈ oranı, özyineleme ağacında bir düzeyden sonrakine işin nasıl değiştiğini gösterir. a < bᵈ iken iş aşağıya doğru azalır ve kökteki nᵈ baskın olur; a = bᵈ iken log n düzeyin hepsi eşit iş yapar; a > bᵈ iken iş artar ve yapraklar (nᵖ tane) belirleyici olur.

Örnek 3: ana teoremin uygulanması

Çöz:
a) T(n) = 2T(n/2) + n
b) T(n) = T(n/2) + 1
c) T(n) = 8T(n/2) + n²
d) T(n) = 2T(n/2) + n²

Çözümü göster
a) a = 2, b = 2, d = 1: bᵈ = 2 = a → Θ(n log n) (birleştirmeli sıralama).
b) a = 1, b = 2, d = 0: bᵈ = 1 = a → Θ(n⁰ · log n) = Θ(log n) (ikili arama).
c) a = 8, b = 2, d = 2: bᵈ = 4 < 8 → Θ(nᵖ), p = log₂ 8 = 3 → Θ(n³) (saf özyinelemeli matris çarpımı).
d) a = 2, b = 2, d = 2: bᵈ = 4 > 2 → Θ(n²); kökteki iş baskındır.
Python
import math

def T(n):
    if n == 1:
        return 1
    return 2 * T(n // 2) + n

for k in [2, 4, 8, 16, 20]:
    n = 2 ** k
    print(n, T(n), n * k + n, round(T(n) / (n * math.log2(n)), 3))
▸ Beklenen çıktı
4 12 12 1.5
16 80 80 1.25
256 2304 2304 1.125
65536 1114112 1114112 1.062
1048576 22020096 22020096 1.05
T(n) = 2T(n/2) + n, T(1) = 1 bağıntısını doğrudan hesaplıyoruz: sonuç tam olarak n · log₂ n + n formülüyle örtüşür ve T(n) / (n log₂ n) oranı 1'e yaklaşır; Θ(n log n) böyle görünür.

Önemli noktalar

  • Algoritmalar saniyeyle değil, n'ye bağlı temel işlem sayısıyla, genellikle en kötü durumda değerlendirilir.
  • O üst sınır, Ω alt sınır, Θ tam büyüme hızıdır; sabitler ve küçük terimler atılır.
  • Sınıfların sırası: 1 < log n < n < n log n < n² < 2ⁿ < n!.
  • Ardışık bloklar toplanır, iç içe döngüler çarpılır, yarıya bölme log n verir.
  • T(n) = aT(n/b) + Θ(nᵈ) için a ile bᵈ karşılaştırılır; T(n) = 2T(n/2) + n → Θ(n log n).

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
Dış döngü i = 0 … n − 1, iç döngü j = i … n − 1 üzerinde döner; gövdede sabit iş vardır. Karmaşıklık nedir?