<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://program.sonsuz.us/feed.xml" rel="self" type="application/atom+xml" /><link href="https://program.sonsuz.us/" rel="alternate" type="text/html" /><updated>2026-09-15T17:14:16+00:00</updated><id>https://program.sonsuz.us/feed.xml</id><title type="html">SonsuzUs</title><subtitle>Programlama ve Yazılım</subtitle><author><name>Sonsuz Us</name></author><entry><title type="html">Çöp Toplama Arenası: Mark-Sweep ve Nesil Bazlı GC</title><link href="https://program.sonsuz.us/posts/cop-toplama-arenasi-mark-sweep-ve-nesil-bazli-gc/" rel="alternate" type="text/html" title="Çöp Toplama Arenası: Mark-Sweep ve Nesil Bazlı GC" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/cop-toplama-arenasi-mark-sweep-ve-nesil-bazli-gc</id><content type="html" xml:base="https://program.sonsuz.us/posts/cop-toplama-arenasi-mark-sweep-ve-nesil-bazli-gc/"><![CDATA[<p>Program çalışırken oluşturulan her nesne sonsuza kadar yaşamaz. Bir kullanıcı oturumu kapanır, geçici liste işini bitirir veya fonksiyon yerel değişkenleriyle vedalaşır. Bu nesnelerin kapladığı belleği otomatik olarak geri kazanan mekanizmaya <strong>çöp toplayıcı</strong> ya da GC (Garbage Collector) denir. Ancak bellekteki çöpleri bulmak, mutfaktaki çöp kutusunu boşaltmak kadar basit değildir: Önce hangi nesnenin gerçekten sahipsiz kaldığını anlamak gerekir.</p>

<p>``</p>

<h2 id="erişilebilirlik-çöpün-matematiksel-tanımı">Erişilebilirlik: Çöpün matematiksel tanımı</h2>

<p>Belleği yönlü bir grafik olarak düşünelim. Her nesne bir düğüm, nesneler arasındaki referanslar ise kenardır. Programın doğrudan erişebildiği global değişkenler, aktif çağrı yığınları ve CPU kayıtları <strong>kök kümesini</strong> oluşturur.</p>

<p>Kök kümesi $R$, tüm nesneler kümesi $V$ ve erişilebilir nesneler kümesi $L$ olsun:</p>

\[L = \{v \in V \mid R'den\ v'ye\ bir\ yol\ vardır\}\]

<p>Dolayısıyla çöp kümesi şöyledir:</p>

\[G = V - L\]

<p>Bir nesnenin başka bir nesne tarafından işaret edilmesi tek başına yeterli değildir. Birbirini gösteren iki sahipsiz nesne de çöptür; çünkü köklerden ikisine de ulaşılamaz. Bu ayrıntı, yalnızca referans saymaya dayalı yöntemlerin döngüler karşısında neden zorlandığını açıklar.</p>

<h2 id="mark-sweep-nasıl-çalışır">Mark-sweep nasıl çalışır?</h2>

<p>Mark-sweep, adını iki temel aşamasından alır:</p>

<ol>
  <li><strong>Mark:</strong> Köklerden başlanır ve erişilebilen bütün nesneler işaretlenir.</li>
  <li><strong>Sweep:</strong> Bellek taranır; işaretlenmemiş nesneler serbest bırakılır.</li>
</ol>

<p>Basitleştirilmiş sözde kod şöyledir:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function collect(roots, heap):
    for root in roots:
        markRecursively(root)

    for object in heap:
        if object.marked:
            object.marked = false
        else:
            free(object)
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">markRecursively</code>, nesnenin referans verdiği komşuları dolaşır. İşaretlerin temizlenmesi ise bir sonraki toplama turuna hazırlıktır. Mark-sweep döngüsel yapıları başarıyla toplar ve nesneleri taşımadığı için adreslerin sabit kalmasını sağlar. Buna karşılık tüm yığını taramak gecikmeye neden olabilir. Ayrıca boşalan alanlar dağınık kalır ve <strong>bellek parçalanması</strong> oluşabilir.</p>

<h2 id="nesil-bazlı-gc-gençler-hızlı-yaşar">Nesil bazlı GC: Gençler hızlı yaşar</h2>

<p>Nesil bazlı GC, deneysel bir gözleme dayanır: Yeni oluşturulan nesnelerin büyük bölümü kısa sürede ölür. Buna <strong>zayıf nesil hipotezi</strong> denir. Bellek genellikle genç, yaşlı ve bazen kalıcı bölgeler şeklinde ayrılır.</p>

<p>Bir nesnenin hayatta kalma olasılığı $p$ ise, art arda $k$ toplama turundan sağ çıkma olasılığı kabaca:</p>

\[P(yaşam) = p^k\]

<p>Genç bölgede sık fakat küçük çaplı <strong>minor GC</strong> çalışır. Birkaç tur hayatta kalan nesneler yaşlı bölgeye terfi ettirilir. Yaşlı bölge daha seyrek taranır; kapsamlı işlem çoğunlukla <strong>major</strong> veya <strong>full GC</strong> olarak adlandırılır.</p>

<p>Genç nesneleri tararken yaşlı bir nesnenin genç bir nesneye verdiği referans kaybolmamalıdır. Bu nedenle çalışma zamanı, referans güncellemelerini izleyen <strong>write barrier</strong> ve hatırlanan kümeler kullanır. Böylece her minor GC sırasında bütün yaşlı bölgeyi taramak gerekmez.</p>

<h2 id="yan-yana-karşılaştırma">Yan yana karşılaştırma</h2>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Mark-sweep</th>
      <th>Nesil bazlı GC</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Temel fikir</td>
      <td>Erişilebilirleri işaretle, kalanları temizle</td>
      <td>Nesneleri yaşlarına göre ayır</td>
    </tr>
    <tr>
      <td>Tarama alanı</td>
      <td>Genellikle tüm yığın</td>
      <td>Çoğunlukla genç nesil</td>
    </tr>
    <tr>
      <td>Kısa ömürlü nesneler</td>
      <td>Özel optimizasyon yok</td>
      <td>Çok verimli</td>
    </tr>
    <tr>
      <td>Uygulama karmaşıklığı</td>
      <td>Görece düşük</td>
      <td>Write barrier nedeniyle yüksek</td>
    </tr>
    <tr>
      <td>Duraklama</td>
      <td>Büyük yığında uzayabilir</td>
      <td>Minor GC kısa; full GC uzun olabilir</td>
    </tr>
    <tr>
      <td>Parçalanma</td>
      <td>Oluşabilir</td>
      <td>Kopyalama veya sıkıştırmayla azaltılabilir</td>
    </tr>
  </tbody>
</table>

<p>Burada önemli bir nüans vardır: Nesil bazlı GC, mark-sweep’in mutlaka rakibi değildir. Modern çalışma zamanları genç bölgede kopyalamalı toplama, yaşlı bölgede ise mark-sweep veya mark-compact kullanabilir. Yani nesil yaklaşımı, nesnelerin <strong>nerede ve ne sıklıkta</strong> toplanacağını; mark-sweep ise <strong>nasıl</strong> tespit edilip temizleneceğini anlatır.</p>

<h2 id="hangisi-ne-zaman-mantıklı">Hangisi ne zaman mantıklı?</h2>

<p>Basitlik, sabit nesne adresleri veya sınırlı çalışma zamanı altyapısı önemliyse mark-sweep iyi bir başlangıçtır. Web sunucuları, sanal makineler ve yoğun şekilde geçici nesne oluşturan uygulamalar ise nesil bazlı GC’den ciddi fayda görür. Yine de gecikmeye duyarlı sistemlerde ortalama hız kadar en uzun duraklama da ölçülmelidir. Kısacası en iyi GC, yalnızca çöpleri bulan değil, programın çalışma karakterine en az sürprizle uyum sağlayandır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="çöp toplama" /><category term="garbage collection" /><category term="mark-sweep" /><category term="nesil bazlı gc" /><category term="bellek yönetimi" /><category term="programlama" /><summary type="html"><![CDATA[Program çalışırken oluşturulan her nesne sonsuza kadar yaşamaz. Bir kullanıcı oturumu kapanır, geçici liste işini bitirir veya fonksiyon yerel değişkenleriyle vedalaşır. Bu nesnelerin kapladığı belleği otomatik olarak geri kazanan mekanizmaya çöp toplayıcı ya da GC (Garbage Collector) denir. Ancak bellekteki çöpleri bulmak, mutfaktaki çöp kutusunu boşaltmak kadar basit değildir: Önce hangi nesnenin gerçekten sahipsiz kaldığını anlamak gerekir.]]></summary></entry><entry><title type="html">Digit DP ile Dev Sayı Aralıklarında Basamak Basamak Sayma</title><link href="https://program.sonsuz.us/posts/digit-dp-ile-dev-sayi-araliklarinda-basamak-basamak-sayma/" rel="alternate" type="text/html" title="Digit DP ile Dev Sayı Aralıklarında Basamak Basamak Sayma" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/digit-dp-ile-dev-sayi-araliklarinda-basamak-basamak-sayma</id><content type="html" xml:base="https://program.sonsuz.us/posts/digit-dp-ile-dev-sayi-araliklarinda-basamak-basamak-sayma/"><![CDATA[<p>Bir aralıkta belirli özelliklere sahip kaç sayı bulunduğunu hesaplamak bazen göründüğünden çok daha zordur. Örneğin, $1$ ile $10^{18}$ arasında rakamları toplamı 42 olan veya içinde hiç 7 geçmeyen sayıları tek tek kontrol edemeyiz. Digit DP, yani basamak dinamik programlama, tam burada devreye girerek sayıları değil, sayıların basamaklarında oluşabilecek durumları sayar.
``</p>
<h2 id="temel-fikir-aralığı-üst-sınıra-dönüştürmek">Temel fikir: Aralığı üst sınıra dönüştürmek</h2>

<p>Digit DP problemleri çoğunlukla şu fonksiyonla çözülür:</p>

\[F(N) = [0,N] \text{ aralığında koşulu sağlayan sayı adedi}\]

<p>Böylece $[L,R]$ aralığının cevabı basitçe</p>

\[Cevap = F(R) - F(L-1)\]

<p>olur. Bu dönüşüm önemlidir; çünkü yalnızca “$N$ sayısını aşmadan basamakları nasıl seçerim?” sorusuna odaklanmamızı sağlar.</p>

<p>Sayının basamaklarını soldan sağa gezerken genellikle aşağıdaki durumlar tutulur:</p>

<table>
  <thead>
    <tr>
      <th>Durum</th>
      <th>Anlamı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">pos</code></td>
      <td>Şu anda işlenen basamağın konumu</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">tight</code></td>
      <td>Seçimler hâlâ üst sınırın basamaklarıyla aynı mı?</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">started</code></td>
      <td>Sayının anlamlı kısmı başladı mı?</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">state</code></td>
      <td>Rakam toplamı, kalan veya önceki rakam gibi probleme özel bilgi</td>
    </tr>
  </tbody>
</table>

<p><code class="language-plaintext highlighter-rouge">tight</code> doğruysa mevcut basamak en fazla $N$’nin aynı konumdaki rakamı olabilir. Yanlışsa artık sınırdan küçük bir sayı oluşturduğumuz kesindir ve $0$ ile $9$ arasındaki tüm rakamları özgürce deneyebiliriz.</p>

<h2 id="neden-klasik-döngü-yetmez">Neden klasik döngü yetmez?</h2>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th style="text-align: right">Zaman karmaşıklığı</th>
      <th>Uygun aralık</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Her sayıyı kontrol etme</td>
      <td style="text-align: right">$O(N \cdot d)$</td>
      <td>Küçük sınırlar</td>
    </tr>
    <tr>
      <td>Kombinasyon formülleri</td>
      <td style="text-align: right">Probleme bağlı</td>
      <td>Basit koşullar</td>
    </tr>
    <tr>
      <td>Digit DP</td>
      <td style="text-align: right">Yaklaşık $O(d \cdot durum \cdot 10)$</td>
      <td>$10^{18}$ gibi sınırlar</td>
    </tr>
  </tbody>
</table>

<p>Burada $d$, basamak sayısıdır. Digit DP aynı <code class="language-plaintext highlighter-rouge">pos</code>, <code class="language-plaintext highlighter-rouge">tight</code> ve problem durumuna tekrar ulaşıldığında sonucu önbellekten getirir. Böylece devasa sayı uzayı küçük bir durum grafiğine dönüşür.</p>

<h2 id="örnek-rakamları-toplamı-hedefe-eşit-sayılar">Örnek: Rakamları toplamı hedefe eşit sayılar</h2>

<p>Aşağıdaki Python kodu, $0$ ile $N$ arasında rakamları toplamı <code class="language-plaintext highlighter-rouge">target</code> olan sayıların adedini hesaplar:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="n">lru_cache</span>

<span class="k">def</span> <span class="nf">count_up_to</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">return</span> <span class="mi">0</span>

    <span class="n">digits</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">n</span><span class="p">)))</span>

    <span class="nd">@lru_cache</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">dp</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">total</span><span class="p">,</span> <span class="n">tight</span><span class="p">):</span>
        <span class="c1"># Tüm basamaklar seçildiyse toplamı kontrol et.
</span>        <span class="k">if</span> <span class="n">pos</span> <span class="o">==</span> <span class="nf">len</span><span class="p">(</span><span class="n">digits</span><span class="p">):</span>
            <span class="k">return</span> <span class="nf">int</span><span class="p">(</span><span class="n">total</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span>

        <span class="c1"># Toplam hedefi geçtiyse devam etmek gereksizdir.
</span>        <span class="k">if</span> <span class="n">total</span> <span class="o">&gt;</span> <span class="n">target</span><span class="p">:</span>
            <span class="k">return</span> <span class="mi">0</span>

        <span class="n">limit</span> <span class="o">=</span> <span class="n">digits</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="k">if</span> <span class="n">tight</span> <span class="k">else</span> <span class="mi">9</span>
        <span class="n">answer</span> <span class="o">=</span> <span class="mi">0</span>

        <span class="k">for</span> <span class="n">digit</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">limit</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
            <span class="n">next_tight</span> <span class="o">=</span> <span class="n">tight</span> <span class="ow">and</span> <span class="p">(</span><span class="n">digit</span> <span class="o">==</span> <span class="n">limit</span><span class="p">)</span>
            <span class="n">answer</span> <span class="o">+=</span> <span class="nf">dp</span><span class="p">(</span><span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">total</span> <span class="o">+</span> <span class="n">digit</span><span class="p">,</span> <span class="n">next_tight</span><span class="p">)</span>

        <span class="k">return</span> <span class="n">answer</span>

    <span class="k">return</span> <span class="nf">dp</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">count_in_range</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
    <span class="k">return</span> <span class="nf">count_up_to</span><span class="p">(</span><span class="n">right</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span> <span class="o">-</span> <span class="nf">count_up_to</span><span class="p">(</span><span class="n">left</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">count_in_range</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">10000</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>
</code></pre></div></div>

<p>Fonksiyondaki baştaki sıfırlar sorun oluşturmaz: <code class="language-plaintext highlighter-rouge">0073</code>, sayısal olarak 73’ü temsil eder ve rakam toplamı değişmez. Ancak “kaç basamaklı?”, “ilk rakam nedir?” veya “yan yana iki eşit rakam var mı?” gibi koşullarda <code class="language-plaintext highlighter-rouge">started</code> durumu eklenmelidir. Böylece henüz başlamamış sıfırlar gerçek basamaklarla karıştırılmaz.</p>

<h2 id="durumu-doğru-tasarlamak">Durumu doğru tasarlamak</h2>

<p>Digit DP’nin zor kısmı kod değil, geçmişten hangi bilginin geleceği etkilediğini bulmaktır. Bölünebilirlik için <code class="language-plaintext highlighter-rouge">remainder</code>, yasaklı alt dize için otomat durumu, ardışık rakamlar için <code class="language-plaintext highlighter-rouge">previous_digit</code> tutulabilir. Örneğin yeni kalan</p>

\[yeni = (eski \cdot 10 + rakam) \bmod K\]

<p>şeklinde güncellenir.</p>

<p>Her ayrıntıyı duruma eklemek belleği şişirir; eksik bilgi eklemek ise yanlış sonuç üretir. Altın kural şudur: Gelecekteki seçimlerin sonucunu etkileyen en küçük bilgiyi sakla. Bu denge kurulduğunda Digit DP, astronomik aralıkları birkaç bin durumla sayan oldukça güçlü ve şaşırtıcı derecede eğlenceli bir tekniğe dönüşür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="digit dp" /><category term="dinamik programlama" /><category term="algoritma" /><category term="basamak" /><category term="sayma" /><category term="rekabetçi programlama" /><summary type="html"><![CDATA[Bir aralıkta belirli özelliklere sahip kaç sayı bulunduğunu hesaplamak bazen göründüğünden çok daha zordur. Örneğin, $1$ ile $10^{18}$ arasında rakamları toplamı 42 olan veya içinde hiç 7 geçmeyen sayıları tek tek kontrol edemeyiz. Digit DP, yani basamak dinamik programlama, tam burada devreye girerek sayıları değil, sayıların basamaklarında oluşabilecek durumları sayar.]]></summary></entry><entry><title type="html">Dinic Algoritması: Katmanlı Ağlarla Maksimum Akışı Hızlandırmak</title><link href="https://program.sonsuz.us/posts/dinic-algoritmasi-katmanli-aglarla-maksimum-akisi-hizlandirmak/" rel="alternate" type="text/html" title="Dinic Algoritması: Katmanlı Ağlarla Maksimum Akışı Hızlandırmak" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/dinic-algoritmasi-katmanli-aglarla-maksimum-akisi-hizlandirmak</id><content type="html" xml:base="https://program.sonsuz.us/posts/dinic-algoritmasi-katmanli-aglarla-maksimum-akisi-hizlandirmak/"><![CDATA[<p>Bir boru hattından taşınabilecek suyu, bir ağın kaldırabileceği veri trafiğini veya depodan mağazalara gönderilebilecek ürün miktarını hesaplamak istediğimizi düşünelim. Bu problemlerin ortak noktası, belirli kapasitelere sahip bağlantılardan kaynaktan hedefe mümkün olan en büyük akışı göndermektir. Dinic algoritması, katmanlı ağ fikrini kullanarak bu maksimum akışı verimli biçimde bulur.
``</p>
<h2 id="maksimum-akışın-teorik-temeli">Maksimum akışın teorik temeli</h2>

<p>Yönlü bir $G=(V,E)$ grafında her $(u,v)$ kenarı için bir $c(u,v)$ kapasitesi bulunur. Akış değeri $f(u,v)$ şu kısıtları sağlamalıdır:</p>

\[0 \leq f(u,v) \leq c(u,v)\]

<p>Kaynak $s$ ve hedef $t$ dışındaki her düğümde akış korunur:</p>

\[\sum_{u} f(u,v)=\sum_{w} f(v,w)\]

<p>Başka bir deyişle, ara düğüme giren miktar oradan çıkmalıdır; düğüm gizlice su içemez! Amaç, $s$ düğümünden çıkan toplam akışı maksimize etmektir.</p>

<p>Algoritmalar bunu <strong>artık ağ</strong> üzerinden yapar. Bir kenardan akış gönderildiğinde ileri yöndeki kullanılabilir kapasite azalırken ters yönde aynı miktarda kapasite oluşur. Bu ters kenarlar, daha önce verilen kötü bir kararı geri alıp akışı farklı bir rotaya taşıyabilmemizi sağlar.</p>

<h2 id="dinicin-iki-temel-adımı">Dinic’in iki temel adımı</h2>

<p>Dinic algoritması her turda iki işlem gerçekleştirir:</p>

<ol>
  <li><strong>BFS ile katmanlı ağ kurar.</strong> Her düğüme kaynaktan olan kenar mesafesi atanır. Yalnızca <code class="language-plaintext highlighter-rouge">level[v] = level[u] + 1</code> koşulunu sağlayan kenarlar kullanılır.</li>
  <li><strong>DFS ile bloklayan akış gönderir.</strong> Katmanlı ağda ilerletilebilecek akış kalmayıncaya kadar uygun yollar değerlendirilir.</li>
</ol>

<p>Hedef BFS sonucunda erişilemez olduğunda artırma yolu kalmamıştır ve bulunan akış maksimumdur.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Yol seçimi</th>
      <th style="text-align: right">Genel karmaşıklık</th>
      <th>Temel fikir</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Ford–Fulkerson</td>
      <td>Değişken</td>
      <td style="text-align: right">Kapasitelere bağlı</td>
      <td>Herhangi bir artırma yolu</td>
    </tr>
    <tr>
      <td>Edmonds–Karp</td>
      <td>BFS</td>
      <td style="text-align: right">$O(VE^2)$</td>
      <td>En kısa artırma yolu</td>
    </tr>
    <tr>
      <td>Dinic</td>
      <td>BFS + DFS</td>
      <td style="text-align: right">$O(V^2E)$</td>
      <td>Katmanlı ağda bloklayan akış</td>
    </tr>
  </tbody>
</table>

<p>Dinic’in genel karmaşıklığı $O(V^2E)$ olsa da pratikte çoğu ağda oldukça hızlıdır. Birim kapasiteli özel graflarda daha güçlü sınırlar elde edilebilir.</p>

<h2 id="c-ile-uygulama">C++ ile uygulama</h2>

<p>Aşağıdaki uygulamada her gerçek kenarla birlikte kapasitesi başlangıçta sıfır olan ters kenar eklenir. <code class="language-plaintext highlighter-rouge">work</code> dizisi, DFS’nin sonuç vermeyen kenarları tekrar tekrar incelemesini önler.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">struct</span> <span class="nc">Edge</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">to</span><span class="p">,</span> <span class="n">rev</span><span class="p">;</span>
    <span class="kt">long</span> <span class="kt">long</span> <span class="n">cap</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">class</span> <span class="nc">Dinic</span> <span class="p">{</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">Edge</span><span class="o">&gt;&gt;</span> <span class="n">graph</span><span class="p">;</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">level</span><span class="p">,</span> <span class="n">work</span><span class="p">;</span>

    <span class="kt">bool</span> <span class="n">bfs</span><span class="p">(</span><span class="kt">int</span> <span class="n">source</span><span class="p">,</span> <span class="kt">int</span> <span class="n">sink</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fill</span><span class="p">(</span><span class="n">level</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">level</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">queue</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">q</span><span class="p">;</span>
        <span class="n">level</span><span class="p">[</span><span class="n">source</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">source</span><span class="p">);</span>

        <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">q</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
            <span class="kt">int</span> <span class="n">u</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">front</span><span class="p">();</span> <span class="n">q</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span>
            <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="n">Edge</span><span class="o">&amp;</span> <span class="n">e</span> <span class="o">:</span> <span class="n">graph</span><span class="p">[</span><span class="n">u</span><span class="p">])</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">cap</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">level</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">to</span><span class="p">]</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">level</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">to</span><span class="p">]</span> <span class="o">=</span> <span class="n">level</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
                    <span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">to</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">level</span><span class="p">[</span><span class="n">sink</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">long</span> <span class="kt">long</span> <span class="nf">dfs</span><span class="p">(</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">sink</span><span class="p">,</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">flow</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">u</span> <span class="o">==</span> <span class="n">sink</span><span class="p">)</span> <span class="k">return</span> <span class="n">flow</span><span class="p">;</span>

        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span><span class="o">&amp;</span> <span class="n">i</span> <span class="o">=</span> <span class="n">work</span><span class="p">[</span><span class="n">u</span><span class="p">];</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">graph</span><span class="p">[</span><span class="n">u</span><span class="p">].</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">Edge</span><span class="o">&amp;</span> <span class="n">e</span> <span class="o">=</span> <span class="n">graph</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">i</span><span class="p">];</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">cap</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">level</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">to</span><span class="p">]</span> <span class="o">!=</span> <span class="n">level</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

            <span class="kt">long</span> <span class="kt">long</span> <span class="n">sent</span> <span class="o">=</span> <span class="n">dfs</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">to</span><span class="p">,</span> <span class="n">sink</span><span class="p">,</span> <span class="n">min</span><span class="p">(</span><span class="n">flow</span><span class="p">,</span> <span class="n">e</span><span class="p">.</span><span class="n">cap</span><span class="p">));</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">sent</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">e</span><span class="p">.</span><span class="n">cap</span> <span class="o">-=</span> <span class="n">sent</span><span class="p">;</span>
                <span class="n">graph</span><span class="p">[</span><span class="n">e</span><span class="p">.</span><span class="n">to</span><span class="p">][</span><span class="n">e</span><span class="p">.</span><span class="n">rev</span><span class="p">].</span><span class="n">cap</span> <span class="o">+=</span> <span class="n">sent</span><span class="p">;</span>
                <span class="k">return</span> <span class="n">sent</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>

<span class="k">public</span><span class="o">:</span>
    <span class="k">explicit</span> <span class="nf">Dinic</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="o">:</span> <span class="n">graph</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">level</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">work</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">{}</span>

    <span class="kt">void</span> <span class="nf">addEdge</span><span class="p">(</span><span class="kt">int</span> <span class="n">from</span><span class="p">,</span> <span class="kt">int</span> <span class="n">to</span><span class="p">,</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">capacity</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">Edge</span> <span class="n">forward</span><span class="p">{</span><span class="n">to</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">graph</span><span class="p">[</span><span class="n">to</span><span class="p">].</span><span class="n">size</span><span class="p">(),</span> <span class="n">capacity</span><span class="p">};</span>
        <span class="n">Edge</span> <span class="n">reverse</span><span class="p">{</span><span class="n">from</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">graph</span><span class="p">[</span><span class="n">from</span><span class="p">].</span><span class="n">size</span><span class="p">(),</span> <span class="mi">0</span><span class="p">};</span>
        <span class="n">graph</span><span class="p">[</span><span class="n">from</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="n">forward</span><span class="p">);</span>
        <span class="n">graph</span><span class="p">[</span><span class="n">to</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="n">reverse</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">long</span> <span class="kt">long</span> <span class="nf">maxFlow</span><span class="p">(</span><span class="kt">int</span> <span class="n">source</span><span class="p">,</span> <span class="kt">int</span> <span class="n">sink</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">long</span> <span class="kt">long</span> <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">sent</span><span class="p">;</span>
        <span class="k">const</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">INF</span> <span class="o">=</span> <span class="n">numeric_limits</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;::</span><span class="n">max</span><span class="p">();</span>

        <span class="k">while</span> <span class="p">(</span><span class="n">bfs</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">sink</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">fill</span><span class="p">(</span><span class="n">work</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">work</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="mi">0</span><span class="p">);</span>
            <span class="k">while</span> <span class="p">((</span><span class="n">sent</span> <span class="o">=</span> <span class="n">dfs</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">sink</span><span class="p">,</span> <span class="n">INF</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
                <span class="n">total</span> <span class="o">+=</span> <span class="n">sent</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">total</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<h2 id="neden-katmanlar-hız-kazandırır">Neden katmanlar hız kazandırır?</h2>

<p>Sıradan artırma yaklaşımı aynı bölgelerde dolaşabilir. Katmanlı ağ ise DFS’yi yalnızca hedefe doğru ilerleyen kenarlarla sınırlar. Bir BFS turunda bulunan bloklayan akış, en az bir kritik geçişi tamamen doldurur. Sonraki turda hedefe olan katman mesafesi artar; bu mesafe en fazla $V-1$ olabileceğinden BFS turlarının sayısı da sınırlıdır.</p>

<p>Dinic; ağ yönlendirme, bipartite eşleme, görev atama ve kapasite planlama problemlerinde güçlü bir seçimdir. Özellikle graf büyük, fakat bağlantılar nispeten seyrekse katmanlama ile gereksiz aramaların azaltılması belirgin bir performans avantajı sağlar.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="dinic" /><category term="maksimum-akış" /><category term="graf-algoritmaları" /><category term="katmanlı-ağ" /><category term="c++" /><category term="algoritma" /><summary type="html"><![CDATA[Bir boru hattından taşınabilecek suyu, bir ağın kaldırabileceği veri trafiğini veya depodan mağazalara gönderilebilecek ürün miktarını hesaplamak istediğimizi düşünelim. Bu problemlerin ortak noktası, belirli kapasitelere sahip bağlantılardan kaynaktan hedefe mümkün olan en büyük akışı göndermektir. Dinic algoritması, katmanlı ağ fikrini kullanarak bu maksimum akışı verimli biçimde bulur.]]></summary></entry><entry><title type="html">Gale-Shapley Algoritması: Kararlı Eşleştirmenin Matematiği</title><link href="https://program.sonsuz.us/posts/gale-shapley-algoritmasi-kararli-eslestirmenin-matematigi/" rel="alternate" type="text/html" title="Gale-Shapley Algoritması: Kararlı Eşleştirmenin Matematiği" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/gale-shapley-algoritmasi-kararli-eslestirmenin-matematigi</id><content type="html" xml:base="https://program.sonsuz.us/posts/gale-shapley-algoritmasi-kararli-eslestirmenin-matematigi/"><![CDATA[<p>Bir grup doktoru hastanelere, öğrencileri üniversitelere veya kullanıcıları tercihlerine göre oyun sunucularına yerleştirdiğimizi düşünelim. Herkesin bir tercih listesi var ve bazı eşleşmeler diğerlerinden daha cazip. Rastgele seçim yapmak kolaydır; zor olan, kimsenin mevcut eşini bırakıp başka biriyle karşılıklı olarak eşleşmek istemediği <strong>kararlı</strong> bir sonuç bulmaktır. İşte Gale-Shapley algoritması bu sosyal dramayı düzenli, kanıtlanabilir ve verimli bir sürece dönüştürür.
``</p>

<h2 id="kararlı-eşleştirme-nedir">Kararlı eşleştirme nedir?</h2>

<p>Problemde genellikle eşit büyüklükte iki küme bulunur: $A$ ve $B$. Her katılımcı, karşı kümedeki kişileri en çok tercih edilenden en aza doğru sıralar. Amaç, her elemanın tam bir kişiyle eşleştirildiği bir sonuç üretmektir.</p>

<p>Bir eşleştirmede $a \in A$ ve $b \in B$, mevcut eşlerinden daha çok birbirlerini tercih ediyorsa bu ikiliye <strong>engelleyici çift</strong> denir. Kararlı eşleştirmede böyle bir çift bulunmaz.</p>

\[\text{Kararlı}(M) \iff \nexists(a,b): b \succ_a M(a) \land a \succ_b M(b)\]

<p>Burada $M(a)$, $a$ katılımcısının mevcut eşini; $\succ_a$ ise $a$ açısından “daha çok tercih edilir” ilişkisini gösterir.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th>Anlamı</th>
      <th>Sonuca etkisi</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tam eşleştirme</td>
      <td>Herkesin bir eşi vardır</td>
      <td>Kimse açıkta kalmaz</td>
    </tr>
    <tr>
      <td>Engelleyici çift</td>
      <td>Birbirlerini mevcut eşlerinden çok isteyen ikili</td>
      <td>Kararlılığı bozar</td>
    </tr>
    <tr>
      <td>Kararlı eşleştirme</td>
      <td>Engelleyici çift içermeyen sonuç</td>
      <td>Tarafların kaçıp yeni çift kurmasını önler</td>
    </tr>
    <tr>
      <td>Optimal eşleştirme</td>
      <td>Bir taraf için mümkün olan en iyi kararlı sonuç</td>
      <td>Teklif yapan tarafı avantajlı kılar</td>
    </tr>
  </tbody>
</table>

<h2 id="algoritma-nasıl-çalışır">Algoritma nasıl çalışır?</h2>

<p>Gale-Shapley, <strong>ertelenmiş kabul</strong> yaklaşımını kullanır. $A$ tarafının teklif verdiğini düşünelim:</p>

<ol>
  <li>Eşi olmayan bir $A$ katılımcısı, henüz teklif etmediği en yüksek tercihine gider.</li>
  <li>Teklifi alan $B$ katılımcısı boşsa teklifi geçici olarak kabul eder.</li>
  <li>Zaten bir teklifi tutuyorsa iki adayı karşılaştırır; daha çok tercih ettiğini tutup diğerini reddeder.</li>
  <li>Reddedilen kişi sıradaki tercihine teklif verir.</li>
  <li>Eşleşmemiş teklif sahibi kalmayana kadar süreç devam eder.</li>
</ol>

<p>“Geçici kabul” kritik ayrıntıdır. Bir katılımcı iyi bir teklifi tutabilir, ancak daha iyisi gelirse fikrini değiştirebilir. Romantik komedi gibi görünse de süreç kesinlikle sona erer: Her teklif sahibi karşı taraftaki herkese en fazla bir kez teklif eder. $n$ kişilik iki grup için en fazla $n^2$ teklif yapılır; dolayısıyla zaman karmaşıklığı $O(n^2)$ olur.</p>

<h2 id="python-ile-uygulama">Python ile uygulama</h2>

<p>Aşağıdaki kod, teklif alanların tercih sıralarını sözlük biçiminde önceden hesaplar. Böylece iki adayı karşılaştırmak $O(1)$ zamanda gerçekleştirilebilir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">gale_shapley</span><span class="p">(</span><span class="n">proposers</span><span class="p">,</span> <span class="n">receivers</span><span class="p">):</span>
    <span class="n">free</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="n">proposers</span><span class="p">)</span>
    <span class="n">next_choice</span> <span class="o">=</span> <span class="p">{</span><span class="n">p</span><span class="p">:</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">proposers</span><span class="p">}</span>
    <span class="n">engaged</span> <span class="o">=</span> <span class="p">{}</span>

    <span class="c1"># Küçük sıra değeri, daha yüksek tercih anlamına gelir.
</span>    <span class="n">rank</span> <span class="o">=</span> <span class="p">{</span>
        <span class="n">r</span><span class="p">:</span> <span class="p">{</span><span class="n">p</span><span class="p">:</span> <span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">p</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">order</span><span class="p">)}</span>
        <span class="k">for</span> <span class="n">r</span><span class="p">,</span> <span class="n">order</span> <span class="ow">in</span> <span class="n">receivers</span><span class="p">.</span><span class="nf">items</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">while</span> <span class="n">free</span><span class="p">:</span>
        <span class="n">p</span> <span class="o">=</span> <span class="n">free</span><span class="p">.</span><span class="nf">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="n">r</span> <span class="o">=</span> <span class="n">proposers</span><span class="p">[</span><span class="n">p</span><span class="p">][</span><span class="n">next_choice</span><span class="p">[</span><span class="n">p</span><span class="p">]]</span>
        <span class="n">next_choice</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>

        <span class="k">if</span> <span class="n">r</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">engaged</span><span class="p">:</span>
            <span class="n">engaged</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">current</span> <span class="o">=</span> <span class="n">engaged</span><span class="p">[</span><span class="n">r</span><span class="p">]</span>
            <span class="k">if</span> <span class="n">rank</span><span class="p">[</span><span class="n">r</span><span class="p">][</span><span class="n">p</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">rank</span><span class="p">[</span><span class="n">r</span><span class="p">][</span><span class="n">current</span><span class="p">]:</span>
                <span class="n">engaged</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span>
                <span class="n">free</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">current</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">free</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>

    <span class="k">return</span> <span class="p">{</span><span class="n">p</span><span class="p">:</span> <span class="n">r</span> <span class="k">for</span> <span class="n">r</span><span class="p">,</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">engaged</span><span class="p">.</span><span class="nf">items</span><span class="p">()}</span>

<span class="n">students</span> <span class="o">=</span> <span class="p">{</span>
    <span class="sh">"</span><span class="s">Ada</span><span class="sh">"</span><span class="p">:</span> <span class="p">[</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Y</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Z</span><span class="sh">"</span><span class="p">],</span>
    <span class="sh">"</span><span class="s">Bora</span><span class="sh">"</span><span class="p">:</span> <span class="p">[</span><span class="sh">"</span><span class="s">Y</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Z</span><span class="sh">"</span><span class="p">],</span>
    <span class="sh">"</span><span class="s">Cem</span><span class="sh">"</span><span class="p">:</span> <span class="p">[</span><span class="sh">"</span><span class="s">Y</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Z</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">]</span>
<span class="p">}</span>

<span class="n">schools</span> <span class="o">=</span> <span class="p">{</span>
    <span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">:</span> <span class="p">[</span><span class="sh">"</span><span class="s">Bora</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Ada</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Cem</span><span class="sh">"</span><span class="p">],</span>
    <span class="sh">"</span><span class="s">Y</span><span class="sh">"</span><span class="p">:</span> <span class="p">[</span><span class="sh">"</span><span class="s">Ada</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Cem</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Bora</span><span class="sh">"</span><span class="p">],</span>
    <span class="sh">"</span><span class="s">Z</span><span class="sh">"</span><span class="p">:</span> <span class="p">[</span><span class="sh">"</span><span class="s">Cem</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Bora</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Ada</span><span class="sh">"</span><span class="p">]</span>
<span class="p">}</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">gale_shapley</span><span class="p">(</span><span class="n">students</span><span class="p">,</span> <span class="n">schools</span><span class="p">))</span>
</code></pre></div></div>

<h2 id="neden-sonuç-garanti-edilir">Neden sonuç garanti edilir?</h2>

<p>Algoritma sonlandığında reddedilmiş bir teklif sahibi, onu reddeden kişinin mevcut veya daha çok tercih ettiği bir adayı tuttuğunu bilir. Bu nedenle sonradan engelleyici çift oluşturamaz. Ayrıca teklifler sonlu olduğu için algoritma sonsuz döngüye girmez.</p>

<p>Önemli bir nüans vardır: Kararlı sonuç her zaman herkes için eşit derecede iyi değildir. Teklif yapan taraf, tüm kararlı eşleştirmeler arasındaki kendisi için en iyi sonucu alırken diğer taraf açısından sonuç daha az avantajlı olabilir. Tercihlerde eşitlikler, eksik listeler veya farklı kapasiteler bulunduğunda model genişletilmelidir. Yine de temel fikir değişmez: Yerel tercihler dikkatlice yönetildiğinde küresel bir kararlılık elde edilebilir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="gale-shapley" /><category term="kararlı eşleştirme" /><category term="algoritma" /><category term="python" /><category term="ayrık matematik" /><summary type="html"><![CDATA[Bir grup doktoru hastanelere, öğrencileri üniversitelere veya kullanıcıları tercihlerine göre oyun sunucularına yerleştirdiğimizi düşünelim. Herkesin bir tercih listesi var ve bazı eşleşmeler diğerlerinden daha cazip. Rastgele seçim yapmak kolaydır; zor olan, kimsenin mevcut eşini bırakıp başka biriyle karşılıklı olarak eşleşmek istemediği kararlı bir sonuç bulmaktır. İşte Gale-Shapley algoritması bu sosyal dramayı düzenli, kanıtlanabilir ve verimli bir sürece dönüştürür.]]></summary></entry><entry><title type="html">Hindley-Milner Tip Çıkarımı: Tip Yazmadan Tip Güvenliği</title><link href="https://program.sonsuz.us/posts/hindley-milner-tip-cikarimi-tip-yazmadan-tip-guvenligi/" rel="alternate" type="text/html" title="Hindley-Milner Tip Çıkarımı: Tip Yazmadan Tip Güvenliği" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/hindley-milner-tip-cikarimi-tip-yazmadan-tip-guvenligi</id><content type="html" xml:base="https://program.sonsuz.us/posts/hindley-milner-tip-cikarimi-tip-yazmadan-tip-guvenligi/"><![CDATA[<p>Bir fonksiyon yazdığınızı, hiçbir parametreye tip eklemediğinizi ve derleyicinin yine de bütün tip hatalarını yakaladığını düşünün. Üstelik derleyici yalnızca kodun doğru olup olmadığını söylemekle kalmasın, mümkün olan en genel tipi de keşfetsin! Hindley-Milner, yani HM tip sistemi, fonksiyonel programlama dünyasının bu etkileyici numarasını matematiksel olarak gerçekleştirir.</p>

<p>``</p>

<h2 id="temel-fikir-i̇fadeler-kısıt-üretir">Temel fikir: İfadeler kısıt üretir</h2>

<p>HM sisteminde her ifadeye başlangıçta bilinmeyen bir tip değişkeni atanabilir. Bu değişkenler genellikle $\alpha$, $\beta$ ve $\gamma$ ile gösterilir. Ardından programın yapısı incelenerek tip denklemleri oluşturulur.</p>

<p>Örneğin şu fonksiyonu ele alalım:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">f</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">x</code> için $\alpha$ tipini seçersek fonksiyon aynı tipte bir değer döndürür. Dolayısıyla:</p>

\[f : \alpha \rightarrow \alpha\]

<p>Buradaki $\alpha$, belirli bir tip değildir. <code class="language-plaintext highlighter-rouge">Int</code>, <code class="language-plaintext highlighter-rouge">String</code> veya başka herhangi bir tip olabilir. Bu nedenle <code class="language-plaintext highlighter-rouge">f</code>, polimorfik bir fonksiyondur.</p>

<p>Bir fonksiyon uygulamasında ise kural şöyledir: <code class="language-plaintext highlighter-rouge">f x</code> ifadesinin tipi $\beta$ ise <code class="language-plaintext highlighter-rouge">f</code> fonksiyonunun tipi mutlaka</p>

\[\operatorname{type}(f) = \operatorname{type}(x) \rightarrow \beta\]

<p>biçiminde olmalıdır. Tip çıkarımı, program boyunca bu tür denklemleri toplar ve çözer.</p>

<h2 id="birleştirme-tip-bulmacasının-motoru">Birleştirme: Tip bulmacasının motoru</h2>

<p>Denklemleri çözme işlemine <strong>unification</strong>, yani birleştirme denir. Amaç, iki tipi eşit hâle getiren yer değiştirmeleri bulmaktır.</p>

<table>
  <thead>
    <tr>
      <th>Denklem</th>
      <th>Sonuç</th>
      <th>Açıklama</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>$\alpha = Int$</td>
      <td>$\alpha \mapsto Int$</td>
      <td>Değişken somutlaşır</td>
    </tr>
    <tr>
      <td>$\alpha = \beta$</td>
      <td>$\alpha \mapsto \beta$</td>
      <td>İki bilinmeyen eşitlenir</td>
    </tr>
    <tr>
      <td>$\alpha \to Int = Bool \to \beta$</td>
      <td>$\alpha = Bool$, $\beta = Int$</td>
      <td>Parçalar ayrı ayrı eşitlenir</td>
    </tr>
    <tr>
      <td>$Int = String$</td>
      <td>Hata</td>
      <td>Tipler uzlaştırılamaz</td>
    </tr>
  </tbody>
</table>

<p>Birleştirme sırasında önemli bir güvenlik kontrolü de <strong>occurs check</strong> işlemidir. Örneğin $\alpha = \alpha \to \beta$ eşitliğine izin verilirse sonsuz bir tip oluşur. HM bunu reddeder; matematiksel canavarların programa sızmasına müsaade etmez.</p>

<h2 id="algorithm-w-nasıl-çalışır">Algorithm W nasıl çalışır?</h2>

<p>HM tip çıkarımının klasik uygulaması <strong>Algorithm W</strong> olarak bilinir. Algoritma, sözdizimi ağacını dolaşırken hem bir tip hem de bulunan tip eşitliklerini temsil eden bir substitution üretir.</p>

<p>Aşağıdaki sadeleştirilmiş Python kodu, fonksiyon uygulamasının mantığını gösterir:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">infer_application</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">function</span><span class="p">,</span> <span class="n">argument</span><span class="p">):</span>
    <span class="c1"># Fonksiyonun ve argümanın tiplerini ayrı ayrı çıkar.
</span>    <span class="n">s1</span><span class="p">,</span> <span class="n">function_type</span> <span class="o">=</span> <span class="nf">infer</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">function</span><span class="p">)</span>
    <span class="n">s2</span><span class="p">,</span> <span class="n">argument_type</span> <span class="o">=</span> <span class="nf">infer</span><span class="p">(</span><span class="nf">apply_env</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">env</span><span class="p">),</span> <span class="n">argument</span><span class="p">)</span>

    <span class="c1"># Uygulamanın henüz bilinmeyen sonuç tipi.
</span>    <span class="n">result_type</span> <span class="o">=</span> <span class="nf">fresh_type_variable</span><span class="p">()</span>

    <span class="c1"># Fonksiyon tipi, argüman -&gt; sonuç biçimiyle eşleşmelidir.
</span>    <span class="n">s3</span> <span class="o">=</span> <span class="nf">unify</span><span class="p">(</span>
        <span class="nf">apply_type</span><span class="p">(</span><span class="n">s2</span><span class="p">,</span> <span class="n">function_type</span><span class="p">),</span>
        <span class="nc">FunctionType</span><span class="p">(</span><span class="n">argument_type</span><span class="p">,</span> <span class="n">result_type</span><span class="p">)</span>
    <span class="p">)</span>

    <span class="n">substitution</span> <span class="o">=</span> <span class="nf">compose</span><span class="p">(</span><span class="n">s3</span><span class="p">,</span> <span class="nf">compose</span><span class="p">(</span><span class="n">s2</span><span class="p">,</span> <span class="n">s1</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">substitution</span><span class="p">,</span> <span class="nf">apply_type</span><span class="p">(</span><span class="n">s3</span><span class="p">,</span> <span class="n">result_type</span><span class="p">)</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">fresh_type_variable</code> yeni bir tip değişkeni oluşturur. <code class="language-plaintext highlighter-rouge">unify</code> gerekli eşleştirmeleri bulur, <code class="language-plaintext highlighter-rouge">compose</code> ise farklı aşamalarda bulunan sonuçları birleştirir. Böylece çıkarım yerel tahminlerden tutarlı bir genel sonuca ilerler.</p>

<h2 id="let-polimorfizmi-neden-önemlidir">Let-polimorfizmi neden önemlidir?</h2>

<p>HM sisteminin güçlü taraflarından biri <code class="language-plaintext highlighter-rouge">let</code> ile tanımlanan değerleri <strong>genelleştirmesidir</strong>:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">let</span> <span class="n">identity</span> <span class="o">=</span> <span class="nf">\</span><span class="n">x</span> <span class="o">-&gt;</span> <span class="n">x</span>
<span class="kr">in</span> <span class="p">(</span><span class="n">identity</span> <span class="mi">42</span><span class="p">,</span> <span class="n">identity</span> <span class="s">"merhaba"</span><span class="p">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">identity</code> yalnızca tek bir bilinmeyen tipe sabitlenmez. Tipi $\forall \alpha.\alpha \to \alpha$ olarak genelleştirilir ve her kullanımda yeni değişkenlerle örneklenir. Böylece aynı fonksiyon hem sayı hem metin üzerinde güvenle kullanılabilir.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th style="text-align: right">Tip anotasyonu</th>
      <th style="text-align: right">Hataları yakalama</th>
      <th style="text-align: right">Polimorfizm</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Dinamik tipleme</td>
      <td style="text-align: right">Gerekmez</td>
      <td style="text-align: right">Çalışma zamanında</td>
      <td style="text-align: right">Doğal fakat dinamik</td>
    </tr>
    <tr>
      <td>Açık statik tipleme</td>
      <td style="text-align: right">Genellikle gerekir</td>
      <td style="text-align: right">Derleme zamanında</td>
      <td style="text-align: right">Sisteme bağlı</td>
    </tr>
    <tr>
      <td>Hindley-Milner</td>
      <td style="text-align: right">Çoğunlukla gerekmez</td>
      <td style="text-align: right">Derleme zamanında</td>
      <td style="text-align: right">Otomatik ve genel</td>
    </tr>
  </tbody>
</table>

<p>HM kusursuz değildir; alt tipleme, gelişmiş nesne modelleri ve bazı yan etkiler sistemi karmaşıklaştırır. Yine de ML, OCaml ve Haskell gibi dillerin temelinde yer alarak önemli bir denge kurar: geliştirici az tip yazar, derleyici çok düşünür ve hatalı programlar üretime çıkmadan kapıda yakalanır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="hindley-milner" /><category term="tip-sistemleri" /><category term="fonksiyonel-programlama" /><category term="tip-çıkarımı" /><category term="algoritma-w" /><summary type="html"><![CDATA[Bir fonksiyon yazdığınızı, hiçbir parametreye tip eklemediğinizi ve derleyicinin yine de bütün tip hatalarını yakaladığını düşünün. Üstelik derleyici yalnızca kodun doğru olup olmadığını söylemekle kalmasın, mümkün olan en genel tipi de keşfetsin! Hindley-Milner, yani HM tip sistemi, fonksiyonel programlama dünyasının bu etkileyici numarasını matematiksel olarak gerçekleştirir.]]></summary></entry><entry><title type="html">IOI ve ICPC’nin Efsane Problemleri: Bir Soru Nasıl Klasiğe Dönüşür?</title><link href="https://program.sonsuz.us/posts/ioi-ve-icpcnin-efsane-problemleri-bir-soru-nasil-klasige-donusur/" rel="alternate" type="text/html" title="IOI ve ICPC’nin Efsane Problemleri: Bir Soru Nasıl Klasiğe Dönüşür?" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/ioi-ve-icpcnin-efsane-problemleri-bir-soru-nasil-klasige-donusur</id><content type="html" xml:base="https://program.sonsuz.us/posts/ioi-ve-icpcnin-efsane-problemleri-bir-soru-nasil-klasige-donusur/"><![CDATA[<p>Bazı yarışma problemleri çözülüp unutulur; bazılarıysa yıllar sonra bile eğitim kamplarında, çevrim içi jürilerde ve algoritma sohbetlerinde karşımıza çıkar. IOI ile ICPC tarihinde klasikleşen soruların sırrı yalnızca zor olmaları değildir. Bu problemler, gündelik görünen bir hikâyenin altına güçlü bir matematiksel model saklar ve çözücüye belirli bir algoritmayı ezberletmek yerine onu keşfettirir.
``</p>

<h2 id="i̇ki-yarışma-iki-farklı-problem-kültürü">İki yarışma, iki farklı problem kültürü</h2>

<p>International Olympiad in Informatics (IOI), lise öğrencilerine yönelik bireysel bir yarışmadır. Sorular çoğunlukla alt görevler ve kısmi puanlama içerir. Bu nedenle önce yavaş ama doğru bir çözüm kurup ardından onu aşamalı biçimde hızlandırmak mümkündür.</p>

<p>International Collegiate Programming Contest (ICPC) ise üç kişilik üniversite takımlarının süreye karşı yarıştığı bir formattır. Burada problem seçimi, hatasız uygulama ve takım koordinasyonu da algoritma bilgisi kadar önemlidir.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>IOI</th>
      <th>ICPC</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Katılım</td>
      <td>Bireysel</td>
      <td>Üç kişilik takım</td>
    </tr>
    <tr>
      <td>Puanlama</td>
      <td>Genellikle kısmi puanlı</td>
      <td>Çoğunlukla kabul veya ret</td>
    </tr>
    <tr>
      <td>Tipik vurgu</td>
      <td>Derin modelleme ve optimizasyon</td>
      <td>Hızlı teşhis ve geniş konu yelpazesi</td>
    </tr>
    <tr>
      <td>Klasikleşme biçimi</td>
      <td>Yeni bir tekniği katman katman öğretmesi</td>
      <td>Basit anlatımdan şaşırtıcı model çıkarması</td>
    </tr>
  </tbody>
</table>

<h2 id="ioi-2000-post-office">IOI 2000: Post Office</h2>

<p><strong>Post Office</strong>, doğrusal bir yol üzerindeki köylere belirli sayıda postane yerleştirme problemidir. Amaç, her köyün en yakın postaneye uzaklıkları toplamını küçültmektir. Tek bir postanenin hizmet verdiği sıralı köy grubu için en iyi konum medyandır.</p>

<p>Köy konumları $x_l,\ldots,x_r$ ise grubun maliyeti</p>

\[C(l,r)=\sum_{i=l}^{r}\vert x_i-x_m\vert\]

<p>şeklindedir; burada $m=\lfloor(l+r)/2\rfloor$ seçilebilir. Ardından bölümlemeli dinamik programlama gelir:</p>

\[dp[k][i]=\min_{0\le j&lt;i}\left(dp[k-1][j]+C(j+1,i)\right).\]

<p>Bu soru klasikleşmiştir çünkü üç önemli fikri kusursuz biçimde birleştirir: sıralama, medyanın mutlak uzaklık için optimal oluşu ve aralıkları dinamik programlamayla bölme.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;=</span> <span class="n">postaneSayisi</span><span class="p">;</span> <span class="o">++</span><span class="n">k</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">koySayisi</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">i</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">dp</span><span class="p">[</span><span class="n">k</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">k</span><span class="p">][</span><span class="n">i</span><span class="p">],</span> <span class="n">dp</span><span class="p">[</span><span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">maliyet</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">i</span><span class="p">]);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bu çekirdek kod, ilk <code class="language-plaintext highlighter-rouge">i</code> köyü <code class="language-plaintext highlighter-rouge">k</code> postaneyle kapsamak için son grubun nerede başladığını dener. Asıl ustalık, <code class="language-plaintext highlighter-rouge">maliyet[l][r]</code> değerlerini önceden hesaplayarak geçişi ucuzlatmaktır.</p>

<h2 id="ioi-2011-race">IOI 2011: Race</h2>

<p><strong>Race</strong>, ağırlıklı bir ağaçta toplam uzunluğu tam $K$ olan ve mümkün olan en az kenarı kullanan yolu arar. İlk bakışta bütün düğüm çiftlerini denemek doğal görünür; fakat bu yaklaşık $O(N^2)$ yol anlamına gelir.</p>

<p>Klasik çözüm centroid decomposition kullanır. Ağaç dengeli parçalara ayrılır; centroid’den alt ağaçlara olan uzaklıklar toplanır ve daha önce görülen $K-d$ uzaklığı aranır. Böylece çalışma süresi kabaca $O(N\log N)$ seviyesine iner. Problem, centroid decomposition tekniğini yalnızca sergilemediği, neden gerekli olduğunu hissettirdiği için efsanedir.</p>

<h2 id="icpc-klasiği-tower-of-cubes">ICPC klasiği: Tower of Cubes</h2>

<p>1997 Dünya Finalleri ile özdeşleşen <strong>Tower of Cubes</strong>, renkli yüzlere sahip küpleri üst üste dizerken temas eden renklerin eşleşmesini ister. Hikâye fiziksel görünse de yapı, yönlendirilmiş en uzun yol problemine dönüşür. Her küpün altı olası yönelimi birer durumdur; bir yönelimden diğerine geçiş, üst ve alt renklerin uyuşmasına bağlıdır.</p>

<table>
  <thead>
    <tr>
      <th>Görünen hikâye</th>
      <th>Algoritmik gerçek</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Küpleri döndürmek</td>
      <td>Durum üretmek</td>
    </tr>
    <tr>
      <td>Renkleri eşleştirmek</td>
      <td>Geçiş koşulu</td>
    </tr>
    <tr>
      <td>En yüksek kule</td>
      <td>En uzun yol / dinamik programlama</td>
    </tr>
    <tr>
      <td>Küp sırasını korumak</td>
      <td>Döngüsüz durum düzeni</td>
    </tr>
  </tbody>
</table>

<h2 id="peki-bir-problem-neden-klasikleşir">Peki bir problem neden klasikleşir?</h2>

<p>Klasik bir problem genellikle kısa anlatılır, birden fazla makul fakat yetersiz çözüm sunar ve çözüm bulunduğunda “Bunu nasıl göremedim?” duygusu yaratır. Dahası, kullanılan fikir başka problemlere taşınabilir: <strong>Post Office</strong> medyanı ve bölümlemeyi, <strong>Race</strong> dengeli ağaç ayrıştırmayı, <strong>Tower of Cubes</strong> ise hikâyeyi durum grafiğine çevirmeyi öğretir.</p>

<p>Efsane sorular geçmişin zor bilmeceleri değildir; algoritmik düşüncenin laboratuvarlarıdır. Onları çözerken yalnızca kabul işareti kazanmaz, bir sonraki bilinmeyen probleme bakmak için yeni bir gözlük edinirsiniz.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="ioi" /><category term="icpc" /><category term="algoritma" /><category term="rekabetçi programlama" /><category term="dinamik programlama" /><category term="graf teorisi" /><summary type="html"><![CDATA[Bazı yarışma problemleri çözülüp unutulur; bazılarıysa yıllar sonra bile eğitim kamplarında, çevrim içi jürilerde ve algoritma sohbetlerinde karşımıza çıkar. IOI ile ICPC tarihinde klasikleşen soruların sırrı yalnızca zor olmaları değildir. Bu problemler, gündelik görünen bir hikâyenin altına güçlü bir matematiksel model saklar ve çözücüye belirli bir algoritmayı ezberletmek yerine onu keşfettirir.]]></summary></entry><entry><title type="html">JIT Derleme Mantığı: Kod Çalışırken Nasıl Hızlanır?</title><link href="https://program.sonsuz.us/posts/jit-derleme-mantigi-kod-calisirken-nasil-hizlanir/" rel="alternate" type="text/html" title="JIT Derleme Mantığı: Kod Çalışırken Nasıl Hızlanır?" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/jit-derleme-mantigi-kod-calisirken-nasil-hizlanir</id><content type="html" xml:base="https://program.sonsuz.us/posts/jit-derleme-mantigi-kod-calisirken-nasil-hizlanir/"><![CDATA[<p>Bir programın çalışmaya başladıktan birkaç saniye sonra hızlanması ilk bakışta sihir gibi görünebilir. Oysa perde arkasında, kodu izleyen ve sık kullanılan bölümleri daha verimli makine koduna dönüştüren bir mekanizma vardır: <strong>JIT (Just-In-Time) derleme</strong>. Java, JavaScript ve .NET gibi platformlarda kullanılan bu yaklaşım, yorumlayıcının esnekliğiyle önceden derlemenin hızını birleştirir.
``</p>
<h2 id="derleme-ile-yorumlama-arasındaki-köprü">Derleme ile yorumlama arasındaki köprü</h2>

<p>Bir işlemci Java, C# veya JavaScript gibi yüksek seviyeli dilleri doğrudan anlayamaz; yalnızca kendi komut setine uygun makine kodunu çalıştırır. Bu dönüşüm için üç temel yaklaşım bulunur:</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Dönüşüm zamanı</th>
      <th>Avantajı</th>
      <th>Dezavantajı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AOT derleme</td>
      <td>Program çalışmadan önce</td>
      <td>Hızlı başlangıç ve öngörülebilir performans</td>
      <td>Çalışma zamanı davranışını bilemez</td>
    </tr>
    <tr>
      <td>Yorumlama</td>
      <td>Her komut çalıştırılırken</td>
      <td>Esnek ve taşınabilir</td>
      <td>Tekrarlanan çözümleme maliyetlidir</td>
    </tr>
    <tr>
      <td>JIT derleme</td>
      <td>Program çalışırken</td>
      <td>Gerçek kullanım verisine göre optimizasyon</td>
      <td>Isınma süresi ve ek bellek gerektirir</td>
    </tr>
  </tbody>
</table>

<p>JIT kullanan sistemler genellikle kaynak kodu önce <strong>bytecode</strong> veya benzeri bir ara temsile dönüştürür. Sanal makine başlangıçta bu ara kodu yorumlayabilir. Aynı fonksiyon tekrar tekrar çağrıldığında sistem, “Bu bölge sıcak!” diyerek onu yerel makine koduna derler.</p>

<h2 id="sıcak-kod-nasıl-belirlenir">Sıcak kod nasıl belirlenir?</h2>

<p>Çalışma zamanı; metot çağrılarını, döngü tekrarlarını ve kullanılan veri türlerini sayaçlarla takip eder. Bir kod parçasının çağrı sayısını $n$, yorumlama maliyetini $I$, derleme maliyetini $C$ ve derlenmiş çalışma maliyetini $M$ olarak düşünelim. JIT derlemenin kazançlı olması için yaklaşık olarak şu koşul aranır:</p>

\[nI &gt; C + nM\]

<p>Başka bir ifadeyle, gelecekte sağlanacak hız kazancı derleme masrafını aşmalıdır. Yalnızca bir kez çalışan başlangıç kodunu derlemek çoğu zaman gereksizdir. Fakat milyonlarca kez dönen küçük bir hesaplama fonksiyonu oldukça değerli bir hedeftir.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kt">long</span> <span class="nf">karelerToplami</span><span class="o">(</span><span class="kt">int</span> <span class="n">limit</span><span class="o">)</span> <span class="o">{</span>
    <span class="kt">long</span> <span class="n">toplam</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">limit</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
        <span class="n">toplam</span> <span class="o">+=</span> <span class="o">(</span><span class="kt">long</span><span class="o">)</span> <span class="n">i</span> <span class="o">*</span> <span class="n">i</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="n">toplam</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Bu metot sık çağrılırsa JIT, yalnızca makine kodu üretmekle kalmaz; döngüyü açabilir, gereksiz kontrolleri kaldırabilir veya işlemcinin vektör komutlarından yararlanabilir. Böylece aynı sonuç daha az komutla hesaplanır.</p>

<h2 id="jitin-gizli-silahı-spekülatif-optimizasyon">JIT’in gizli silahı: spekülatif optimizasyon</h2>

<p>AOT derleyici, programın gelecekte hangi türlerle karşılaşacağını kesin olarak bilemez. JIT ise gerçek çalışma verisini görür. Örneğin JavaScript’te aşağıdaki fonksiyon başlangıçta her tür değeri kabul edebilir:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">topla</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">a</span> <span class="o">+</span> <span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">for </span><span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="nx">_000_000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="nf">topla</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Motor, <code class="language-plaintext highlighter-rouge">a</code> ve <code class="language-plaintext highlighter-rouge">b</code> değerlerinin sürekli tam sayı olduğunu gözlemlerse genel amaçlı toplama yerine hızlı bir tamsayı komutu üretebilir. Buna <strong>spekülatif optimizasyon</strong> denir: sistem, gözlenen düzenin devam edeceğini varsayar.</p>

<p>Peki daha sonra <code class="language-plaintext highlighter-rouge">topla("merhaba", " dünya")</code> çağrılırsa ne olur? Varsayım bozulur ve motor <strong>deoptimizasyon</strong> yaparak daha genel koda geri döner. JIT hızlıdır ama falcı değildir; tahminlerini gerektiğinde iptal eder.</p>

<h2 id="isınma-neden-önemlidir">Isınma neden önemlidir?</h2>

<p>İlk çağrılar sırasında profil oluşturma ve derleme maliyeti bulunduğundan performans testlerinde “warm-up” aşaması kullanılır. Aksi hâlde ölçüm, uygulamanın kararlı hızından çok başlangıç masraflarını gösterir.</p>

<p>JIT’in çalışma döngüsü kısaca şöyledir:</p>

<ol>
  <li>Ara kodu yorumlayarak hızlıca başla.</li>
  <li>Çalışma davranışını profille.</li>
  <li>Sıcak bölgeleri makine koduna derle.</li>
  <li>Gözlenen türlere göre agresif optimizasyon uygula.</li>
  <li>Varsayımlar bozulursa güvenli sürüme geri dön.</li>
</ol>

<p>Sonuç olarak kod aslında kendi kendine öğrenmez; çalışma zamanı sistemi onu gözlemler ve kaynaklarını en çok kullanılan bölgelere yatırır. Program “ısındıkça” hızlanmasının nedeni de tam olarak budur.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="jit" /><category term="derleyici" /><category term="performans" /><category term="java" /><category term="javascript" /><category term="sanal-makine" /><summary type="html"><![CDATA[Bir programın çalışmaya başladıktan birkaç saniye sonra hızlanması ilk bakışta sihir gibi görünebilir. Oysa perde arkasında, kodu izleyen ve sık kullanılan bölümleri daha verimli makine koduna dönüştüren bir mekanizma vardır: JIT (Just-In-Time) derleme. Java, JavaScript ve .NET gibi platformlarda kullanılan bu yaklaşım, yorumlayıcının esnekliğiyle önceden derlemenin hızını birleştirir.]]></summary></entry><entry><title type="html">Konveks Zarf Hilesi ile Dinamik Programlamayı Jet Hızına Çıkarma</title><link href="https://program.sonsuz.us/posts/konveks-zarf-hilesi-ile-dinamik-programlamayi-jet-hizina-cikarma/" rel="alternate" type="text/html" title="Konveks Zarf Hilesi ile Dinamik Programlamayı Jet Hızına Çıkarma" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/konveks-zarf-hilesi-ile-dinamik-programlamayi-jet-hizina-cikarma</id><content type="html" xml:base="https://program.sonsuz.us/posts/konveks-zarf-hilesi-ile-dinamik-programlamayi-jet-hizina-cikarma/"><![CDATA[<p>Dinamik programlama bazen doğru bağıntıyı bulduğumuz hâlde bizi $O(n^2)$ karmaşıklığıyla baş başa bırakır. Konveks Zarf Hilesi, İngilizce adıyla <strong>Convex Hull Trick (CHT)</strong>, belirli biçimdeki geçişleri doğru parçaları olarak yorumlayarak bu maliyeti $O(n\log n)$, hatta uygun koşullarda $O(n)$ seviyesine indirebilir. Yani iç içe döngüleri geometrinin küçük ama etkili bir numarasıyla değiştiririz.
``</p>

<h2 id="cht-hangi-bağıntılarda-kullanılabilir">CHT hangi bağıntılarda kullanılabilir?</h2>

<p>Şu klasik DP geçişini ele alalım:</p>

\[dp[i] = \min_{j&lt;i}\{dp[j] + a[j]\cdot x[i] + b[j]\}\]

<p>Sabit bir $i$ için $x[i]$ bellidir. Her $j$ seçeneği ise</p>

\[y = m_jx + c_j\]

<p>biçiminde bir doğru oluşturur. Burada $m_j=a[j]$ eğim, $c_j=dp[j]+b[j]$ sabit terimdir. Dolayısıyla $dp[i]$, bütün doğruların $x[i]$ noktasındaki minimum değeridir.</p>

<p>Naif çözüm her $i$ için tüm eski doğruları dener. CHT ise yalnızca gelecekte gerçekten en iyi olabilecek doğruları saklar. Bir doğru hiçbir $x$ değerinde minimum olamıyorsa zarftan çıkarılır; geometrik olarak alt konveks zarf korunur.</p>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th style="text-align: right">Naif DP</th>
      <th style="text-align: right">CHT ile DP</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Her durumdaki aday sayısı</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">Amortize $O(1)$ veya $O(\log n)$</td>
    </tr>
    <tr>
      <td>Toplam karmaşıklık</td>
      <td style="text-align: right">$O(n^2)$</td>
      <td style="text-align: right">$O(n)$ ya da $O(n\log n)$</td>
    </tr>
    <tr>
      <td>Gerekli yapı</td>
      <td style="text-align: right">İç içe döngü</td>
      <td style="text-align: right">Doğrular ve sorgular</td>
    </tr>
    <tr>
      <td>Temel koşul</td>
      <td style="text-align: right">Yok</td>
      <td style="text-align: right">Geçişin doğrusal biçime dönüşmesi</td>
    </tr>
  </tbody>
</table>

<h2 id="bir-doğru-ne-zaman-gereksizdir">Bir doğru ne zaman gereksizdir?</h2>

<p>Eğimleri sıralı üç doğru düşünelim: $L_1$, $L_2$ ve $L_3$. Eğer $L_1$ ile $L_2$ kesişimi, $L_2$ ile $L_3$ kesişiminin sağında kalıyorsa $L_2$ hiçbir zaman en iyi seçenek olamaz. Bölme işlemi ve kayan nokta hatalarından kaçınmak için kesişimleri çapraz çarparak karşılaştırabiliriz.</p>

<p>Doğruların eğimleri monoton ekleniyor ve sorgu değerleri $x$ de monoton geliyorsa bir <code class="language-plaintext highlighter-rouge">deque</code> yeterlidir. Ön taraftaki doğru, sıradaki doğrudan kötü olduğunda silinir. Böylece her doğru en fazla bir kez eklenir ve bir kez çıkarılır.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
<span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span>

<span class="k">struct</span> <span class="nc">Line</span> <span class="p">{</span>
    <span class="n">ll</span> <span class="n">m</span><span class="p">,</span> <span class="n">b</span><span class="p">;</span>
    <span class="n">ll</span> <span class="n">value</span><span class="p">(</span><span class="n">ll</span> <span class="n">x</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">m</span> <span class="o">*</span> <span class="n">x</span> <span class="o">+</span> <span class="n">b</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span>

<span class="c1">// Orta doğru, alt zarf üzerinde gereksiz mi?</span>
<span class="kt">bool</span> <span class="nf">redundant</span><span class="p">(</span><span class="k">const</span> <span class="n">Line</span><span class="o">&amp;</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="n">Line</span><span class="o">&amp;</span> <span class="n">b</span><span class="p">,</span> <span class="k">const</span> <span class="n">Line</span><span class="o">&amp;</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">__int128</span><span class="p">)(</span><span class="n">b</span><span class="p">.</span><span class="n">b</span> <span class="o">-</span> <span class="n">a</span><span class="p">.</span><span class="n">b</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">m</span> <span class="o">-</span> <span class="n">c</span><span class="p">.</span><span class="n">m</span><span class="p">)</span>
         <span class="o">&gt;=</span> <span class="p">(</span><span class="n">__int128</span><span class="p">)(</span><span class="n">c</span><span class="p">.</span><span class="n">b</span> <span class="o">-</span> <span class="n">b</span><span class="p">.</span><span class="n">b</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">m</span> <span class="o">-</span> <span class="n">b</span><span class="p">.</span><span class="n">m</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="nc">MonotoneCHT</span> <span class="p">{</span>
    <span class="n">deque</span><span class="o">&lt;</span><span class="n">Line</span><span class="o">&gt;</span> <span class="n">hull</span><span class="p">;</span>

    <span class="c1">// Eğimlerin azalan sırada geldiği varsayılıyor.</span>
    <span class="kt">void</span> <span class="n">add</span><span class="p">(</span><span class="n">ll</span> <span class="n">m</span><span class="p">,</span> <span class="n">ll</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">Line</span> <span class="n">line</span><span class="p">{</span><span class="n">m</span><span class="p">,</span> <span class="n">b</span><span class="p">};</span>
        <span class="k">while</span> <span class="p">(</span><span class="n">hull</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">2</span> <span class="o">&amp;&amp;</span>
               <span class="n">redundant</span><span class="p">(</span><span class="n">hull</span><span class="p">[</span><span class="n">hull</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">-</span> <span class="mi">2</span><span class="p">],</span> <span class="n">hull</span><span class="p">.</span><span class="n">back</span><span class="p">(),</span> <span class="n">line</span><span class="p">))</span>
            <span class="n">hull</span><span class="p">.</span><span class="n">pop_back</span><span class="p">();</span>
        <span class="n">hull</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">line</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// x değerlerinin artan sırada sorgulandığı varsayılıyor.</span>
    <span class="n">ll</span> <span class="nf">query</span><span class="p">(</span><span class="n">ll</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">while</span> <span class="p">(</span><span class="n">hull</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">2</span> <span class="o">&amp;&amp;</span>
               <span class="n">hull</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">value</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">hull</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">value</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
            <span class="n">hull</span><span class="p">.</span><span class="n">pop_front</span><span class="p">();</span>
        <span class="k">return</span> <span class="n">hull</span><span class="p">.</span><span class="n">front</span><span class="p">().</span><span class="n">value</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">__int128</code>, çarpımların <code class="language-plaintext highlighter-rouge">long long</code> sınırını aşma riskini azaltır. Ancak doğru değerlerinin kendisi de taşabilecekse <code class="language-plaintext highlighter-rouge">value</code> fonksiyonunda aynı tür kullanılmalıdır.</p>

<h2 id="monotonluk-yoksa-ne-olacak">Monotonluk yoksa ne olacak?</h2>

<p>Eğimler veya sorgular sıralı değilse deque sürümü güvenli değildir. Bu durumda iki yaygın seçenek vardır:</p>

<table>
  <thead>
    <tr>
      <th>Yöntem</th>
      <th>Kullanım durumu</th>
      <th style="text-align: right">Karmaşıklık</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kesişim noktalarıyla CHT</td>
      <td>Eğimler sıralı, sorgular karışık</td>
      <td style="text-align: right">$O(\log n)$ sorgu</td>
    </tr>
    <tr>
      <td>Li Chao ağacı</td>
      <td>Eğimler ve sorgular tamamen karışık</td>
      <td style="text-align: right">$O(\log X)$</td>
    </tr>
  </tbody>
</table>

<p>Li Chao ağacı, belirli bir $x$ aralığında doğruları segment ağacına benzer biçimde tutar. Biraz daha fazla bellek kullanır ama monotonluk şartlarını ortadan kaldırır.</p>

<p>CHT uygularken önce bağıntıdaki $j$ kaynaklı ifadeleri eğim ve sabit terime, $i$ kaynaklı ifadeyi ise sorgu koordinatına ayır. Ardından minimum mu maksimum mu aradığını, eşit eğimlerin nasıl yönetileceğini ve sayı taşmalarını kontrol et. Doğru cebir yapıldığında konveks zarf hilesi, ürkütücü görünen karesel DP’leri oldukça zarif algoritmalara dönüştürür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="dinamik programlama" /><category term="konveks zarf hilesi" /><category term="cht" /><category term="algoritma" /><category term="c++" /><category term="optimizasyon" /><summary type="html"><![CDATA[Dinamik programlama bazen doğru bağıntıyı bulduğumuz hâlde bizi $O(n^2)$ karmaşıklığıyla baş başa bırakır. Konveks Zarf Hilesi, İngilizce adıyla Convex Hull Trick (CHT), belirli biçimdeki geçişleri doğru parçaları olarak yorumlayarak bu maliyeti $O(n\log n)$, hatta uygun koşullarda $O(n)$ seviyesine indirebilir. Yani iç içe döngüleri geometrinin küçük ama etkili bir numarasıyla değiştiririz.]]></summary></entry><entry><title type="html">Küçükten Büyüğe Birleştirme: Veriyi Akıllıca Taşımanın Gücü</title><link href="https://program.sonsuz.us/posts/kucukten-buyuge-birlestirme-veriyi-akillica-tasimanin-gucu/" rel="alternate" type="text/html" title="Küçükten Büyüğe Birleştirme: Veriyi Akıllıca Taşımanın Gücü" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/kucukten-buyuge-birlestirme-veriyi-akillica-tasimanin-gucu</id><content type="html" xml:base="https://program.sonsuz.us/posts/kucukten-buyuge-birlestirme-veriyi-akillica-tasimanin-gucu/"><![CDATA[<p>Birçok algoritmada kümeleri, listeleri veya sözlükleri tekrar tekrar birleştirmemiz gerekir. Bunu dikkatsizce yaptığımızda aynı elemanlar defalarca taşınır ve masum görünen kodumuz kağnı hızına düşer. <strong>Küçükten büyüğe birleştirme</strong> ya da İngilizce adıyla <em>small-to-large merging</em>, her adımda küçük koleksiyonu büyük koleksiyona ekleyerek toplam maliyeti kontrol altında tutan zarif bir tekniktir.</p>

<p>``</p>

<h2 id="temel-fikir">Temel fikir</h2>

<p>Elimizde $A$ ve $B$ adında iki küme bulunsun. Bunları birleştirirken hangi kümenin hedef olacağı sonucu değiştirmez; fakat çalışma süresini ciddi biçimde etkiler.</p>

<ul>
  <li>$\vert A\vert  &lt; \vert B\vert $ ise $A$ içindeki elemanları $B$’ye taşı.</li>
  <li>$\vert B\vert  &lt; \vert A\vert $ ise $B$ içindeki elemanları $A$’ya taşı.</li>
  <li>Büyük koleksiyon yerinde kalırken yalnızca küçük koleksiyon dolaşılır.</li>
</ul>

<p>Tekniğin sırrı, taşınan bir elemanın bulunduğu koleksiyonun büyüklüğünün her taşımadan sonra en az iki katına çıkmasıdır. Bir eleman sırasıyla büyüklüğü $1, 2, 4, 8, \ldots$ olan koleksiyonlara geçebilir. Toplam eleman sayısı $n$ olduğuna göre:</p>

\[2^k \leq n \Rightarrow k \leq \log_2 n\]

<p>Dolayısıyla her eleman en fazla $O(\log n)$ kez taşınır. Ekleme işlemi sabit maliyetliyse toplam maliyet $O(n\log n)$ olur.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Birleştirme tercihi</th>
      <th style="text-align: right">Bir elemanın taşınma sayısı</th>
      <th style="text-align: right">Tipik toplam maliyet</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Saf birleştirme</td>
      <td>Rastgele veya büyükten küçüğe</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(n^2)$</td>
    </tr>
    <tr>
      <td>Small-to-large</td>
      <td>Küçük koleksiyon büyüğe</td>
      <td style="text-align: right">$O(\log n)$</td>
      <td style="text-align: right">$O(n\log n)$</td>
    </tr>
    <tr>
      <td>Dengeli ağaç kümesi</td>
      <td>Küçükten büyüğe, ekleme $O(\log n)$</td>
      <td style="text-align: right">$O(\log n)$</td>
      <td style="text-align: right">$O(n\log^2 n)$</td>
    </tr>
  </tbody>
</table>

<h2 id="ağaçlarda-kullanım">Ağaçlarda kullanım</h2>

<p>Yaygın bir problem, köklü bir ağaçta her düğümün alt ağacında kaç farklı renk bulunduğunu hesaplamaktır. Her düğüm için sıfırdan tarama yapmak pahalıdır. Bunun yerine çocuklardan gelen renk kümeleri küçükten büyüğe birleştirilebilir.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;set&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">graph</span><span class="p">;</span>
<span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">color</span><span class="p">,</span> <span class="n">answer</span><span class="p">;</span>

<span class="n">set</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;*</span> <span class="nf">dfs</span><span class="p">(</span><span class="kt">int</span> <span class="n">node</span><span class="p">,</span> <span class="kt">int</span> <span class="n">parent</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Başlangıçta küme yalnızca düğümün kendi rengini içerir.</span>
    <span class="n">set</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;*</span> <span class="n">bag</span> <span class="o">=</span> <span class="k">new</span> <span class="n">set</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">();</span>
    <span class="n">bag</span><span class="o">-&gt;</span><span class="n">insert</span><span class="p">(</span><span class="n">color</span><span class="p">[</span><span class="n">node</span><span class="p">]);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">child</span> <span class="o">:</span> <span class="n">graph</span><span class="p">[</span><span class="n">node</span><span class="p">])</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">child</span> <span class="o">==</span> <span class="n">parent</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

        <span class="n">set</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;*</span> <span class="n">childBag</span> <span class="o">=</span> <span class="n">dfs</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="n">node</span><span class="p">);</span>

        <span class="c1">// Her zaman küçük kümeyi büyük kümeye ekleyeceğiz.</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">bag</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">childBag</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">swap</span><span class="p">(</span><span class="n">bag</span><span class="p">,</span> <span class="n">childBag</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">value</span> <span class="o">:</span> <span class="o">*</span><span class="n">childBag</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">bag</span><span class="o">-&gt;</span><span class="n">insert</span><span class="p">(</span><span class="n">value</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">delete</span> <span class="n">childBag</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">answer</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">bag</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">());</span>
    <span class="k">return</span> <span class="n">bag</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Burada <code class="language-plaintext highlighter-rouge">dfs</code>, her alt ağaç için bir renk kümesi döndürür. <code class="language-plaintext highlighter-rouge">swap</code> işlemi koleksiyonları kopyalamaz; yalnızca işaretçileri değiştirir. Böylece büyük küme hedef olarak korunur. <code class="language-plaintext highlighter-rouge">std::set</code> eklemesi $O(\log n)$ sürdüğü için genel üst sınır $O(n\log^2 n)$ olur. Uygun bir <code class="language-plaintext highlighter-rouge">unordered_set</code> kullanılırsa beklenen süre $O(n\log n)$ seviyesine yaklaşabilir; ancak hash çakışmaları ve yeniden boyutlandırma maliyetleri unutulmamalıdır.</p>

<h2 id="nerelerde-işe-yarar">Nerelerde işe yarar?</h2>

<p>Teknik yalnızca renk kümeleriyle sınırlı değildir. Alt ağaç frekanslarını birleştirme, bağlı bileşenlere veri ekleme, sorguları çevrim dışı işleme ve harita tabanlı sayaçları toplama gibi durumlarda kullanılabilir. DSU birleştirmesinde kullanılan <strong>union by size</strong> yaklaşımı da aynı düşünce ailesindendir: küçük bileşen büyük bileşene bağlanır.</p>

<p>Dikkat edilmesi gereken nokta, birleştirme yönünün gerçekten boyuta göre seçilmesidir. Ayrıca koleksiyon kopyalamak yerine referans, işaretçi veya taşıma semantiği kullanılmalıdır. Aksi hâlde kazandığımız karmaşıklığı gizli kopyalama maliyetleriyle geri verebiliriz.</p>

<p>Özetle small-to-large, “daha az veriyi hareket ettir” ilkesinin algoritmik hâlidir. Uygulaması birkaç satırlık bir karşılaştırma ve takastan ibaret olsa da karesel çalışan çözümleri çoğu zaman logaritmik amortismanla çok daha ölçeklenebilir hâle getirir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="algoritma" /><category term="small-to-large" /><category term="veri-yapıları" /><category term="karmaşıklık" /><category term="cpp" /><category term="ağaçlar" /><summary type="html"><![CDATA[Birçok algoritmada kümeleri, listeleri veya sözlükleri tekrar tekrar birleştirmemiz gerekir. Bunu dikkatsizce yaptığımızda aynı elemanlar defalarca taşınır ve masum görünen kodumuz kağnı hızına düşer. Küçükten büyüğe birleştirme ya da İngilizce adıyla small-to-large merging, her adımda küçük koleksiyonu büyük koleksiyona ekleyerek toplam maliyeti kontrol altında tutan zarif bir tekniktir.]]></summary></entry><entry><title type="html">Lambda Calculus’tan Gerçek Fonksiyonlara: Programlamanın Matematiksel Kökleri</title><link href="https://program.sonsuz.us/posts/lambda-calculustan-gercek-fonksiyonlara-programlamanin-matematiksel-kokleri/" rel="alternate" type="text/html" title="Lambda Calculus’tan Gerçek Fonksiyonlara: Programlamanın Matematiksel Kökleri" /><published>2026-09-15T00:00:00+00:00</published><updated>2026-09-15T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/lambda-calculustan-gercek-fonksiyonlara-programlamanin-matematiksel-kokleri</id><content type="html" xml:base="https://program.sonsuz.us/posts/lambda-calculustan-gercek-fonksiyonlara-programlamanin-matematiksel-kokleri/"><![CDATA[<p>Bir programlama dilinde fonksiyon yazarken aslında 1930’larda ortaya atılmış matematiksel bir modelin izlerini takip ederiz. Parametreler, dönüş değerleri, anonim fonksiyonlar ve closure gibi modern araçların kökünde <strong>lambda calculus</strong> bulunur. Üstelik bu model, bilgisayarların henüz oda büyüklüğünde bile olmadığı bir dönemde geliştirilmiştir!</p>

<p>``</p>

<h2 id="lambda-calculus-nedir">Lambda calculus nedir?</h2>

<p>Lambda calculus, Alonzo Church tarafından hesaplanabilirliği incelemek amacıyla geliştirilen küçük fakat güçlü bir biçimsel sistemdir. Yalnızca üç temel yapıdan oluşur:</p>

<ol>
  <li><strong>Değişken:</strong> $x$</li>
  <li><strong>Fonksiyon tanımı:</strong> $\lambda x.M$</li>
  <li><strong>Fonksiyon uygulaması:</strong> $M\ N$</li>
</ol>

<p>Örneğin kendisine verilen değeri değiştirmeden döndüren özdeşlik fonksiyonu şöyledir:</p>

\[\lambda x.x\]

<p>Bu fonksiyona $5$ uygularsak ifade $(\lambda x.x)\ 5$ olur ve sonuç $5$ çıkar. Burada gerçekleşen işleme <strong>beta indirgeme</strong> denir:</p>

\[(\lambda x.M)\ N \rightarrow M[x := N]\]

<p>Sözel olarak: Fonksiyon gövdesindeki $x$ değişkenlerini, verilen $N$ değeriyle değiştir. Yani beta indirgeme, matematik dünyasının fonksiyon çağrısıdır.</p>

<h2 id="matematikten-koda-geçiş">Matematikten koda geçiş</h2>

<p>Lambda ifadelerinin modern dillerdeki karşılıkları şaşırtıcı derecede açıktır:</p>

<table>
  <thead>
    <tr>
      <th>Lambda calculus</th>
      <th>Programlama karşılığı</th>
      <th>Örnek</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>$\lambda x.x$</td>
      <td>Anonim fonksiyon</td>
      <td><code class="language-plaintext highlighter-rouge">x =&gt; x</code></td>
    </tr>
    <tr>
      <td>Fonksiyon uygulaması</td>
      <td>Fonksiyon çağrısı</td>
      <td><code class="language-plaintext highlighter-rouge">f(5)</code></td>
    </tr>
    <tr>
      <td>Beta indirgeme</td>
      <td>Parametre yerleştirme</td>
      <td><code class="language-plaintext highlighter-rouge">x</code> yerine <code class="language-plaintext highlighter-rouge">5</code></td>
    </tr>
    <tr>
      <td>Serbest değişken</td>
      <td>Closure tarafından yakalanan değer</td>
      <td>Dış kapsamdaki değişken</td>
    </tr>
    <tr>
      <td>İç içe fonksiyon</td>
      <td>Currying</td>
      <td><code class="language-plaintext highlighter-rouge">f(a)(b)</code></td>
    </tr>
  </tbody>
</table>

<p>JavaScript ile iki sayıyı toplayan curried bir fonksiyon yazalım:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">topla</span> <span class="o">=</span> <span class="nx">x</span> <span class="o">=&gt;</span> <span class="nx">y</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">y</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">onaEkle</span> <span class="o">=</span> <span class="nf">topla</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nf">onaEkle</span><span class="p">(</span><span class="mi">7</span><span class="p">));</span> <span class="c1">// 17</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">topla(10)</code> çağrısı hemen bir sayı üretmez; <code class="language-plaintext highlighter-rouge">x</code> değerini hatırlayan yeni bir fonksiyon döndürür. <code class="language-plaintext highlighter-rouge">onaEkle</code>, dış kapsamındaki <code class="language-plaintext highlighter-rouge">10</code> değerini koruduğu için bir <strong>closure</strong> örneğidir. Lambda calculus açısından ifade şöyledir:</p>

\[\lambda x.\lambda y.x+y\]

<p>Birden fazla parametre alıyor gibi görünen fonksiyonların tek parametreli fonksiyon zincirine dönüştürülmesine <strong>currying</strong> denir.</p>

<h2 id="sayılar-bile-fonksiyon-olabilir">Sayılar bile fonksiyon olabilir</h2>

<p>Saf lambda calculus içinde sayı sabitleri yoktur. Buna rağmen sayılar, fonksiyonların kaç kez uygulanacağını belirten yapılarla temsil edilebilir. Bunlara <strong>Church sayıları</strong> adı verilir:</p>

\[0 = \lambda f.\lambda x.x\]

\[1 = \lambda f.\lambda x.f(x)\]

\[2 = \lambda f.\lambda x.f(f(x))\]

<p>İki sayısı, <code class="language-plaintext highlighter-rouge">f</code> fonksiyonunu başlangıç değerine iki defa uygular. Python’da bu fikri çalıştırabiliriz:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">church_iki</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">f</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="nf">f</span><span class="p">(</span><span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
<span class="n">artir</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span>

<span class="n">sonuc</span> <span class="o">=</span> <span class="nf">church_iki</span><span class="p">(</span><span class="n">artir</span><span class="p">)(</span><span class="mi">0</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">sonuc</span><span class="p">)</span>  <span class="c1"># 2
</span></code></pre></div></div>

<p>Bu kod günlük kullanım için pratik değildir; ancak veri ile davranış arasındaki sınırın düşündüğümüz kadar keskin olmadığını gösterir. Yeterince güçlü fonksiyonlarla sayıları, mantıksal değerleri ve veri yapılarını modellemek mümkündür.</p>

<h2 id="saf-fonksiyonların-önemi">Saf fonksiyonların önemi</h2>

<p>Lambda calculus, durum değiştirmek yerine ifadeleri dönüştürür. Bu yaklaşım modern fonksiyonel programlamadaki <strong>saf fonksiyon</strong> kavramını besler.</p>

<table>
  <thead>
    <tr>
      <th>Saf fonksiyon</th>
      <th>Yan etkili fonksiyon</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Aynı girdiye aynı sonucu verir</td>
      <td>Sonuç dış duruma bağlı olabilir</td>
    </tr>
    <tr>
      <td>Dış veriyi değiştirmez</td>
      <td>Dosya, ekran veya global durum değişebilir</td>
    </tr>
    <tr>
      <td>Test edilmesi kolaydır</td>
      <td>Ek kurulum gerekebilir</td>
    </tr>
    <tr>
      <td>Paralelleştirmeye uygundur</td>
      <td>Yarış koşulları oluşabilir</td>
    </tr>
  </tbody>
</table>

<p>Örneğin <code class="language-plaintext highlighter-rouge">kare(x) = x²</code> saf bir fonksiyondur. Buna karşılık global bir sayacı artıran fonksiyon, yalnızca çıktı üretmez; sistemin durumunu da değiştirir.</p>

<p>Lambda calculus bize programlamanın özünde komut listelerinden ibaret olmadığını anlatır. Bir program, ifadelerin başka ifadelere dönüştürülmesi olarak da görülebilir. Bugün <code class="language-plaintext highlighter-rouge">map</code>, <code class="language-plaintext highlighter-rouge">filter</code>, anonim fonksiyonlar, higher-order functions ve closure kullanıyorsak Church’ün soyut matematik dünyası hâlâ klavyemizin hemen altındadır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="lambda-calculus" /><category term="fonksiyonel-programlama" /><category term="matematik" /><category term="programlama" /><category term="algoritma" /><category term="bilgisayar-bilimi" /><summary type="html"><![CDATA[Bir programlama dilinde fonksiyon yazarken aslında 1930’larda ortaya atılmış matematiksel bir modelin izlerini takip ederiz. Parametreler, dönüş değerleri, anonim fonksiyonlar ve closure gibi modern araçların kökünde lambda calculus bulunur. Üstelik bu model, bilgisayarların henüz oda büyüklüğünde bile olmadığı bir dönemde geliştirilmiştir!]]></summary></entry></feed>