Ders 05 / 25
Karmaşıklık Hesaplama Yöntemi
Döngü ve koşul kurallarıyla maliyet sayımı, özyineleme bağıntıları, ana teorem ve amortize çözümleme.
İçindekiler
Sınıflar tanındı; sıra bir kod parçasına bakıp hangi sınıfa ait olduğunu belirlemeye geldi. Bu ders, hesabın kurallarını verir. Kurallar azdır ve birleştirilerek uygulanır.
Temel Kurallar
Ardışık bölümler toplanır. İki bölüm arka arkaya çalışıyorsa maliyetleri toplanır ve büyük olan baskın kalır: .
Döngü, gövde maliyeti çarpı tur sayısıdır. Gövdesi sabit olan ve kez dönen döngü ’dir.
İç içe döngülerde maliyetler çarpılır. Her ikisi de kez dönen iki döngü ’dir.
Koşullu yapılarda en kötü dal alınır. Güvence en kötü durum üzerinden verilir.
Sabit sayıda tekrar sabittir. Kaç kez döndüğü ’e bağlı olmayan döngü ’dir.
def sayim_ornekleri(n: int) -> dict[str, int]: sayac = {"tekli": 0, "icice": 0, "ucgen": 0, "yarilanan": 0} for i in range(n): # O(n) sayac["tekli"] += 1 for i in range(n): # O(n²) for j in range(n): sayac["icice"] += 1 for i in range(n): # O(n²) — üçgensel ama yine karesel for j in range(i): sayac["ucgen"] += 1 i = 1 while i < n: # O(log n) sayac["yarilanan"] += 1 i *= 2 return sayac print(sayim_ornekleri(8)) # {'tekli': 8, 'icice': 64, 'ucgen': 28, 'yarilanan': 3} print(sayim_ornekleri(16)) # {'tekli': 16, 'icice': 256, 'ucgen': 120, 'yarilanan': 4}
Girdi ikiye katlandığında sayılar sınıfların beklediği gibi değişir: tekli iki katına, iç içe dört katına çıkar; yarılanan döngü ise yalnızca bir artar.
Üçgensel döngü dikkat ister. Toplam tur sayısı ’dir; sabit çarpan elendiğinde kalır. “Yarısı kadar dönüyor” gözlemi sınıfı değiştirmez — yalnızca sabiti değiştirir.
Döngü Sınırının Değişimi
Tur sayısı, döngü değişkeninin nasıl ilerlediğine bağlıdır:
| İlerleme | Tur sayısı | Sınıf |
|---|---|---|
i += 1 |
||
i += k |
||
i *= 2 |
||
i = i * i |
||
i -= 1 (n’den) |
İkinci satır önemlidir: sabit adımla ilerlemek sınıfı değiştirmez. Çarpımsal ilerleme ise değiştirir; ikili aramanın logaritmik olmasının kaynağı budur.
Özyineleme Bağıntıları
Özyinelemeli bir algoritmanın maliyeti, kendi cinsinden yazılır. Bu ifadeye özyineleme bağıntısı denir.
Üç yaygın kalıp:
Her adımda problem bir eleman küçülür ve sabit iş yapılır; toplam adım. Faktöriyel hesabı bu kalıptadır.
Her adımda problem yarılanır ve sabit iş yapılır; yarılama sayısı ’dir. İkili arama bu kalıptadır.
Problem iki yarıya bölünür, her düzeyde tüm elemanlar işlenir. Birleştirmeli sıralama bu kalıptadır ve bir sonraki konunun ana örneğidir.
Özyineleme Ağacı
Bağıntıyı çözmenin sezgisel yolu, çağrıları bir ağaç olarak çizip düzey düzey maliyet toplamaktır.
bağıntısında:
| Düzey | Çağrı sayısı | Çağrı başına iş | Düzey toplamı |
|---|---|---|---|
| 0 | 1 | ||
| 1 | 2 | ||
| 2 | 4 | ||
Her düzeyin toplamı ’dir ve düzey sayısı ’dir; toplam çıkar.
Aynı yöntem için farklı sonuç verir: düzey toplamları biçiminde büyür ve toplam olur. Yaprak sayısı baskın hâle gelir.
Ana Teorem
Böl ve yönet bağıntılarının çoğu tek bir kalıba uyar:
Burada alt problem sayısı, küçülme çarpanı, bölme ve birleştirme maliyetidir. Sonuç, ile karşılaştırılarak belirlenir:
- küçükse: — yapraklar baskın.
- İkisi aynı mertebedeyse: — tüm düzeyler eşit.
- büyükse: — kök baskın.
Birleştirmeli sıralamada , , ’dir; olduğundan ikinci durum geçerlidir ve sonuç çıkar.
Teoremin tam ifadesi ek koşullar içerir ve her bağıntı bu kalıba uymaz; uymayanlar için ağaç yöntemi veya doğrudan çözüm kullanılır.
Amortize Çözümleme
Bazı işlemlerin maliyeti tek tek bakıldığında yanıltıcıdır. Veri Yapıları kursundaki dinamik dizi buna örnekti: sona ekleme çoğunlukla sabit, ara sıra doğrusaldır.
Toplam maliyet yöntemi, işlemin toplam maliyetini hesaplar ve ’e böler. Dinamik dizide ekleme için toplam kopyalama ’den azdı; ekleme başına amortize maliyet sabittir.
def toplam_kopyalama(n: int) -> int: """n eklemede yapılan toplam kopyalama sayısı (kapasite ikiye katlanarak).""" kapasite, uzunluk, kopyalama = 1, 0, 0 for _ in range(n): if uzunluk == kapasite: kopyalama += uzunluk # tüm elemanlar yeni bloğa taşınır kapasite *= 2 uzunluk += 1 return kopyalama for n in (16, 1000, 100_000): print(n, toplam_kopyalama(n), f"{toplam_kopyalama(n)/n:.2f}") # 16 15 0.94 # 1000 1023 1.02 # 100000 131071 1.31
Ekleme başına ortalama kopyalama sayısı, girdi büyüdükçe küçük bir sabitin çevresinde kalır; doğrusal büyümez. Amortize sabit maliyet ifadesi tam olarak bunu söyler.
İki yöntem daha vardır. Muhasebe yöntemi, her ucuz işleme ileride yapılacak pahalı iş için bir pay yükler; potansiyel yöntemi, yapının “biriken enerjisini” bir fonksiyonla ifade eder. Üçü de aynı sonucu verir; seçim, ispatın hangisiyle daha kısa yazıldığına bağlıdır.
Uygulama Sırası
Bir kod parçasının maliyetini bulmak için izlenen sıra:
- Girdi büyüklüğünü tanımla. neyi sayıyor?
- En iç işlemi bul. Hangi işlem en sık tekrarlanıyor?
- Tur sayılarını çarp. İç içe yapılarda dıştan içe doğru.
- Ardışık bölümleri topla, büyük terimi al.
- Özyineleme varsa bağıntıyı yaz ve çöz.
- Amortize davranış varsa toplam maliyeti işlem sayısına böl.
Adımlar sırayla uygulandığında, sezgiye başvurmadan sonuç elde edilir.
Özet
- Ardışık bölümler toplanır, iç içe döngülerde maliyetler çarpılır, koşullarda en kötü dal alınır.
- Döngü değişkeninin çarpımsal ilerlemesi tur sayısını logaritmik yapar; sabit adımla ilerleme sınıfı değiştirmez.
- Özyinelemeli maliyet bir bağıntıyla yazılır; üç yaygın kalıp doğrusal, logaritmik ve doğrusal-logaritmik sonuç verir.
- Özyineleme ağacı, düzey düzey maliyet toplayarak bağıntıyı çözer.
- Ana teorem, böl ve yönet bağıntılarını üç durumda sınıflandırır.
- Amortize çözümleme, bir işlem dizisinin toplam maliyetini işlem sayısına böler.
Sonraki Adım
Buraya kadar yalnızca zaman ele alındı. Oysa algoritmalar bellek de kullanır ve ikisi sıklıkla birbirinin yerine geçer: daha çok bellek kullanarak daha hızlı, daha az bellek kullanarak daha yavaş çalışmak mümkündür. Sonraki ders bu ödünleşimi ve tipik kalıplarını ele alacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.