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ı 🗺️
Böl ve fethet — büyük problemi küçüklere böl divide and conquer
Birleştirme — iki sıralı listeyi tek sıralı listeye merge
Yukarıdan aşağı mergesort — özyinelemeli top-down
Analiz — neden N lg N?
İyileştirmeler — küçük parçalar, zaten sıralı kontrolü, Timsort
Aşağıdan yukarı — özyinelemesiz bottom-up
Sıralamanın alt sınırı — daha hızlısı mümkün mü?
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ştirmemerge. Mergesort bunun üzerine kurulu.
Mini sözlük
divide and conquer
böl ve fethet
Problemi parçalara böl, parçaları çöz, sonuçları birleştir
merge
birleştirme
İki sıralı diziyi tek bir sıralı diziye dönüştürmek
auxiliary array
yardımcı dizi (aux)
Birleştirme için geçici kopya
top-down / bottom-up
yukarıdan aşağı / aşağıdan yukarı
Özyinelemeli / döngülü sürüm
recurrence
yineleme bağıntısı
C(N) = 2 C(N/2) + N gibi kendine atıf yapan formül
lower bound
alt sınır
Hiçbir algoritmanın daha iyisini yapamayacağı en az iş
stable
kararlı
Eşit anahtarlıların sırası korunur
comparator
karşı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
Diziyi ikiye böl.
Her yarıyı (aynı yöntemle!) sırala → özyineleme.
İ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ı.
Parçayı aux’a kopyala
i sol yarıda, j sağ yarıda başlasın
Her k için küçüğü a[k]’ye yaz, onun işaretçisini ilerlet
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.
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ü):
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 yerindein-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 aramagalloping 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
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!
algoritma
kararlı?
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.
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
[kolay]Kararlı liderlik tablosu — Eşit puanlıların önceki sırasını koruyan çok anahtarlı sıralama ve testi.
[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.
[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.
[araştırma]Doğal parçalar — Timsort/Powersort’un “run” tespitini görselleştirin; neredeyse sıralı veride neden bu kadar hızlı?
İ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 🏋️
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.
İki sıralı bağlı listeyi yeni düğüm oluşturmadan birleştiren bir fonksiyon yazın. Sonra bağlı liste için mergesort.
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?
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.
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ı?
🤖 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.
Doom1993 · nişancı
Türü popülerleştiren nişancı; kaynak kodu 1997’de açıldı.