T: tema · Z: kodu büyüt · S: konuşmacı notları · F: tam ekran · Esc: genel bakış · ⌂ ders haritası
OYUNLARDA YAPAY ZEKÂ · SIRALAMA
Mergesort
Böl ve fethet — N² duvarını yıkan ilk algoritma: her zaman N lg N
PRESS ▶ TO START

Bu derste oyunun haritası 🗺️

  1. Böl ve fethet — büyük problemi küçüklere böl divide and conquer
  2. Birleştirme — iki sıralı listeyi tek sıralı listeye merge
  3. Yukarıdan aşağı mergesort — özyinelemeli top-down
  4. Analiz — neden N lg N?
  5. İyileştirmeler — küçük parçalar, zaten sıralı kontrolü, Timsort
  6. Aşağıdan yukarı — özyinelemesiz bottom-up
  7. Sıralamanın alt sınırı — daha hızlısı mümkün mü?
  8. Kararlılık ve karşılaştırıcılar — skor tabloları
🎮 Bu dersin sonunda…

…milyon elemanlı bir skor tablosunu saniyenin altında sıralayabilecek ve “bundan daha hızlı karşılaştırmalı sıralama yok” cümlesinin neden doğru olduğunu açıklayabileceksiniz.

🤔 ISINMA: İKİ SUNUCU

Avrupa ve Asya sunucularının skor tabloları ayrı ayrı sıralı.

Küresel skor tablosunu nasıl oluşturursunuz? Hepsini tek listeye atıp baştan mı sıralarsınız?

Daha iyisi: iki listenin tepelerine bakın; büyüğü alın, tekrarlayın. Her adımda bir oyuncu yerine oturur → toplam N adım.

Bu işlemin adı birleştirme merge. Mergesort bunun üzerine kurulu.

Mini sözlük

divide and conquerböl ve fethetProblemi parçalara böl, parçaları çöz, sonuçları birleştir
mergebirleştirmeİki sıralı diziyi tek bir sıralı diziye dönüştürmek
auxiliary arrayyardımcı dizi (aux)Birleştirme için geçici kopya
top-down / bottom-upyukarıdan aşağı / aşağıdan yukarıÖzyinelemeli / döngülü sürüm
recurrenceyineleme bağıntısıC(N) = 2 C(N/2) + N gibi kendine atıf yapan formül
lower boundalt sınırHiçbir algoritmanın daha iyisini yapamayacağı en az iş
stablekararlıEşit anahtarlıların sırası korunur
comparatorkarşılaştırıcı“Hangisi önce?” sorusunu cevaplayan fonksiyon
LEVEL 0
Böl ve Fethet
divide and conquer — Roma’dan bilgisayara bir strateji
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

Fikir: ikiye böl, ikisini sırala, birleştir

  1. Diziyi ikiye böl.
  2. Her yarıyı (aynı yöntemle!) sırala → özyineleme.
  3. İki sıralı yarıyı birleştir.

Tek elemanlı bir dizi zaten sıralıdır → özyinelemenin temel durumu.

🎮 Böl ve fethet oyunlardaQuadtree (dünyayı 4’e böl), BSP (Doom), minimax (oyun ağacı), LOD (uzaktaki modelleri kaba çiz)… Hepsi “büyük problemi küçüklere bölme” fikri.
📜 1945: John von Neumann Mergesort, modern bilgisayarın mimarisini tasarlayan John von Neumann’ın EDVAC bilgisayarı için yazdığı ilk programlardan biriydi. Yani bilgisayar tarihindeki ilk “ciddi” algoritmalardan biri bir sıralama algoritması!
LEVEL 1
Birleştirme
merge — iki sıralı listeden tek sıralı liste
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

🎮 İki skor tablosunu birleştir

Birleştirme: yardımcı dizi ile

a[lo…mid] ve a[mid+1…hi] ayrı ayrı sıralı. Hedef: a[lo…hi] tamamen sıralı.

  1. Parçayı aux’a kopyala
  2. i sol yarıda, j sağ yarıda başlasın
  3. Her k için küçüğü a[k]’ye yaz, onun işaretçisini ilerlet
  4. Bir yarı bitince diğerinden devam et

🎒 aux = auxiliary (yardımcı). Neden kopya? Çünkü a’ya yazarken okuduğumuz değerlerin üzerine yazmamalıyız.

🔗 Başka gözle: etkileşimli anlatımlar ve yazılar

Aynı konuyu başka bir anlatımla, kendi elinizle oynayarak görmek çok işe yarar. Kartlar yeni sekmede açılır; çoğu İngilizce ama görsel ve etkileşimli.

Quiz 1 · birleştirme
Toplam N elemanlı iki sıralı yarıyı birleştirmek en fazla kaç karşılaştırma gerektirir?
Her karşılaştırma bir elemanı yerine yerleştirir; son eleman karşılaştırmasız yerleşir (diğer yarı bitince) → en fazla N − 1. En az N/2 (bir yarının tamamı diğerinden küçükse). Birleştirme doğrusal zamanlıdır.
LEVEL 2
Yukarıdan Aşağı
top-down mergesort — özyinelemenin gücü
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

Mergesort — kod

🎒 C#’ta aynı adlı iki Sort metodu var: parametre listeleri farklı olduğu için derleyici hangisini çağıracağını bilir (method overloading). İçerideki yardımcıyı private/static yapmak, kullanıcıya sadece basit olanı göstermek içindir.

İz tablosu — D O N K E Y K O N G J U N I O R

kırmızı = bu adımda birleştirilen parça (artık sıralı) · gri = bu adımda dokunulmayanlar

Mergesort — animasyon

🎬 Ara: dansla mergesort

Merge-sort with Transylvanian-saxon (German) folk dance
AlgoRythmics (Sapientia Üniversitesi) · 4 dk 16 sn · YouTube’da aç

Dansçılar dizinin elemanları: önce ikiye ikiye ayrılıp sonra sıralı gruplar hâlinde birleşiyorlar. Komik ama çok öğretici; derste izleyin.

🤔 İzlerkenDansçılar hangi anda “yardımcı dizi”ye geçiyor? Birleştirme sırasında iki grubun başındakiler nasıl karşılaştırılıyor? Kaç “seviye” birleştirme oldu (lg N)?
Quiz 2 · yukarıdan aşağı
16 elemanlı bir diziyi yukarıdan aşağı mergesort ile sıralarken merge fonksiyonu toplam kaç kez çağrılır?
8 tane ikili birleştirme (1+1), 4 tane dörtlü, 2 tane sekizli, 1 tane on altılı: 8 + 4 + 2 + 1 = 15 = N − 1. Her birleştirme iki parçayı bire indirir; 16 parçadan 1 parçaya inmek için 15 birleştirme gerekir.
LEVEL 3
Analiz
neden N lg N? özyineleme ağacı ve bellek
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

📐 Özyineleme ağacı: her seviye N, lg N seviye

📐 Aynı sonuç, formülle

C(N) = N elemanı sıralarken yapılan karşılaştırma sayısı (en kötü):

\( C(N) = \underbrace{C(N/2)}_{\text{sol}} + \underbrace{C(N/2)}_{\text{sağ}} + \underbrace{N}_{\text{birleştir}} \)

N = 2ⁿ ise iki tarafı N’e bölelim: \( \frac{C(N)}{N} = \frac{C(N/2)}{N/2} + 1 \)

Her yarıya bölmede sağ taraf 1 artıyor; lg N kez bölebiliriz → \( \frac{C(N)}{N} = \lg N \) → C(N) = N lg N

ÖnermeMergesort N elemanlı bir diziyi sıralarken ½ N lg N ile N lg N arasında karşılaştırma ve en fazla 6 N lg N dizi erişimi yapar.

Ve bu her girdi için geçerli: en kötü durum bile N lg N. Selection/insertion sort’un N²’sine karşı N = 1 milyonda 50.000 kat fark!

🧪 Canlı ölçüm: N² ile N lg N arasındaki uçurum

⚙️ Bedeli: ek bellek

Mergesort yerinde in-place değildir: N elemanlık bir aux dizisi ister.

  • 1 milyon int → +4 MB
  • 1 milyon nesne referansı → +8 MB

Selection, insertion, shellsort ve (birazdan) quicksort ek dizi istemez. Bellek kısıtlı bir konsolda ya da mobil cihazda bu önemli olabilir.

⚙️ Ama…
  • Bağlı listeleri mergesort ek dizi olmadan sıralar (düğüm bağlantılarını değiştirerek).
  • Diskteki dev dosyaları sıralamak (dış sıralama, external sort) neredeyse hep mergesort ile yapılır: parça parça sırala, sonra birleştir.
LEVEL 4
İyileştirmeler
küçük parçalar · zaten sıralı mı? · Timsort
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

Üç pratik iyileştirme

1) Küçük parçalara insertion sort~10 elemanlık parçalar için özyinelemenin yükü fazla. Insertion sort küçük dizilerde çok hızlı (temel sıralama sunumu!). %10–15 kazanç.
2) Zaten sıralı mı?a[mid] ≤ a[mid+1] ise iki yarı zaten doğru sırada → birleştirmeyi atla. Sıralı girdide mergesort doğrusal olur!
3) Kopyalamayı azaltHer özyineleme seviyesinde a ile aux’un rollerini değiştirerek kopyalama adımını ortadan kaldırmak.

“Zaten sıralı” kontrolünün etkisi

📜 Timsort: Python’un ve Java’nın sıralaması

2002’de Tim Peters, Python için mergesort’un pratikte çok hızlı bir çeşidini yazdı: Timsort.

  • Dizide zaten sıralı parçaları (“koşu”, run) bulur — gerçek verilerde çok olur.
  • Kısa koşuları insertion sort ile uzatır (temel sıralama sunumu).
  • Koşuları birleştirir; bir taraf hep kazanıyorsa dörtnala arama galloping ile atlar (arama sunumu!).

Sonuç: kararlı, en kötü N lg N, neredeyse sıralı veride N’e yakın. Bugün Python, Java (nesneler için), Android ve V8 (JavaScript) Timsort kullanıyor.

🎮 DersBu dönem gördüğünüz parçalar — insertion sort, ikili/dörtnala arama, birleştirme, “gerçek veri neredeyse sıralıdır” gözlemi — bir araya gelince dünyanın en çok kullanılan sıralama algoritması oluyor.
LEVEL 5
Aşağıdan Yukarı
bottom-up mergesort — özyineleme olmadan
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

Önce 1’likleri, sonra 2’likleri, 4’lükleri birleştir

  • sz = 1: komşu tekleri birleştir → sıralı ikililer
  • sz = 2: ikilileri birleştir → sıralı dörtlüler
  • … sz diziyi aşana kadar ikiye katla

Özyineleme yok, çağrı yığını yok: iki iç içe döngü. Aynı ~N lg N.

🎒 min(lo + 2*sz - 1, N - 1): son parça tam boyutlu olmayabilir; dizinin dışına taşmamak için.

Aşağıdan yukarı — animasyon

🏁 Yarış: temel sıralamalar vs mergesort

LEVEL 6
Sıralamanın Alt Sınırı
N lg N’den hızlısı mümkün mü?
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

Karar ağacı: her karşılaştırma bir evet/hayır sorusu

evethayıra < b ?b < c ?a < c ?a < c ?b < c ?a b cb a ca c bc a bb c ac b asol dal = evet · sağ dal = hayır · yeşil yaprak = bulunan sıra (küçükten büyüğe)

3 eleman: 3! = 6 olası sıralanış → ağacın en az 6 yaprağı olmalı.

Yüksekliği h olan ikili ağacın en fazla 2ʰ yaprağı olur → 2ʰ ≥ N! → h ≥ lg N!

Karar ağacının yüksekliği = en kötü durumdaki karşılaştırma sayısı.

Örnek: a = 7, b = 3, c = 5. “a < b?” hayır → “a < c?” hayır → “b < c?” evet → yaprak b c a (3, 5, 7). Üç soru sorduk: ağacın yüksekliği de 3.

📐 lg N! ≈ N lg N

Önerme (alt sınır)Karşılaştırmaya dayalı her sıralama algoritması, en kötü durumda en az lg N! ~ N lg N karşılaştırma yapar.
Sonuç: mergesort optimal!Mergesort en kötü durumda ~N lg N karşılaştırma yapıyor → karşılaştırma sayısı açısından daha iyisi imkânsız.

Ama: bellek açısından optimal değil (ek N). Ve karşılaştırma kullanmayan sıralamalar (sayma sıralaması, radix sort) belirli verilerde daha hızlı olabilir.

Quiz 3 · alt sınır
Biri “karşılaştırmaya dayalı ve her girdide ~N lg lg N karşılaştırma yapan” bir sıralama algoritması bulduğunu iddia ediyor. Ne dersiniz?
Karar ağacı argümanı her karşılaştırmalı algoritma için geçerli: N! farklı sonucu ayırt etmek için en az lg N! ≈ N lg N soru gerekir. Bu, matematiksel bir imkânsızlık kanıtıdır. (Belirli girdilerde — ör. neredeyse sıralı — daha az olabilir; ama “her girdide” olamaz.)
LEVEL 7
Kararlılık ve Karşılaştırıcılar
eşitlikte kim önde? · çok anahtarlı sıralama
  • böl ve fethet
  • birleştirme
  • yukarıdan aşağı mergesort
  • analiz
  • iyileştirmeler
  • aşağıdan yukarı
  • sıralamanın alt sınırı
  • kararlılık ve karşılaştırıcılar

Mergesort kararlıdır — tek bir karakter sayesinde

Birleştirmede eşitlik durumunda soldan alıyoruz:

elif aux[j] < aux[i]: # sadece KESİN küçükse sağdan
a[k] = aux[j]; j += 1
else: # eşitse SOLDAN → sıra korunur
a[k] = aux[i]; i += 1

< yerine <= yazsaydık eşitlerde sağdakini önce alırdık → kararsız!

algoritmakararlı?
insertion sort✔
selection sort✘
shellsort✘
mergesort✔
quicksort (sonraki ders)✘

🎮 Çok anahtarlı sıralama: skor tablosu

Kural: önce puan (büyükten küçüğe), puan eşitse bitirme süresi (kısadan uzuna).

🎒 Python’da (-p.score, p.time) bir demet: Python demetleri önce ilk elemana, eşitse ikinciye göre karşılaştırır. - işareti büyükten küçüğe sıralamanın kısa yolu (sayılar için).

🧪 Kod laboratuvarı: merge’ü kendin yaz

4 durum: sol yarı bitti mi? · sağ yarı bitti mi? · sağdaki küçük mü? · değilse soldakini al. Bonus: < yerine <= yaz — testler yine geçer mi? Kararlılık neden testlerde görünmüyor?

🐍 Kod laboratuvarı — Python

Bu kodda iki durum eksik: çalıştır, hata mesajını oku (IndexError), eksik iki kontrolü ekle.

Derin hikâye: Timsort’taki hatayı bir ispat aracı buldu (2015)

  • Timsort, Python’un (2002) ve sonra Java’nın, Android’in standart sıralamasıydı: milyarlarca cihazda her gün çalışıyordu.
  • Stijn de Gouw ve arkadaşları, Java’nın Timsort kodunu biçimsel doğrulama aracı KeY ile “matematiksel olarak doğru” ispatlamaya çalıştı. İspat tutmadı: çünkü kodda bir hata vardı.
  • Timsort, sıralı parçaları (run) bir yığında tutar ve yığındaki uzunluklar için bir kural (değişmez) korunmalıdır. Algoritma bu kuralı sadece yığının en üstündeki birkaç eleman için kontrol ediyordu; belirli girdilerde kural daha aşağıda bozuluyor, yığın için ayrılan sabit boyut aşılıyor ve program çöküyordu.
  • Hata Python’un özgün uygulamasında da vardı. Python düzeltmeyi hızla uyguladı; Java ilk aşamada yığın boyutunu büyüterek sorunu ötelemeyi seçti. Ekip, düzeltilmiş sürümü KeY ile doğruladı.
DersYıllarca test edilmiş, milyarlarca kez çalışmış bir kod bile hatalı olabilir: hata sadece çok büyük ve özel girdilerde ortaya çıkıyordu. Değişmez (invariant) düşüncesi bu sunumdaki birleştirme ispatının ve iyi testlerin temeli. Ayrıca: standart kütüphaneyi kullanın; bu tür hataları sizin için onlar düzeltir.

Kaynak: de Gouw ve ark. 2015 (CWI) · CPython listsort.txt

🧰 Kütüphaneler ve araçlar

adplatformne yapar / sınırlarınasıl eklenir
sorted · list.sortPythonKararlı; birleştirme tabanlı (3.11+ Powersort). Doğal sıralı parçaları kullanır.yerleşik
LINQ OrderByC#Kararlı sıralama; List.Sort ise kararsızdır. Çok anahtarlı sıralama için ThenBy.using System.Linq;
std::stable_sort · std::mergeC++Kararlı sıralama ve iki sıralı aralığı birleştirme.#include <algorithm>
heapq.mergePythonBirçok sıralı akışı tembel (lazy) birleştirme: k-yollu birleştirme.standart kütüphane
GNU sortkomut satırıBellekten büyük dosyaları geçici dosyalarla harici birleştirme sıralaması yaparak sıralar.Linux/macOS’ta hazır

🧩 Birleştirme fikri oyunlarda

  • Kararlılık gereken her yer: liderlik tablosunda eşit puanlıların önceki sırası, envanterde “önce türe, sonra isme göre”.
  • k-yollu birleştirme: birden çok oyuncunun zaman damgalı olaylarını (tekrar oynatma, ağ günlükleri) tek bir sıralı akışta birleştirmek.
  • Harici sıralama: telemetri günlükleri belleğe sığmazsa parça parça sıralayıp birleştirmek.
  • Bellek: klasik birleştirme ek dizi ister; Unity’de her çağrıda yeni dizi ayırmayın, yardımcı diziyi bir kez ayırın.
Kararlılığı bozan küçük şeyBirleştirmede eşit anahtarlarda sol yarıdan almak (<=) kararlılığı korur; < yazmak sessizce bozar. Testinize mutlaka “eşit anahtarlı kayıtların sırası korunuyor mu?” durumunu ekleyin.

🤖 Bu konuyu bir YZ kod asistanıyla yazarken

Sık yapılan hatalar — kontrol edin
  • Birleştirmede < kullanıp kararlılığı bozmak
  • Her özyinelemede yeni dizi ayırmak
  • Sınır indekslerinde bir fazla/eksik hatası
  • Kararlı sıralama gerekirken List.Sort kullanmak
İyi bir istek (prompt) örneği

“C# (.NET 8). Aşağıdan yukarı (bottom-up) mergesort yaz: yardımcı dizi bir kez ayrılsın, karşılaştırıcı parametre olsun, kararlı olsun. Sonra NUnit testleri: boş dizi, tek eleman, tersine sıralı, çok sayıda eşit anahtar (kararlılık kontrolü), 1 milyon rastgele eleman (sonucu Array.Sort + LINQ OrderBy ile karşılaştır).”

  • Kararlılık testini açıkça isteyin
  • Yardımcı diziyi bir kez ayırmasını isteyin
  • Değişmezleri (invariant) yorum olarak yazdırın
  • Standart kütüphane alternatifini de sorun

🚀 Proje ve araştırma önerileri

  1. [kolay] Kararlı liderlik tablosu — Eşit puanlıların önceki sırasını koruyan çok anahtarlı sıralama ve testi.
  2. [orta] Tekrar oynatma birleştirici — Dört oyuncunun ayrı zaman damgalı olay günlüklerini yığın tabanlı k-yollu birleştirmeyle tek akışa çevirin.
  3. [orta] Harici sıralama — Belleğe sığmayan büyük bir telemetri dosyasını parçalara bölüp sıralayıp birleştirin.
  4. [araştırma] Doğal parçalar — Timsort/Powersort’un “run” tespitini görselleştirin; neredeyse sıralı veride neden bu kadar hızlı?

📄 Akademik okumalar ve kaynaklar

de Gouw ve ark. 2015 · OpenJDK’s java.utils.Collection.sort() Is Broken: The Good, the Bad and the Worst Case (CAV 2015)
Biçimsel doğrulamayla bulunan Timsort hatası.
Peters 2002 · listsort.txt (Timsort açıklaması) (CPython kaynak kodu)
Timsort’un tasarımcısından.

Neler öğrendik? 🎒

  • Böl ve fethet: böl, ikisini sırala, birleştir.
  • Birleştirme: doğrusal zaman, yardımcı dizi.
  • Mergesort: her girdide ~N lg N karşılaştırma.
  • Bedeli: N ek bellek.
  • İyileştirmeler: küçük parçalara insertion, “zaten sıralı” kontrolü → Timsort.
  • Aşağıdan yukarı: özyinelemesiz.
  • Alt sınır: karşılaştırmalı sıralama ≥ lg N! ~ N lg N → mergesort optimal.
  • Kararlı: çok anahtarlı sıralama, skor tabloları.

Alıştırmalar 🏋️

  1. Kâğıt-kalem: E A S Y Q U E S T I O N için yukarıdan aşağı mergesort iz tablosunu çıkarın.
  2. İki sıralı bağlı listeyi yeni düğüm oluşturmadan birleştiren bir fonksiyon yazın. Sonra bağlı liste için mergesort.
  3. Ters çift sayma: mergesort’u değiştirerek bir dizideki ters çift sayısını N lg N’de bulun. (İpucu: sağdan alınan her eleman, soldaki kalan elemanların hepsiyle ters çift.) 🎮 İki oyuncunun “en iyi 10 oyun” listeleri ne kadar benziyor?
  4. Unity’de bir skor tablosunu List.Sort ve OrderBy…ThenBy ile sıralayın; eşit puanlarda sıranın değişip değişmediğini gözlemleyin.
  5. Canlı ölçüm demosunda N 4 katına çıkınca mergesort süresinin kaç katına çıktığını not edin; N lg N ile tutarlı mı?
  6. 🤖 Bir YZ asistanından “k tane sıralı listeyi birleştir” (k-way merge) isteyin. Öncelik kuyruğu kullandı mı? (İleride göreceğiz!)

🎮 Bu sunumdaki oyunlar

Adı geçen oyunu tanımıyorsanız: oyuncuların kullandığı wiki sayfası ve oyunu kısaca gösteren bir video. ▶ video slaydın üstünde açılır (Esc ile kapanır); 🎯 işaretli bağlantılar doğrudan bu sunumdaki konuya gider.

Doom 1993 · nişancı
Türü popülerleştiren nişancı; kaynak kodu 1997’de açıldı.

Kaynaklar: videolar 🎬

Kaynaklar: okuma ve görselleştirme 📚

NEXT LEVEL ▶
Quicksort
Ek bellek istemeyen, pratikte en hızlı sıralama — ve onun riskleri
SONRAKİ SUNUM ▶