<?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-25T06:05:38+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">2-SAT Problemlerini SCC ile Doğrusal Zamanda Çözmek</title><link href="https://program.sonsuz.us/posts/2-sat-problemlerini-scc-ile-dogrusal-zamanda-cozmek/" rel="alternate" type="text/html" title="2-SAT Problemlerini SCC ile Doğrusal Zamanda Çözmek" /><published>2026-09-25T00:00:00+00:00</published><updated>2026-09-25T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/2-sat-problemlerini-scc-ile-dogrusal-zamanda-cozmek</id><content type="html" xml:base="https://program.sonsuz.us/posts/2-sat-problemlerini-scc-ile-dogrusal-zamanda-cozmek/"><![CDATA[<p>Bir festivalde her etkinlik için “yapılsın” veya “yapılmasın” kararı verdiğimizi düşünelim. Kurallar ise “Konser yapılacaksa güvenlik ekibi gelsin” ya da “Ya konser ya tiyatro seçilsin” biçiminde olsun. Binlerce ikili karar birbirine bağlandığında deneme-yanılma yaklaşımı hızla çöker. 2-SAT, tam da bu tür sistemlerin çözülebilirliğini yönlü grafikler ve güçlü bağlı bileşenler sayesinde zarifçe belirler.</p>

<p>``</p>

<h2 id="2-sat-nedir">2-SAT nedir?</h2>

<p>2-SAT formülleri, her parantezinde en fazla iki literal bulunan ve bağlaçlarla birleştirilen ifadelerdir:</p>

\[(x_1 \lor \neg x_2) \land (x_2 \lor x_3) \land (\neg x_1 \lor \neg x_3)\]

<p>Literal, bir değişkenin kendisi ($x$) veya değili ($\neg x$) olabilir. Genel bir madde $(a \lor b)$ biçimindedir. Bu ifade yalnızca $a$ ve $b$ birlikte yanlış olduğunda bozulur. Dolayısıyla şu iki zorunluluğu türetebiliriz:</p>

\[(a \lor b) \equiv (\neg a \Rightarrow b) \land (\neg b \Rightarrow a)\]

<p>Her zorunluluğu yönlü bir kenara dönüştürürsek <strong>implikasyon grafiği</strong> ortaya çıkar.</p>

<table>
  <thead>
    <tr>
      <th>Mantıksal kısıt</th>
      <th>Grafiğe eklenen kenarlar</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>$a \lor b$</td>
      <td>$\neg a \to b$, $\neg b \to a$</td>
    </tr>
    <tr>
      <td>$a \Rightarrow b$</td>
      <td>$a \to b$, $\neg b \to \neg a$</td>
    </tr>
    <tr>
      <td>$a$ doğru olmalı</td>
      <td>$\neg a \to a$</td>
    </tr>
    <tr>
      <td>$a$ ve $b$ eşit</td>
      <td>$a \to b$, $b \to a$, $\neg a \to \neg b$, $\neg b \to \neg a$</td>
    </tr>
  </tbody>
</table>

<h2 id="scc-neden-çözümü-belirliyor">SCC neden çözümü belirliyor?</h2>

<p>Bir güçlü bağlı bileşende, her düğümden diğer bütün düğümlere ulaşılabilir. Eğer $x$ ile $\neg x$ aynı SCC içindeyse hem $x \Rightarrow \neg x$ hem de $\neg x \Rightarrow x$ geçerlidir. Hangi doğruluk değerini seçersek seçelim sistem tersini de zorlar; formül çözülemez.</p>

<p>Böylece temel ölçüt şudur:</p>

\[\text{Formül çözülebilir} \iff \forall x_i,\ SCC(x_i) \ne SCC(\neg x_i)\]

<p>Bu, yönlü döngülerin mantıksal çelişkiyi görünür hâle getirmesidir. SCC’ler Tarjan veya Kosaraju algoritmasıyla $O(V+E)$ zamanda bulunur. $n$ değişken ve $m$ madde için grafikte $2n$ düğüm, $2m$ kenar bulunduğundan toplam karmaşıklık $O(n+m)$ olur.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th style="text-align: right">Zaman</th>
      <th>Büyük girdilerde durum</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Tüm atamaları denemek</td>
      <td style="text-align: right">$O(2^n)$</td>
      <td>Hızla kullanılamaz</td>
    </tr>
    <tr>
      <td>SCC tabanlı 2-SAT</td>
      <td style="text-align: right">$O(n+m)$</td>
      <td>Milyonlarca kısıta uygundur</td>
    </tr>
  </tbody>
</table>

<h2 id="kosaraju-ile-uygulama">Kosaraju ile uygulama</h2>

<p>Aşağıdaki C++ kodu, pozitif literali <code class="language-plaintext highlighter-rouge">2*x</code>, negatifini <code class="language-plaintext highlighter-rouge">2*x+1</code> ile temsil eder. <code class="language-plaintext highlighter-rouge">p^1</code> işlemi bir literalden karşıtına geçer. İlk DFS bitiş sırasını, ikinci DFS ise ters grafikte bileşenleri üretir.</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">TwoSAT</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">n</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">g</span><span class="p">,</span> <span class="n">rg</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">order</span><span class="p">,</span> <span class="n">comp</span><span class="p">,</span> <span class="n">used</span><span class="p">;</span>

    <span class="n">TwoSAT</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">n</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">g</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">n</span><span class="p">),</span> <span class="n">rg</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">n</span><span class="p">),</span>
                    <span class="n">comp</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">n</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="n">used</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">n</span><span class="p">)</span> <span class="p">{}</span>

    <span class="c1">// (a OR b) maddesini implikasyon grafiğine ekler.</span>
    <span class="kt">void</span> <span class="nf">addClause</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">g</span><span class="p">[</span><span class="n">a</span> <span class="o">^</span> <span class="mi">1</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="n">b</span><span class="p">);</span>
        <span class="n">rg</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="n">a</span> <span class="o">^</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">g</span><span class="p">[</span><span class="n">b</span> <span class="o">^</span> <span class="mi">1</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
        <span class="n">rg</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="n">b</span> <span class="o">^</span> <span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="nf">dfs1</span><span class="p">(</span><span class="kt">int</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">used</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">u</span> <span class="o">:</span> <span class="n">g</span><span class="p">[</span><span class="n">v</span><span class="p">])</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">used</span><span class="p">[</span><span class="n">u</span><span class="p">])</span> <span class="n">dfs1</span><span class="p">(</span><span class="n">u</span><span class="p">);</span>
        <span class="n">order</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">v</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="nf">dfs2</span><span class="p">(</span><span class="kt">int</span> <span class="n">v</span><span class="p">,</span> <span class="kt">int</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">comp</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">id</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">u</span> <span class="o">:</span> <span class="n">rg</span><span class="p">[</span><span class="n">v</span><span class="p">])</span> <span class="k">if</span> <span class="p">(</span><span class="n">comp</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">dfs2</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">bool</span> <span class="nf">solve</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;&amp;</span> <span class="n">value</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">v</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="o">*</span><span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">v</span><span class="p">)</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">used</span><span class="p">[</span><span class="n">v</span><span class="p">])</span> <span class="n">dfs1</span><span class="p">(</span><span class="n">v</span><span class="p">);</span>

        <span class="n">reverse</span><span class="p">(</span><span class="n">order</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">order</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
        <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">v</span> <span class="o">:</span> <span class="n">order</span><span class="p">)</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">comp</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">dfs2</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">id</span><span class="o">++</span><span class="p">);</span>

        <span class="n">value</span><span class="p">.</span><span class="n">resize</span><span class="p">(</span><span class="n">n</span><span class="p">);</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">comp</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">x</span><span class="p">]</span> <span class="o">==</span> <span class="n">comp</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span> <span class="k">return</span> <span class="nb">false</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">=</span> <span class="n">comp</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">x</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">comp</span><span class="p">[</span><span class="mi">2</span><span class="o">*</span><span class="n">x</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">solve</code>, çelişki yoksa örnek bir doğruluk ataması da üretir. Bileşen numaralarına göre yapılan karşılaştırma, SCC’lerin yoğunlaştırılmış grafiğindeki ters topolojik sıradan yararlanır.</p>

<p>Sonuç olarak 2-SAT’ın gücü, mantığı kaba kuvvetle çözmek yerine onu erişilebilirlik problemine dönüştürmesinden gelir. Bir değişken kendi değiliyle aynı yönlü döngünün içine hapsolmuşsa çelişki kaçınılmazdır; aksi durumda bileşen sırası tutarlı bir atamayı doğrudan verir.</p>

<p><img src="/img/2-sat-problemlerini-73.svg" alt="2-sat-problemlerini-73" /></p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="2-sat" /><category term="graf teorisi" /><category term="güçlü bağlı bileşenler" /><category term="algoritma" /><category term="kosaraju" /><category term="mantıksal kısıtlamalar" /><summary type="html"><![CDATA[Bir festivalde her etkinlik için “yapılsın” veya “yapılmasın” kararı verdiğimizi düşünelim. Kurallar ise “Konser yapılacaksa güvenlik ekibi gelsin” ya da “Ya konser ya tiyatro seçilsin” biçiminde olsun. Binlerce ikili karar birbirine bağlandığında deneme-yanılma yaklaşımı hızla çöker. 2-SAT, tam da bu tür sistemlerin çözülebilirliğini yönlü grafikler ve güçlü bağlı bileşenler sayesinde zarifçe belirler.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/2-sat-problemlerini-73.png" /><media:content medium="image" url="https://program.sonsuz.us/img/2-sat-problemlerini-73.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fenwick Ağacı ile Çok Boyutlu Aralık Sorgularında Bellek Tasarrufu</title><link href="https://program.sonsuz.us/posts/fenwick-agaci-ile-cok-boyutlu-aralik-sorgularinda-bellek-tasarrufu/" rel="alternate" type="text/html" title="Fenwick Ağacı ile Çok Boyutlu Aralık Sorgularında Bellek Tasarrufu" /><published>2026-09-25T00:00:00+00:00</published><updated>2026-09-25T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/fenwick-agaci-ile-cok-boyutlu-aralik-sorgularinda-bellek-tasarrufu</id><content type="html" xml:base="https://program.sonsuz.us/posts/fenwick-agaci-ile-cok-boyutlu-aralik-sorgularinda-bellek-tasarrufu/"><![CDATA[<p>Bir oyun haritasındaki kaynak miktarlarını, elektronik tablodaki hücre değerlerini veya koordinat düzlemindeki hareketli puanları sürekli güncellediğimizi düşünelim. Her değişiklikten sonra dikdörtgen bir bölgenin toplamını hesaplamak, naif yöntemle pahalıdır. Fenwick Ağacı ya da kısa adıyla <strong>BIT (Binary Indexed Tree)</strong>, segment ağacına göre daha az kod ve yardımcı bellek kullanarak bu işi logaritmik sürede yapar.</p>

<p>``</p>

<h2 id="fenwick-ağacının-temel-fikri">Fenwick Ağacının temel fikri</h2>

<p>Tek boyutlu BIT, her indeksin belirli uzunluktaki bir aralığın toplamını saklamasına dayanır. Bir indeksin sorumlu olduğu aralık, ikili gösterimindeki en düşük anlamlı bit ile belirlenir:</p>

\[lowbit(x)=x\ \&amp;\ (-x)\]

<p>Örneğin $12=(1100)_2$ için $lowbit(12)=4$ olur. Güncelleme sırasında indeksleri $lowbit(i)$ kadar artırır, önek toplamında ise aynı miktarı çıkarırız. Böylece her işlem en fazla $O(\log n)$ düğüme dokunur.</p>

<p>İki boyutta aynı düşünceyi satır ve sütun eksenlerine ayrı ayrı uygularız. $(x,y)$ noktasına eklenen değer, iki iç içe Fenwick yürüyüşüyle ilgili hücrelere taşınır. Bir dikdörtgen toplamı ise dahil etme–hariç tutma ilkesiyle bulunur:</p>

\[R(x_1,y_1,x_2,y_2)=P(x_2,y_2)-P(x_1-1,y_2)-P(x_2,y_1-1)+P(x_1-1,y_1-1)\]

<p>Burada $P(x,y)$, $(1,1)$ ile $(x,y)$ arasındaki önek toplamıdır.</p>

<h2 id="segment-ağacı-mı-fenwick-mi">Segment ağacı mı, Fenwick mi?</h2>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th style="text-align: right">2B Fenwick Ağacı</th>
      <th style="text-align: right">2B Segment Ağacı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Nokta güncelleme</td>
      <td style="text-align: right">$O(\log n\log m)$</td>
      <td style="text-align: right">$O(\log n\log m)$</td>
    </tr>
    <tr>
      <td>Dikdörtgen toplamı</td>
      <td style="text-align: right">$O(\log n\log m)$</td>
      <td style="text-align: right">$O(\log n\log m)$</td>
    </tr>
    <tr>
      <td>Tipik yardımcı bellek</td>
      <td style="text-align: right">Yaklaşık $nm$</td>
      <td style="text-align: right">Yaklaşık $4n\cdot4m$</td>
    </tr>
    <tr>
      <td>Uygulama karmaşıklığı</td>
      <td style="text-align: right">Düşük</td>
      <td style="text-align: right">Yüksek</td>
    </tr>
    <tr>
      <td>Min/maks gibi işlemler</td>
      <td style="text-align: right">Sınırlı</td>
      <td style="text-align: right">Daha esnek</td>
    </tr>
  </tbody>
</table>

<p><img src="/img/fenwick-agaci-ile-35.svg" alt="fenwick-agaci-ile-35" /></p>

<p>BIT’in önemli sınırlaması, işlemin tersinin bulunabilmesidir. Toplamada çıkarma sayesinde iki önekten aralık üretilebilir. Ancak genel minimum sorgusunda “önek minimumlarını çıkarma” diye bir işlem yoktur. Bu durumda segment ağacı daha doğru seçimdir.</p>

<h2 id="bellek-dostu-2b-uygulama">Bellek dostu 2B uygulama</h2>

<p>Aşağıdaki C++ sınıfı matrisi satır satır tek bir <code class="language-plaintext highlighter-rouge">vector</code> içinde tutar. Ayrı ayrı vektörler oluşturmadığı için tahsis maliyetini azaltır ve önbellek yerelliğini iyileştirir.</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">class</span> <span class="nc">Fenwick2D</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">m</span><span class="p">;</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;</span> <span class="n">bit</span><span class="p">;</span>

    <span class="kt">long</span> <span class="kt">long</span><span class="o">&amp;</span> <span class="n">at</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">bit</span><span class="p">[</span><span class="n">x</span> <span class="o">*</span> <span class="p">(</span><span class="n">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">y</span><span class="p">];</span>
    <span class="p">}</span>

<span class="k">public</span><span class="o">:</span>
    <span class="n">Fenwick2D</span><span class="p">(</span><span class="kt">int</span> <span class="n">rows</span><span class="p">,</span> <span class="kt">int</span> <span class="n">cols</span><span class="p">)</span>
        <span class="o">:</span> <span class="n">n</span><span class="p">(</span><span class="n">rows</span><span class="p">),</span> <span class="n">m</span><span class="p">(</span><span class="n">cols</span><span class="p">),</span> <span class="n">bit</span><span class="p">((</span><span class="n">rows</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">cols</span> <span class="o">+</span> <span class="mi">1</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{}</span>

    <span class="c1">// (x, y) noktasına delta ekler.</span>
    <span class="kt">void</span> <span class="nf">add</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">,</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">delta</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="n">x</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="n">i</span> <span class="o">&amp;</span> <span class="o">-</span><span class="n">i</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="n">y</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;=</span> <span class="n">m</span><span class="p">;</span> <span class="n">j</span> <span class="o">+=</span> <span class="n">j</span> <span class="o">&amp;</span> <span class="o">-</span><span class="n">j</span><span class="p">)</span>
                <span class="n">at</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span> <span class="o">+=</span> <span class="n">delta</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// (1,1) ile (x,y) arasındaki toplamı döndürür.</span>
    <span class="kt">long</span> <span class="kt">long</span> <span class="nf">prefix</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">long</span> <span class="kt">long</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">0</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="n">x</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">-=</span> <span class="n">i</span> <span class="o">&amp;</span> <span class="o">-</span><span class="n">i</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="n">y</span><span class="p">;</span> <span class="n">j</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">-=</span> <span class="n">j</span> <span class="o">&amp;</span> <span class="o">-</span><span class="n">j</span><span class="p">)</span>
                <span class="n">result</span> <span class="o">+=</span> <span class="n">at</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">long</span> <span class="kt">long</span> <span class="nf">rectangle</span><span class="p">(</span><span class="kt">int</span> <span class="n">x1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y2</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">prefix</span><span class="p">(</span><span class="n">x2</span><span class="p">,</span> <span class="n">y2</span><span class="p">)</span> <span class="o">-</span> <span class="n">prefix</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y2</span><span class="p">)</span>
             <span class="o">-</span> <span class="n">prefix</span><span class="p">(</span><span class="n">x2</span><span class="p">,</span> <span class="n">y1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">prefix</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p>İndekslerin <strong>1’den başlaması</strong> önemlidir; sıfır için <code class="language-plaintext highlighter-rouge">lowbit(0)</code> sıfırdır ve güncelleme döngüsü ilerlemez.</p>

<h2 id="boyutlar-devasa-olduğunda">Boyutlar devasa olduğunda</h2>

<p>Koordinatlar milyarlara ulaşıyor fakat yalnızca birkaç bin nokta kullanılıyorsa tam matris ayırmak anlamsızdır. Güncelleme ve sorgulardaki koordinatları sıralayıp benzersizleştirerek <strong>koordinat sıkıştırma</strong> uygulanabilir. Her gerçek koordinat, sıralamadaki 1 tabanlı sırasına dönüştürülür.</p>

<p>$d$ boyutta güncelleme ve sorgu maliyeti yaklaşık $O(\log^d n)$ olur. Buna karşılık yoğun depolama $O(n^d)$ büyüdüğünden, pratikte üç ve üzeri boyutlarda yalnızca erişilen Fenwick hücrelerini karma tablo veya sıkıştırılmış iç vektörlerle saklamak gerekir. Kısacası toplam ve frekans sorgularında BIT, küçük kodu ve düşük sabit maliyetiyle güçlüdür; karmaşık birleştirme işlemlerinde ise segment ağacı hâlâ sahnededir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="fenwick ağacı" /><category term="bit" /><category term="veri yapıları" /><category term="algoritma" /><category term="aralık sorguları" /><category term="bellek optimizasyonu" /><category term="cpp" /><summary type="html"><![CDATA[Bir oyun haritasındaki kaynak miktarlarını, elektronik tablodaki hücre değerlerini veya koordinat düzlemindeki hareketli puanları sürekli güncellediğimizi düşünelim. Her değişiklikten sonra dikdörtgen bir bölgenin toplamını hesaplamak, naif yöntemle pahalıdır. Fenwick Ağacı ya da kısa adıyla BIT (Binary Indexed Tree), segment ağacına göre daha az kod ve yardımcı bellek kullanarak bu işi logaritmik sürede yapar.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/fenwick-agaci-ile-35.png" /><media:content medium="image" url="https://program.sonsuz.us/img/fenwick-agaci-ile-35.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">FFT ile Dev Sayıları O(N log N) Sürede Çarpmak</title><link href="https://program.sonsuz.us/posts/fft-ile-dev-sayilari-on-log-n-surede-carpmak/" rel="alternate" type="text/html" title="FFT ile Dev Sayıları O(N log N) Sürede Çarpmak" /><published>2026-09-25T00:00:00+00:00</published><updated>2026-09-25T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/fft-ile-dev-sayilari-on-log-n-surede-carpmak</id><content type="html" xml:base="https://program.sonsuz.us/posts/fft-ile-dev-sayilari-on-log-n-surede-carpmak/"><![CDATA[<p>İlkokulda öğrendiğimiz uzun çarpma yöntemi küçük sayılarda harikadır; ancak milyonlarca basamak söz konusu olduğunda işler dramatik biçimde yavaşlar. Neyse ki sinyal işlemenin yıldızı Hızlı Fourier Dönüşümü, yani FFT, basamakları birer polinom katsayısı gibi ele alarak çarpma işlemini yaklaşık $O(N \log N)$ sürede gerçekleştirebilir.</p>

<p>``</p>

<h2 id="uzun-çarpma-neden-yavaş">Uzun çarpma neden yavaş?</h2>

<p>İki sayının da $N$ basamaklı olduğunu düşünelim. Geleneksel yöntemde birinci sayının her basamağı, ikinci sayının her basamağıyla çarpılır. Dolayısıyla yaklaşık $N^2$ işlem gerekir:</p>

\[T(N)=O(N^2)\]

<p>Basamak sayısı iki katına çıktığında çalışma miktarı yaklaşık dört katına çıkar. FFT tabanlı yaklaşımda ise büyüme çok daha sakindir:</p>

\[T(N)=O(N\log N)\]

<table>
  <thead>
    <tr>
      <th>Yöntem</th>
      <th style="text-align: right">Zaman karmaşıklığı</th>
      <th>Temel fikir</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Uzun çarpma</td>
      <td style="text-align: right">$O(N^2)$</td>
      <td>Tüm basamak çiftlerini çarp</td>
    </tr>
    <tr>
      <td>Karatsuba</td>
      <td style="text-align: right">$O(N^{1.585})$</td>
      <td>Çarpımları parçalayarak azalt</td>
    </tr>
    <tr>
      <td>FFT</td>
      <td style="text-align: right">$O(N\log N)$</td>
      <td>Konvolüsyonu frekans uzayında hesapla</td>
    </tr>
  </tbody>
</table>

<h2 id="sayıları-polinoma-dönüştürmek">Sayıları polinoma dönüştürmek</h2>

<p>Örneğin $1234$ sayısını şu polinomla temsil edebiliriz:</p>

\[A(x)=4+3x+2x^2+x^3\]

<p>Burada $x=10$ seçildiğinde yeniden 1234 elde edilir. İki sayıyı çarpmak, bunlara karşılık gelen polinomları çarpmaya eşdeğerdir. Polinom çarpımındaki katsayılar ise ayrık konvolüsyonla bulunur:</p>

\[c_k=\sum_{i=0}^{k}a_i b_{k-i}\]

<p>Bu formül doğrudan uygulanırsa yine $O(N^2)$ zaman harcar. FFT’nin sihri, konvolüsyonu noktasal çarpıma dönüştürmesidir:</p>

\[\operatorname{FFT}(a*b)=\operatorname{FFT}(a)\cdot\operatorname{FFT}(b)\]

<p>Yani önce katsayıları frekans uzayına taşır, karşılık gelen değerleri tek tek çarpar ve ters FFT ile geri döneriz.</p>

<h2 id="birim-kökler-ve-böl-parçala-yönet">Birim kökler ve böl-parçala-yönet</h2>

<p>Ayrık Fourier Dönüşümü şu şekilde tanımlanır:</p>

\[X_k=\sum_{j=0}^{N-1}x_j e^{-2\pi i jk/N}\]

<p>Üstel ifadeler, karmaşık düzlemde eşit aralıklarla yerleşmiş $N$’inci birim köklerdir. FFT, çift ve tek indeksli katsayıları ayırarak aynı hesaplamaları tekrar yapmaktan kurtulur. Her seviyede toplam $O(N)$ iş yapılır ve yaklaşık $\log_2N$ seviye bulunur.</p>

<table>
  <thead>
    <tr>
      <th>Alan</th>
      <th>İşlem</th>
      <th style="text-align: right">Maliyet</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Katsayı uzayı</td>
      <td>Konvolüsyon</td>
      <td style="text-align: right">$O(N^2)$</td>
    </tr>
    <tr>
      <td>Frekans uzayı</td>
      <td>Noktasal çarpım</td>
      <td style="text-align: right">$O(N)$</td>
    </tr>
    <tr>
      <td>FFT ve ters FFT</td>
      <td>Dönüşüm</td>
      <td style="text-align: right">$O(N\log N)$</td>
    </tr>
  </tbody>
</table>

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

<p>Aşağıdaki örnek, sayıları onluk basamaklarına ayırır. NumPy FFT hesaplamasını yapar; son döngü ise yuvarlama ve elde taşıma işlemlerini düzeltir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="k">def</span> <span class="nf">fft_multiply</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">a</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">([</span><span class="nf">int</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">x</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]],</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>
    <span class="n">b</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">([</span><span class="nf">int</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">y</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]],</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>

    <span class="n">size</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">while</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">+</span> <span class="nf">len</span><span class="p">(</span><span class="n">b</span><span class="p">):</span>
        <span class="n">size</span> <span class="o">*=</span> <span class="mi">2</span>

    <span class="n">fa</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="nf">fft</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
    <span class="n">fb</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="nf">fft</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
    <span class="n">coefficients</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">rint</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="nf">ifft</span><span class="p">(</span><span class="n">fa</span> <span class="o">*</span> <span class="n">fb</span><span class="p">).</span><span class="n">real</span><span class="p">).</span><span class="nf">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">int64</span><span class="p">)</span>

    <span class="n">carry</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">digits</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">coefficients</span><span class="p">:</span>
        <span class="n">total</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="n">carry</span>
        <span class="n">digits</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">total</span> <span class="o">%</span> <span class="mi">10</span><span class="p">)</span>
        <span class="n">carry</span> <span class="o">=</span> <span class="n">total</span> <span class="o">//</span> <span class="mi">10</span>

    <span class="k">while</span> <span class="n">carry</span><span class="p">:</span>
        <span class="n">digits</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">carry</span> <span class="o">%</span> <span class="mi">10</span><span class="p">)</span>
        <span class="n">carry</span> <span class="o">//=</span> <span class="mi">10</span>

    <span class="k">while</span> <span class="nf">len</span><span class="p">(</span><span class="n">digits</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">digits</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">digits</span><span class="p">.</span><span class="nf">pop</span><span class="p">()</span>

    <span class="k">return</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">digits</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">fft_multiply</span><span class="p">(</span><span class="sh">"</span><span class="s">123456789012345</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">987654321098765</span><span class="sh">"</span><span class="p">))</span>
</code></pre></div></div>

<p>Bu uygulama öğreticidir; kayan nokta hataları nedeniyle gerçekten milyonlarca basamakta güvenilir olmayabilir. Pratik sistemler basamakları daha büyük bloklara ayırır veya modüler aritmetik kullanan Sayı Teorik Dönüşümü’nü (NTT) tercih eder. Birden fazla asal modülle yapılan sonuçlar Çin Kalan Teoremi aracılığıyla birleştirilebilir.</p>

<p>Sonuçta FFT yalnızca ses ve görüntü analizi yapan bir araç değildir. Büyük sayı çarpımını polinom çarpımına, polinom çarpımını konvolüsyona ve konvolüsyonu ucuz noktasal çarpımlara dönüştüren güçlü bir algoritmik köprüdür.</p>

<p><img src="/img/fft-ile-dev-80.svg" alt="fft-ile-dev-80" /></p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="fft" /><category term="algoritma" /><category term="matematik" /><category term="python" /><category term="büyük sayılar" /><category term="polinomlar" /><summary type="html"><![CDATA[İlkokulda öğrendiğimiz uzun çarpma yöntemi küçük sayılarda harikadır; ancak milyonlarca basamak söz konusu olduğunda işler dramatik biçimde yavaşlar. Neyse ki sinyal işlemenin yıldızı Hızlı Fourier Dönüşümü, yani FFT, basamakları birer polinom katsayısı gibi ele alarak çarpma işlemini yaklaşık $O(N \log N)$ sürede gerçekleştirebilir.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/fft-ile-dev-80.png" /><media:content medium="image" url="https://program.sonsuz.us/img/fft-ile-dev-80.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Matris Üs Alma ile Devasa Doğrusal Tekrarlamalı Dizileri Hesaplamak</title><link href="https://program.sonsuz.us/posts/matris-us-alma-ile-devasa-dogrusal-tekrarlamali-dizileri-hesaplamak/" rel="alternate" type="text/html" title="Matris Üs Alma ile Devasa Doğrusal Tekrarlamalı Dizileri Hesaplamak" /><published>2026-09-25T00:00:00+00:00</published><updated>2026-09-25T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/matris-us-alma-ile-devasa-dogrusal-tekrarlamali-dizileri-hesaplamak</id><content type="html" xml:base="https://program.sonsuz.us/posts/matris-us-alma-ile-devasa-dogrusal-tekrarlamali-dizileri-hesaplamak/"><![CDATA[<p>Fibonacci dizisinin milyonuncu, hatta milyarıncı terimini hesaplamak istediğimizi düşünelim. Diziyi baştan sona üretmek kolaydır; fakat bilgisayara milyarlarca kez toplama yaptırmak, çay demlenirken başlayıp yeni bir çay plantasyonu kurulunca biten bir plan olabilir. Neyse ki doğrusal tekrarlamaları matris biçiminde ifade ederek problemi $O(n)$ zamandan $O(\log n)$ zamana indirebiliriz.
``
Fibonacci dizisi şu bağıntıyla tanımlanır:</p>

\[F_n = F_{n-1} + F_{n-2}, \qquad F_0=0,\ F_1=1\]

<p>Klasik döngü yaklaşımı, önceki iki değeri saklayıp $F_2$’den $F_n$’e kadar ilerler. Bellek tüketimi $O(1)$ olsa da çalışma süresi $O(n)$ olur. Buradaki kritik gözlem, bir sonraki durumun mevcut durumun <strong>doğrusal dönüşümü</strong> olmasıdır.</p>

<p>Durumumuzu sütun vektörü olarak yazalım:</p>

\[\begin{bmatrix}
F_n \\
F_{n-1}
\end{bmatrix}
=
\begin{bmatrix}
1 &amp; 1 \\
1 &amp; 0
\end{bmatrix}
\begin{bmatrix}
F_{n-1} \\
F_{n-2}
\end{bmatrix}\]

<p>Buradaki dönüşüm matrisi $A$ olsun. Aynı dönüşümü tekrar tekrar uyguladığımızda matris kuvvetleri ortaya çıkar:</p>

\[\begin{bmatrix}
F_n \\
F_{n-1}
\end{bmatrix}
=A^{n-1}
\begin{bmatrix}
F_1 \\
F_0
\end{bmatrix}\]

<p>Yani artık hedefimiz diziyi adım adım yürütmek değil, $A^{n-1}$ matrisini hızlı hesaplamaktır.</p>

<table>
  <thead>
    <tr>
      <th>Yöntem</th>
      <th style="text-align: right">Zaman karmaşıklığı</th>
      <th style="text-align: right">Bellek</th>
      <th>Temel fikir</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Özyinelemeli Fibonacci</td>
      <td style="text-align: right">$O(2^n)$</td>
      <td style="text-align: right">$O(n)$</td>
      <td>Aynı alt problemleri tekrar hesaplar</td>
    </tr>
    <tr>
      <td>Döngü / dinamik programlama</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(1)$</td>
      <td>Terimleri sırayla üretir</td>
    </tr>
    <tr>
      <td>Matris üs alma</td>
      <td style="text-align: right">$O(\log n)$</td>
      <td style="text-align: right">$O(1)$</td>
      <td>Dönüşüm matrisini karesini alarak yükseltir</td>
    </tr>
  </tbody>
</table>

<h2 id="hızlı-üs-alma-neden-logaritmiktir">Hızlı üs alma neden logaritmiktir?</h2>

<p>Bir sayının kuvvetini hesaplarken kullanılan ikili üs alma tekniği matrislerde de geçerlidir. Üs çiftse</p>

\[A^n=(A^{n/2})^2\]

<p>olur. Üs tekse bir adet $A$ dışarı alınır:</p>

\[A^n=A\cdot A^{n-1}\]

<p>Her adımda üs yaklaşık yarıya indiği için yalnızca $\log_2 n$ tur gerekir. Matris boyutu Fibonacci için sabit, yani $2\times2$ olduğundan her çarpım sabit maliyetlidir.</p>

<p>Aşağıdaki C++ kodu sonucu bir modüler sayı altında hesaplar. Mod kullanmak önemlidir; çünkü Fibonacci sayıları son derece hızlı büyür ve standart tam sayı türlerine sığmaz.</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">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">MOD</span> <span class="o">=</span> <span class="mi">1'000'000'007</span><span class="p">;</span>

<span class="k">struct</span> <span class="nc">Matrix</span> <span class="p">{</span>
    <span class="kt">long</span> <span class="kt">long</span> <span class="n">a</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">2</span><span class="p">]{};</span>
<span class="p">};</span>

<span class="n">Matrix</span> <span class="nf">multiply</span><span class="p">(</span><span class="k">const</span> <span class="n">Matrix</span><span class="o">&amp;</span> <span class="n">x</span><span class="p">,</span> <span class="k">const</span> <span class="n">Matrix</span><span class="o">&amp;</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Matrix</span> <span class="n">result</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">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">i</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="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span>
            <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">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">k</span><span class="p">)</span>
                <span class="n">result</span><span class="p">.</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span>
                    <span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">x</span><span class="p">.</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">*</span> <span class="n">y</span><span class="p">.</span><span class="n">a</span><span class="p">[</span><span class="n">k</span><span class="p">][</span><span class="n">j</span><span class="p">])</span> <span class="o">%</span> <span class="n">MOD</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">Matrix</span> <span class="nf">power</span><span class="p">(</span><span class="n">Matrix</span> <span class="n">base</span><span class="p">,</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">exponent</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Matrix</span> <span class="n">result</span><span class="p">;</span>
    <span class="n">result</span><span class="p">.</span><span class="n">a</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="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// Birim matris</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">exponent</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">exponent</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">multiply</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">base</span><span class="p">);</span>
        <span class="n">base</span> <span class="o">=</span> <span class="n">multiply</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">base</span><span class="p">);</span>
        <span class="n">exponent</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">long</span> <span class="kt">long</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="kt">long</span> <span class="kt">long</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">Matrix</span> <span class="n">transition</span><span class="p">{{{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">}}};</span>
    <span class="k">return</span> <span class="n">power</span><span class="p">(</span><span class="n">transition</span><span class="p">,</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">).</span><span class="n">a</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="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">fibonacci</span><span class="p">(</span><span class="mi">1'000'000'000</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">multiply</code> iki dönüşümü birleştirir; <code class="language-plaintext highlighter-rouge">power</code> ise üssün ikili gösterimini kullanır. Başlangıçta sonuç birim matristir, çünkü $A^0=I$ olmalıdır. Üssün ilgili biti bir olduğunda mevcut taban sonuca katılır.</p>

<h2 id="yalnızca-fibonacci-değil">Yalnızca Fibonacci değil</h2>

<p>Bu yöntem, sabit katsayılı her doğrusal tekrarlamaya uygulanabilir. Örneğin</p>

\[X_n=2X_{n-1}+3X_{n-2}\]

<p>için dönüşüm matrisi</p>

\[\begin{bmatrix}
2 &amp; 3 \\
1 &amp; 0
\end{bmatrix}\]

<p>olur. Daha fazla geçmiş terime bağlı bağıntılarda matris boyutu büyür; mantık değişmez. Böylece matris, dizinin hafızasını taşıyan küçük bir zaman makinesine dönüşür: milyarlarca adımı tek tek yürümek yerine onları logaritmik sayıda sıçramayla geçer.</p>

<p><img src="/img/matris-us-alma-18.svg" alt="matris-us-alma-18" /></p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="matris" /><category term="fibonacci" /><category term="algoritma" /><category term="dinamik-programlama" /><category term="hızlı-üs-alma" /><category term="c++" /><summary type="html"><![CDATA[Fibonacci dizisinin milyonuncu, hatta milyarıncı terimini hesaplamak istediğimizi düşünelim. Diziyi baştan sona üretmek kolaydır; fakat bilgisayara milyarlarca kez toplama yaptırmak, çay demlenirken başlayıp yeni bir çay plantasyonu kurulunca biten bir plan olabilir. Neyse ki doğrusal tekrarlamaları matris biçiminde ifade ederek problemi $O(n)$ zamandan $O(\log n)$ zamana indirebiliriz.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/matris-us-alma-18.png" /><media:content medium="image" url="https://program.sonsuz.us/img/matris-us-alma-18.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Möbius İnversiyonu ile Sayılar Teorisinde Kapsama-Dışlama</title><link href="https://program.sonsuz.us/posts/mobius-inversiyonu-ile-sayilar-teorisinde-kapsama-dislama/" rel="alternate" type="text/html" title="Möbius İnversiyonu ile Sayılar Teorisinde Kapsama-Dışlama" /><published>2026-09-25T00:00:00+00:00</published><updated>2026-09-25T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/mobius-inversiyonu-ile-sayilar-teorisinde-kapsama-dislama</id><content type="html" xml:base="https://program.sonsuz.us/posts/mobius-inversiyonu-ile-sayilar-teorisinde-kapsama-dislama/"><![CDATA[<p>Bazı sayılar teorisi problemleri, bölenlerin ve ortak çarpanların iç içe geçmesi yüzünden çözülmez bir düğüm gibi görünür. Möbius inversiyonu ise bu düğümü tek tek açmak yerine matematiksel olarak tersine çevirir. Aralarında asal çiftleri saymaktan bölen toplamlarından özgün bir fonksiyonu geri kazanmaya kadar pek çok işlem, böylece zarif bir kapsama-dışlama hesabına dönüşür.</p>

<p><img src="/img/mobius-inversiyonu-ile-53.svg" alt="mobius-inversiyonu-ile-53" /></p>

<p>``</p>
<h2 id="möbius-fonksiyonu-nedir">Möbius fonksiyonu nedir?</h2>

<p>Möbius fonksiyonu $\mu(n)$, pozitif tam sayıları üç gruba ayırır:</p>

\[\mu(n)=
\begin{cases}
1, &amp; n=1,\\
0, &amp; n \text{ bir asalın karesine bölünüyorsa},\\
(-1)^k, &amp; n \text{ farklı } k \text{ asalın çarpımıysa}.
\end{cases}\]

<p>Örneğin $\mu(6)=1$ olur; çünkü $6=2\cdot3$ ve iki farklı asal çarpanı vardır. Buna karşılık $12$, $2^2$ ile bölündüğü için $\mu(12)=0$ değerini alır.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">$n$</th>
      <th>Asal çarpan yapısı</th>
      <th style="text-align: right">$\mu(n)$</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">1</td>
      <td>Boş çarpım</td>
      <td style="text-align: right">1</td>
    </tr>
    <tr>
      <td style="text-align: right">6</td>
      <td>$2\cdot3$</td>
      <td style="text-align: right">1</td>
    </tr>
    <tr>
      <td style="text-align: right">30</td>
      <td>$2\cdot3\cdot5$</td>
      <td style="text-align: right">-1</td>
    </tr>
    <tr>
      <td style="text-align: right">12</td>
      <td>$2^2\cdot3$</td>
      <td style="text-align: right">0</td>
    </tr>
  </tbody>
</table>

<p>Fonksiyonun sihirli özelliği şudur:</p>

\[\sum_{d\mid n}\mu(d)=
\begin{cases}
1, &amp; n=1,\\
0, &amp; n&gt;1.
\end{cases}\]

<p>Bu eşitlik, istenmeyen ortak bölenleri artı ve eksi işaretlerle yok eden kapsama-dışlama ilkesinin aritmetik karşılığıdır.</p>

<h2 id="möbius-inversiyon-formülü">Möbius inversiyon formülü</h2>

<p>Bir $f$ fonksiyonunun bölenler üzerindeki toplamıyla $g$ fonksiyonu tanımlansın:</p>

\[g(n)=\sum_{d\mid n}f(d).\]

<p>Möbius inversiyonu, toplamın içine saklanmış $f$ değerini geri çıkarır:</p>

\[f(n)=\sum_{d\mid n}\mu(d)g\left(\frac{n}{d}\right).\]

<table>
  <thead>
    <tr>
      <th>İleri işlem</th>
      <th>Ters işlem</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bölenlerden bilgi toplar</td>
      <td>Toplanan bilgiyi ayrıştırır</td>
    </tr>
    <tr>
      <td>$g(n)=\sum_{d\mid n}f(d)$</td>
      <td>$f(n)=\sum_{d\mid n}\mu(d)g(n/d)$</td>
    </tr>
    <tr>
      <td>Birikimli görünüm üretir</td>
      <td>Özgün katkıları bulur</td>
    </tr>
  </tbody>
</table>

<p>Bunun klasik örneği Euler’in totient fonksiyonudur. $n$ sayısının pozitif bölenlerinin totient değerleri toplamı $n$ eder:</p>

\[n=\sum_{d\mid n}\varphi(d).\]

<p>İnversiyon uygulandığında doğrudan şu formül elde edilir:</p>

\[\varphi(n)=\sum_{d\mid n}\mu(d)\frac{n}{d}.\]

<h2 id="aralarında-asal-çiftleri-saymak">Aralarında asal çiftleri saymak</h2>

<p>$1\leq a,b\leq N$ koşulunu sağlayan ve $\gcd(a,b)=1$ olan sıralı çiftleri sayalım. Bir çiftin her iki elemanı da $d$ ile bölünüyorsa yaklaşık değil, tam olarak $\lfloor N/d\rfloor^2$ seçenek vardır. Möbius fonksiyonu ortak bölenleri kapsama-dışlama ile temizler:</p>

\[C(N)=\sum_{d=1}^{N}\mu(d)\left\lfloor\frac{N}{d}\right\rfloor^2.\]

<p>Burada $\mu(d)=0$ olan kare çarpanlı değerler otomatik olarak elenir. Tek sayıda farklı asal içerenler çıkarılır, çift sayıda asal içerenler yeniden eklenir. Kısacası Möbius fonksiyonu, kapsama-dışlamanın muhasebecisidir; üstelik hesap makinesini hiç düşürmez!</p>

<h2 id="python-ile-doğrusal-elek">Python ile doğrusal elek</h2>

<p>Aşağıdaki kod, $1$ ile $N$ arasındaki Möbius değerlerini doğrusal zamanda üretir ve aralarında asal sıralı çiftlerin sayısını hesaplar:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">mobius_sieve</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="n">mu</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="n">primes</span><span class="p">,</span> <span class="n">composite</span> <span class="o">=</span> <span class="p">[],</span> <span class="p">[</span><span class="bp">False</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">composite</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
            <span class="n">primes</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
            <span class="n">mu</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
        <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">primes</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">*</span> <span class="n">p</span> <span class="o">&gt;</span> <span class="n">n</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="n">composite</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="n">p</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">mu</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
                <span class="k">break</span>
            <span class="n">mu</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="n">mu</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">mu</span>


<span class="k">def</span> <span class="nf">coprime_pairs</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="n">mu</span> <span class="o">=</span> <span class="nf">mobius_sieve</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="k">return</span> <span class="nf">sum</span><span class="p">(</span><span class="n">mu</span><span class="p">[</span><span class="n">d</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="n">d</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">coprime_pairs</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>  <span class="c1"># 63
</span></code></pre></div></div>

<p>Elek her bileşik sayıyı kontrollü biçimde işler; böylece karmaşıklık $O(N)$, toplam hesabı da $O(N)$ olur. Büyük sınırlar için $\lfloor N/d\rfloor$ değerlerinin yalnızca yaklaşık $2\sqrt N$ farklı değer aldığından yararlanılarak gruplama yapılabilir.</p>

<p>Möbius inversiyonunu gördüğünüzde temel soruyu sorun: “Elimdeki fonksiyon, bölenler üzerinden birikmiş başka bir bilginin toplamı mı?” Yanıt evetse, tersine çevirme formülü büyük olasılıkla problemi birkaç satırlık temiz bir toplama indirecektir.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="möbius inversiyonu" /><category term="sayılar teorisi" /><category term="kapsama-dışlama" /><category term="aralarında asallık" /><category term="bölen toplamları" /><category term="algoritma" /><summary type="html"><![CDATA[Bazı sayılar teorisi problemleri, bölenlerin ve ortak çarpanların iç içe geçmesi yüzünden çözülmez bir düğüm gibi görünür. Möbius inversiyonu ise bu düğümü tek tek açmak yerine matematiksel olarak tersine çevirir. Aralarında asal çiftleri saymaktan bölen toplamlarından özgün bir fonksiyonu geri kazanmaya kadar pek çok işlem, böylece zarif bir kapsama-dışlama hesabına dönüşür.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/mobius-inversiyonu-ile-53.png" /><media:content medium="image" url="https://program.sonsuz.us/img/mobius-inversiyonu-ile-53.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Ağaç İçi Mesafelerin Gizli Silahı: Centroid Decomposition</title><link href="https://program.sonsuz.us/posts/agac-ici-mesafelerin-gizli-silahi-centroid-decomposition/" rel="alternate" type="text/html" title="Ağaç İçi Mesafelerin Gizli Silahı: Centroid Decomposition" /><published>2026-09-24T00:00:00+00:00</published><updated>2026-09-24T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/agac-ici-mesafelerin-gizli-silahi-centroid-decomposition</id><content type="html" xml:base="https://program.sonsuz.us/posts/agac-ici-mesafelerin-gizli-silahi-centroid-decomposition/"><![CDATA[<p>Bir ağaçta iki düğüm arasındaki mesafeyi hesaplamak kolaydır; fakat binlerce güncelleme ve sorgu geldiğinde işler hızla dallanıp budaklanır. Centroid Decomposition, ağacı dengeli biçimde parçalara ayırarak mesafe problemlerini yaklaşık $O(\log n)$ katman üzerinden çözmemizi sağlar. Kısacası ağacın fiziksel yapısını değiştirmeden, onun üzerinde ikinci ve dengeli bir “centroid ağacı” kurarız.</p>

<p><img src="/img/agac-ici-mesafelerin-68.svg" alt="agac-ici-mesafelerin-68" /></p>

<p>``</p>

<h2 id="centroid-nedir">Centroid nedir?</h2>

<p>Bir ağacın centroid’i, kaldırıldığında geriye kalan bağlı bileşenlerin hiçbirinin toplam düğüm sayısının yarısından büyük olmadığı düğümdür. $n$ düğümlü bir ağaçta centroid $c$ ise her bileşen için şu koşul geçerlidir:</p>

\[\text{bileşenBoyutu} \leq \frac{n}{2}\]

<p>Her ağacın en az bir, en fazla iki centroid’i vardır. Algoritmada bunlardan herhangi birini seçebiliriz. Centroid’i kaldırır, oluşan alt ağaçların her birine aynı işlemi özyineli uygularız. Her seviyede parça boyutu en az yarıya indiğinden decomposition yüksekliği $O(\log n)$ olur.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th style="text-align: right">Güncelleme</th>
      <th style="text-align: right">Mesafe sorgusu</th>
      <th style="text-align: right">Ön işleme</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Her sorguda DFS/BFS</td>
      <td style="text-align: right">$O(1)$</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(1)$</td>
    </tr>
    <tr>
      <td>LCA ile sabit düğümler</td>
      <td style="text-align: right">Yok</td>
      <td style="text-align: right">$O(\log n)$</td>
      <td style="text-align: right">$O(n\log n)$</td>
    </tr>
    <tr>
      <td>Centroid Decomposition</td>
      <td style="text-align: right">$O(\log n)$</td>
      <td style="text-align: right">$O(\log n)$</td>
      <td style="text-align: right">$O(n\log n)$</td>
    </tr>
  </tbody>
</table>

<p>LCA iki belirli düğümün mesafesinde harikadır. Centroid Decomposition ise “işaretli en yakın düğüm”, “$k$ mesafedeki düğüm sayısı” veya dinamik renk güncellemeleri gibi toplu problemlerde parlar.</p>

<h2 id="centroid-nasıl-bulunur">Centroid nasıl bulunur?</h2>

<p>Önce alt ağaç boyutlarını hesaplarız. Ardından boyutu toplamın yarısından büyük olan bir çocuk varsa o yöne ilerleriz. Böyle bir çocuk kalmadığında centroid’i bulmuş oluruz.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">calcSize</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">p</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">sub</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">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">v</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="k">if</span> <span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="n">p</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">removed</span><span class="p">[</span><span class="n">v</span><span class="p">])</span>
            <span class="n">sub</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">+=</span> <span class="n">calcSize</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">u</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">sub</span><span class="p">[</span><span class="n">u</span><span class="p">];</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">findCentroid</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">p</span><span class="p">,</span> <span class="kt">int</span> <span class="n">total</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">v</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="k">if</span> <span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="n">p</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">removed</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">sub</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">total</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">findCentroid</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">total</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">u</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">decompose</span><span class="p">(</span><span class="kt">int</span> <span class="n">entry</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="kt">int</span> <span class="n">total</span> <span class="o">=</span> <span class="n">calcSize</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">c</span> <span class="o">=</span> <span class="n">findCentroid</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">total</span><span class="p">);</span>
    <span class="n">centroidParent</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="n">parent</span><span class="p">;</span>
    <span class="n">removed</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">v</span> <span class="o">:</span> <span class="n">graph</span><span class="p">[</span><span class="n">c</span><span class="p">])</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">removed</span><span class="p">[</span><span class="n">v</span><span class="p">])</span> <span class="n">decompose</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">removed</code> dizisi centroid olarak ayrılmış düğümlerin sonraki hesaplamalara katılmasını engeller. <code class="language-plaintext highlighter-rouge">centroidParent</code> ise yeni centroid ağacındaki ebeveyn ilişkisini saklar.</p>

<h2 id="en-yakın-işaretli-düğüm-uygulaması">En yakın işaretli düğüm uygulaması</h2>

<p>Başlangıçta bazı düğümlerin kırmızı olduğunu düşünelim. Bir düğümü kırmızıya boyamak ve verilen $u$ düğümüne en yakın kırmızının mesafesini bulmak istiyoruz. Her centroid için kendisine en yakın kırmızı düğümün mesafesini <code class="language-plaintext highlighter-rouge">best</code> dizisinde tutarız.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">paint</span><span class="p">(</span><span class="kt">int</span> <span class="n">u</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">u</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">x</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">best</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">best</span><span class="p">[</span><span class="n">x</span><span class="p">],</span> <span class="n">distance</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">x</span><span class="p">));</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">centroidParent</span><span class="p">[</span><span class="n">x</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">query</span><span class="p">(</span><span class="kt">int</span> <span class="n">u</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">answer</span> <span class="o">=</span> <span class="n">INF</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">u</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">x</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">answer</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">answer</span><span class="p">,</span> <span class="n">best</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">+</span> <span class="n">distance</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">x</span><span class="p">));</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">centroidParent</span><span class="p">[</span><span class="n">x</span><span class="p">];</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">answer</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Buradaki <code class="language-plaintext highlighter-rouge">distance(a, b)</code> değeri LCA ile $O(\log n)$ sürede veya centroid atalarına ait önceden saklanmış mesafelerle $O(1)$ sürede alınabilir. İkinci seçenek kullanıldığında güncelleme ve sorgu doğrudan $O(\log n)$ olur.</p>

<p>Temel fikir şudur: Her düğüm yalnızca $O(\log n)$ centroid atasına sahiptir. Olası cevapları bu dengeli ata zinciri üzerinden birleştiririz. Böylece kocaman ağacı her sorguda dolaşmak yerine birkaç stratejik ağırlık merkezine uğrarız; algoritmik anlamda tam bir “merkeze çekilme” hareketi!</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="centroid decomposition" /><category term="ağaç algoritmaları" /><category term="graf teorisi" /><category term="mesafe sorguları" /><category term="c++" /><category term="rekabetçi programlama" /><summary type="html"><![CDATA[Bir ağaçta iki düğüm arasındaki mesafeyi hesaplamak kolaydır; fakat binlerce güncelleme ve sorgu geldiğinde işler hızla dallanıp budaklanır. Centroid Decomposition, ağacı dengeli biçimde parçalara ayırarak mesafe problemlerini yaklaşık $O(\log n)$ katman üzerinden çözmemizi sağlar. Kısacası ağacın fiziksel yapısını değiştirmeden, onun üzerinde ikinci ve dengeli bir “centroid ağacı” kurarız.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/agac-ici-mesafelerin-68.png" /><media:content medium="image" url="https://program.sonsuz.us/img/agac-ici-mesafelerin-68.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Ağaçlarda Euler Turu Tekniği: Alt Ağaçları Tek Boyutlu Dizide Yakalamak</title><link href="https://program.sonsuz.us/posts/agaclarda-euler-turu-teknigi-alt-agaclari-tek-boyutlu-dizide-yakalamak/" rel="alternate" type="text/html" title="Ağaçlarda Euler Turu Tekniği: Alt Ağaçları Tek Boyutlu Dizide Yakalamak" /><published>2026-09-24T00:00:00+00:00</published><updated>2026-09-24T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/agaclarda-euler-turu-teknigi-alt-agaclari-tek-boyutlu-dizide-yakalamak</id><content type="html" xml:base="https://program.sonsuz.us/posts/agaclarda-euler-turu-teknigi-alt-agaclari-tek-boyutlu-dizide-yakalamak/"><![CDATA[<p><img src="/img/agaclarda-euler-turu-67.svg" alt="agaclarda-euler-turu-67" /></p>

<p>Bir şirket hiyerarşisindeki yöneticileri, dosya sistemindeki klasörleri veya bir oyundaki yetenek ağacını düşünün. Bu yapılarda “X düğümünün altındaki bütün değerlerin toplamı nedir?” gibi sorgular sıkça karşımıza çıkar. Ağacı her sorguda yeniden dolaşmak pahalıdır; Euler Turu Tekniği (ETT), alt ağaçları tek boyutlu ve kesintisiz dizi aralıklarına dönüştürerek segment ağacının süper güçlerinden yararlanmamızı sağlar.</p>

<p>``</p>

<h2 id="temel-fikir-ağacı-ütülemek">Temel fikir: Ağacı ütülemek</h2>

<p>Köklü bir ağaçta DFS dolaşması yaparken her düğüme giriş zamanını <code class="language-plaintext highlighter-rouge">tin[v]</code> olarak kaydedelim. Düğümün bütün çocuklarını ziyaret ettikten sonra da alt ağacının bittiği konumu <code class="language-plaintext highlighter-rouge">tout[v]</code> ile işaretleyelim.</p>

<p>DFS, bir düğüme girdikten sonra başka bir dala geçmeden önce o düğümün tüm torunlarını ziyaret eder. Dolayısıyla <code class="language-plaintext highlighter-rouge">v</code> düğümünün alt ağacındaki düğümler, oluşturduğumuz dizide şu kesintisiz aralığa yerleşir:</p>

\[subtree(v) \longleftrightarrow [tin[v],\ tout[v]]\]

<p>Düğüm değerlerini giriş sırasına göre <code class="language-plaintext highlighter-rouge">flat</code> dizisine yazarsak alt ağaç toplamı artık ağaç üzerinde değil, sıradan bir aralık üzerinde hesaplanır:</p>

\[S(v)=\sum_{i=tin[v]}^{tout[v]} flat[i]\]

<p>Örneğin DFS sırası <code class="language-plaintext highlighter-rouge">A, B, D, E, C, F</code> olsun. <code class="language-plaintext highlighter-rouge">B</code> düğümünün altında <code class="language-plaintext highlighter-rouge">B, D, E</code> bulunuyorsa bunlar dizide yan yana durur. Ağaç karmaşık görünse bile sorgu yalnızca <code class="language-plaintext highlighter-rouge">[1, 3]</code> aralığıdır. Kısacası ağaç ütülenmiş, kırışıklıklar kaybolmuştur!</p>

<table>
  <thead>
    <tr>
      <th>İşlem</th>
      <th style="text-align: right">Doğrudan DFS</th>
      <th style="text-align: right">ETT + Segment Ağacı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Ön işleme</td>
      <td style="text-align: right">Yok</td>
      <td style="text-align: right">$O(n)$</td>
    </tr>
    <tr>
      <td>Alt ağaç toplamı</td>
      <td style="text-align: right">$O(k)$</td>
      <td style="text-align: right">$O(\log n)$</td>
    </tr>
    <tr>
      <td>Tek düğüm güncelleme</td>
      <td style="text-align: right">$O(1)$</td>
      <td style="text-align: right">$O(\log n)$</td>
    </tr>
    <tr>
      <td>Bellek</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(n)$</td>
    </tr>
  </tbody>
</table>

<p>Buradaki $k$, sorgulanan alt ağaçtaki düğüm sayısıdır. Ağaç zincir şeklindeyse $k$, $n$ kadar büyüyebilir.</p>

<h2 id="euler-dizisini-oluşturmak">Euler dizisini oluşturmak</h2>

<p>Aşağıdaki Python kodu, her düğümü yalnızca giriş anında diziye ekleyen yaygın ETT çeşidini uygular:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">build_euler</span><span class="p">(</span><span class="n">graph</span><span class="p">,</span> <span class="n">values</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
    <span class="n">n</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">graph</span><span class="p">)</span>
    <span class="n">tin</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">n</span>
    <span class="n">tout</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">n</span>
    <span class="n">flat</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">def</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">parent</span><span class="p">):</span>
        <span class="n">tin</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">flat</span><span class="p">)</span>
        <span class="n">flat</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">values</span><span class="p">[</span><span class="n">node</span><span class="p">])</span>

        <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">graph</span><span class="p">[</span><span class="n">node</span><span class="p">]:</span>
            <span class="k">if</span> <span class="n">child</span> <span class="o">!=</span> <span class="n">parent</span><span class="p">:</span>
                <span class="nf">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="n">tout</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">flat</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>

    <span class="nf">dfs</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">tin</span><span class="p">,</span> <span class="n">tout</span><span class="p">,</span> <span class="n">flat</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">tin[node]</code>, düğümün değerinin <code class="language-plaintext highlighter-rouge">flat</code> içindeki konumudur. <code class="language-plaintext highlighter-rouge">tout[node]</code> ise son torunun konumunu gösterir. Böylece alt ağaç aralığı iki indeksle temsil edilir.</p>

<h2 id="segment-ağacıyla-sorgulamak">Segment ağacıyla sorgulamak</h2>

<p>Aşağıdaki sınıf noktasal güncelleme ve aralık toplamı işlemlerini $O(\log n)$ zamanda gerçekleştirir:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">SegmentTree</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
        <span class="n">size</span> <span class="o">=</span> <span class="mi">1</span>
        <span class="k">while</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
            <span class="n">size</span> <span class="o">*=</span> <span class="mi">2</span>
        <span class="n">self</span><span class="p">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">size</span>
        <span class="n">self</span><span class="p">.</span><span class="n">tree</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">size</span><span class="p">)</span>

        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
            <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="n">size</span> <span class="o">+</span> <span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
            <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>

    <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="n">i</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">index</span>
        <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
        <span class="k">while</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">i</span> <span class="o">//=</span> <span class="mi">2</span>
            <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>

    <span class="k">def</span> <span class="nf">query</span><span class="p">(</span><span class="n">self</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">left</span> <span class="o">+=</span> <span class="n">self</span><span class="p">.</span><span class="n">size</span>
        <span class="n">right</span> <span class="o">+=</span> <span class="n">self</span><span class="p">.</span><span class="n">size</span>
        <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">while</span> <span class="n">left</span> <span class="o">&lt;=</span> <span class="n">right</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">left</span> <span class="o">%</span> <span class="mi">2</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">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="n">left</span><span class="p">]</span>
                <span class="n">left</span> <span class="o">+=</span> <span class="mi">1</span>
            <span class="k">if</span> <span class="n">right</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">total</span> <span class="o">+=</span> <span class="n">self</span><span class="p">.</span><span class="n">tree</span><span class="p">[</span><span class="n">right</span><span class="p">]</span>
                <span class="n">right</span> <span class="o">-=</span> <span class="mi">1</span>
            <span class="n">left</span> <span class="o">//=</span> <span class="mi">2</span>
            <span class="n">right</span> <span class="o">//=</span> <span class="mi">2</span>
        <span class="k">return</span> <span class="n">total</span>
</code></pre></div></div>

<p>Kullanımı oldukça nettir:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tin</span><span class="p">,</span> <span class="n">tout</span><span class="p">,</span> <span class="n">flat</span> <span class="o">=</span> <span class="nf">build_euler</span><span class="p">(</span><span class="n">graph</span><span class="p">,</span> <span class="n">values</span><span class="p">)</span>
<span class="n">segment</span> <span class="o">=</span> <span class="nc">SegmentTree</span><span class="p">(</span><span class="n">flat</span><span class="p">)</span>

<span class="n">subtree_sum</span> <span class="o">=</span> <span class="n">segment</span><span class="p">.</span><span class="nf">query</span><span class="p">(</span><span class="n">tin</span><span class="p">[</span><span class="n">v</span><span class="p">],</span> <span class="n">tout</span><span class="p">[</span><span class="n">v</span><span class="p">])</span>
<span class="n">segment</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="n">tin</span><span class="p">[</span><span class="n">v</span><span class="p">],</span> <span class="n">new_value</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="i̇nce-noktalar">İnce noktalar</h2>

<p>ETT’nin farklı tanımları vardır. Bazı sürümlerde düğüm hem girişte hem çıkışta kaydedilir. Alt ağaç sorgularında genellikle her düğümü bir kez yazan sürüm daha pratiktir. Aralıkların kapalı mı <code class="language-plaintext highlighter-rouge">[l, r]</code>, yoksa yarı açık mı <code class="language-plaintext highlighter-rouge">[l, r)</code> olduğunu da baştan belirlemek önemlidir; indeks hatalarının doğal yaşam alanı tam olarak burasıdır.</p>

<p>Ağaç değişmiyor fakat değerler güncelleniyorsa ETT mükemmel bir seçimdir. Yalnızca toplam gerekiyorsa Fenwick ağacı da kullanılabilir. Minimum, maksimum veya daha gelişmiş birleştirme işlemlerinde ise segment ağacı daha esnektir. Sonuç olarak ETT tek başına sorguları cevaplamaz; ağacın geometrisini, güçlü dizi veri yapılarına uygun bir forma dönüştüren zekice bir köprüdür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="algoritma" /><category term="euler-turu" /><category term="ağaçlar" /><category term="segment-ağacı" /><category term="veri-yapıları" /><category term="rekabetçi-programlama" /><summary type="html"><![CDATA[Bir şirket hiyerarşisindeki yöneticileri, dosya sistemindeki klasörleri veya bir oyundaki yetenek ağacını düşünün. Bu yapılarda “X düğümünün altındaki bütün değerlerin toplamı nedir?” gibi sorgular sıkça karşımıza çıkar. Ağacı her sorguda yeniden dolaşmak pahalıdır; Euler Turu Tekniği (ETT), alt ağaçları tek boyutlu ve kesintisiz dizi aralıklarına dönüştürerek segment ağacının süper güçlerinden yararlanmamızı sağlar.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/agaclarda-euler-turu-67.png" /><media:content medium="image" url="https://program.sonsuz.us/img/agaclarda-euler-turu-67.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Aho-Corasick ve Dinamik Programlama ile Yasaklı Kelimelerden Kaçınan Metin Üretimi</title><link href="https://program.sonsuz.us/posts/aho-corasick-ve-dinamik-programlama-ile-yasakli-kelimelerden-kacinan-metin-uretimi/" rel="alternate" type="text/html" title="Aho-Corasick ve Dinamik Programlama ile Yasaklı Kelimelerden Kaçınan Metin Üretimi" /><published>2026-09-24T00:00:00+00:00</published><updated>2026-09-24T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/aho-corasick-ve-dinamik-programlama-ile-yasakli-kelimelerden-kacinan-metin-uretimi</id><content type="html" xml:base="https://program.sonsuz.us/posts/aho-corasick-ve-dinamik-programlama-ile-yasakli-kelimelerden-kacinan-metin-uretimi/"><![CDATA[<p><img src="/img/aho-corasick-ve-37.svg" alt="aho-corasick-ve-37" /></p>

<p>Bir alfabenin karakterlerini kullanarak belirli uzunlukta metinler üretmek kolaydır: alfabe boyutu $K$, metin uzunluğu $N$ ise toplam $K^N$ seçenek vardır. Ancak metnin içinde “abc”, “kedi” veya “virus” gibi yasaklı kelimelerin hiç geçmemesini istediğimizde işler karışır. Her dizgiyi tek tek üretip kontrol etmek astronomik derecede pahalıdır. Neyse ki Aho-Corasick otomatı ile dinamik programlamayı birleştirerek tüm geçerli dizgileri üretmeden sayabiliriz.</p>

<p>``</p>

<h2 id="problemi-neden-bir-otomat-olarak-düşünmeliyiz">Problemi neden bir otomat olarak düşünmeliyiz?</h2>

<p>Yasaklı kelimeleri kontrol ederken geçmişin tamamını hatırlamamız gerekmez. Yalnızca şimdiye kadar yazdığımız metnin, yasaklı kelimelerden birinin öneki olabilecek en uzun son ekini bilmek yeterlidir.</p>

<p>Örneğin yasaklı kelimeler <code class="language-plaintext highlighter-rouge">aba</code> ve <code class="language-plaintext highlighter-rouge">bab</code> olsun. Üretilen metin <code class="language-plaintext highlighter-rouge">...ab</code> ile bitiyorsa yeni karakter önemlidir: <code class="language-plaintext highlighter-rouge">a</code> eklenirse <code class="language-plaintext highlighter-rouge">aba</code> tamamlanır ve durum geçersiz olur. Aho-Corasick, bu tür bilgileri trie düğümleri ve failure bağlantılarıyla temsil eder.</p>

<table>
  <thead>
    <tr>
      <th>Yapı</th>
      <th>Görevi</th>
      <th>Sağladığı avantaj</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Trie kenarı</td>
      <td>Bir karakterle sonraki öneke geçer</td>
      <td>Kelimelerin ortak öneklerini birleştirir</td>
    </tr>
    <tr>
      <td>Failure bağlantısı</td>
      <td>Eşleşme bozulunca uygun son eke döner</td>
      <td>Baştan aramayı önler</td>
    </tr>
    <tr>
      <td>Terminal işareti</td>
      <td>Yasaklı kelimenin tamamlandığını belirtir</td>
      <td>Geçersiz durumları tanır</td>
    </tr>
    <tr>
      <td>Otomat durumu</td>
      <td>Metnin ilgili son ekini saklar</td>
      <td>DP için kompakt hafıza oluşturur</td>
    </tr>
  </tbody>
</table>

<p>Bir düğüm terminalse veya failure zincirinde terminal bir düğüme ulaşıyorsa o durum “tehlikeli” kabul edilmelidir. Çünkü yasaklı kelime metnin sonunda zaten görülmüştür.</p>

<h2 id="dinamik-programlama-modeli">Dinamik programlama modeli</h2>

<p>$dp[i][s]$, uzunluğu $i$ olan ve Aho-Corasick otomatının $s$ durumunda biten geçerli dizgilerin sayısı olsun. Başlangıçta boş dizgi kök durumundadır:</p>

\[dp[0][0] = 1\]

<p>Her durumdan alfabedeki tüm karakterleri deneriz. Otomatın geçiş fonksiyonu $go(s,c)$ ile gösterilirse, hedef durum tehlikeli olmadığı sürece geçiş şöyledir:</p>

\[dp[i+1][go(s,c)] += dp[i][s]\]

<p>Sonuç, $N$ karakter işlendiğinde güvenli durumların toplamıdır:</p>

\[C_N = \sum_{s \in Safe} dp[N][s]\]

<p>Bu yaklaşımın zaman karmaşıklığı $O(N \cdot S \cdot K)$ olur. Burada $S$ otomat durumlarının, $K$ ise alfabenin büyüklüğüdür. Kaba kuvvetin $O(K^N)$ maliyetiyle karşılaştırıldığında fark oldukça dramatiktir.</p>

<h2 id="c-ile-örnek-dp">C++ ile örnek DP</h2>

<p>Aşağıdaki bölüm, otomatın daha önce oluşturulduğunu varsayar. <code class="language-plaintext highlighter-rouge">nextState[s][c]</code> geçişleri, <code class="language-plaintext highlighter-rouge">bad[s]</code> ise yasaklı bir eşleşmeye ulaşan durumları tutar:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">MOD</span> <span class="o">=</span> <span class="mi">1'000'000'007</span><span class="p">;</span>

<span class="kt">long</span> <span class="kt">long</span> <span class="nf">countValid</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int</span> <span class="n">stateCount</span><span class="p">,</span> <span class="kt">int</span> <span class="n">alphabetSize</span><span class="p">,</span>
                     <span class="k">const</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;&amp;</span> <span class="n">nextState</span><span class="p">,</span>
                     <span class="k">const</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;&amp;</span> <span class="n">bad</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;</span> <span class="n">current</span><span class="p">(</span><span class="n">stateCount</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">next</span><span class="p">(</span><span class="n">stateCount</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">current</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// Boş metin kök durumunda başlar.</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">length</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">length</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fill</span><span class="p">(</span><span class="n">next</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">next</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">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">state</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">state</span> <span class="o">&lt;</span> <span class="n">stateCount</span><span class="p">;</span> <span class="o">++</span><span class="n">state</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">bad</span><span class="p">[</span><span class="n">state</span><span class="p">]</span> <span class="o">||</span> <span class="n">current</span><span class="p">[</span><span class="n">state</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">ch</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">ch</span> <span class="o">&lt;</span> <span class="n">alphabetSize</span><span class="p">;</span> <span class="o">++</span><span class="n">ch</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">int</span> <span class="n">target</span> <span class="o">=</span> <span class="n">nextState</span><span class="p">[</span><span class="n">state</span><span class="p">][</span><span class="n">ch</span><span class="p">];</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">bad</span><span class="p">[</span><span class="n">target</span><span class="p">])</span> <span class="k">continue</span><span class="p">;</span>

                <span class="n">next</span><span class="p">[</span><span class="n">target</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">next</span><span class="p">[</span><span class="n">target</span><span class="p">]</span> <span class="o">+</span> <span class="n">current</span><span class="p">[</span><span class="n">state</span><span class="p">])</span> <span class="o">%</span> <span class="n">MOD</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">current</span><span class="p">.</span><span class="n">swap</span><span class="p">(</span><span class="n">next</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">long</span> <span class="kt">long</span> <span class="n">answer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">state</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">state</span> <span class="o">&lt;</span> <span class="n">stateCount</span><span class="p">;</span> <span class="o">++</span><span class="n">state</span><span class="p">)</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">bad</span><span class="p">[</span><span class="n">state</span><span class="p">])</span> <span class="n">answer</span> <span class="o">=</span> <span class="p">(</span><span class="n">answer</span> <span class="o">+</span> <span class="n">current</span><span class="p">[</span><span class="n">state</span><span class="p">])</span> <span class="o">%</span> <span class="n">MOD</span><span class="p">;</span>

    <span class="k">return</span> <span class="n">answer</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Kodda yalnızca önceki uzunluğun değerleri gerektiği için iki DP satırı kullanılır. Böylece bellek karmaşıklığı $O(N \cdot S)$ yerine $O(S)$ seviyesine iner.</p>

<h2 id="dikkat-edilmesi-gereken-ayrıntılar">Dikkat edilmesi gereken ayrıntılar</h2>

<p>Failure bağlantısından terminal bilgisini miras almak kritik önemdedir. Aksi hâlde otomat, daha uzun bir durumun son eki olarak tamamlanan yasaklı kelimeleri kaçırabilir. Ayrıca sonuçlar hızla büyüdüğünden çoğu yarışma probleminde modüler aritmetik kullanılır.</p>

<p>Çok büyük $N$ değerlerinde aynı model bir geçiş matrisi hâline getirilebilir ve matris hızlı üs alma ile yaklaşık $O(S^3 \log N)$ sürede çözülebilir. Böylece Aho-Corasick yalnızca kelime arayan bir algoritma olmaktan çıkar; güvenli metinlerin evrenini sayan güçlü bir kombinatorik makineye dönüşür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="aho-corasick" /><category term="dinamik programlama" /><category term="otomat" /><category term="algoritma" /><category term="metin işleme" /><category term="kombinatorik" /><summary type="html"><![CDATA[Bir alfabenin karakterlerini kullanarak belirli uzunlukta metinler üretmek kolaydır: alfabe boyutu $K$, metin uzunluğu $N$ ise toplam $K^N$ seçenek vardır. Ancak metnin içinde “abc”, “kedi” veya “virus” gibi yasaklı kelimelerin hiç geçmemesini istediğimizde işler karışır. Her dizgiyi tek tek üretip kontrol etmek astronomik derecede pahalıdır. Neyse ki Aho-Corasick otomatı ile dinamik programlamayı birleştirerek tüm geçerli dizgileri üretmeden sayabiliriz.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/aho-corasick-ve-37.png" /><media:content medium="image" url="https://program.sonsuz.us/img/aho-corasick-ve-37.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Brian Kernighan Algoritmasıyla Set Bitlerini Şimşek Hızında Saymak</title><link href="https://program.sonsuz.us/posts/brian-kernighan-algoritmasiyla-set-bitlerini-simsek-hizinda-saymak/" rel="alternate" type="text/html" title="Brian Kernighan Algoritmasıyla Set Bitlerini Şimşek Hızında Saymak" /><published>2026-09-24T00:00:00+00:00</published><updated>2026-09-24T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/brian-kernighan-algoritmasiyla-set-bitlerini-simsek-hizinda-saymak</id><content type="html" xml:base="https://program.sonsuz.us/posts/brian-kernighan-algoritmasiyla-set-bitlerini-simsek-hizinda-saymak/"><![CDATA[<p>Bir tam sayının ikili gösteriminde kaç tane <code class="language-plaintext highlighter-rouge">1</code> bulunduğunu saymak ilk bakışta sıradan bir döngü problemi gibi görünür. Ancak düşük seviyeli programlamanın büyüsü, bazen bütün bitleri tek tek dolaşmak yerine yalnızca ilgilendiğimiz bitlere dokunabilmemizdir. Brian Kernighan algoritması, her turda sağdaki bir adet set bitini temizleyerek gereksiz döngüleri ortadan kaldıran zarif bir bit maskeleme hilesidir.
``</p>
<h2 id="önce-problemi-ikili-düşünelim">Önce problemi ikili düşünelim</h2>

<p>Bir bitin değeri <code class="language-plaintext highlighter-rouge">1</code> ise o bit <strong>set edilmiş</strong>, <code class="language-plaintext highlighter-rouge">0</code> ise temizlenmiş kabul edilir. Örneğin $n=52$ sayısının ikili gösterimi şöyledir:</p>

\[52 = (110100)_2\]

<p>Bu gösterimde üç tane <code class="language-plaintext highlighter-rouge">1</code> bulunduğundan sonuç 3’tür. Klasik yaklaşım, sayının en sağındaki biti <code class="language-plaintext highlighter-rouge">n &amp; 1</code> ile kontrol eder ve ardından sayıyı sağa kaydırır. Sayı $b$ bit uzunluğundaysa bu yöntem yaklaşık $b$ tur çalışır.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Her turdaki işlem</th>
      <th style="text-align: right">Döngü sayısı</th>
      <th style="text-align: right">Karmaşıklık</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Sağa kaydırma</td>
      <td>Her biti kontrol eder</td>
      <td style="text-align: right">Bit uzunluğu kadar</td>
      <td style="text-align: right">$O(b)$</td>
    </tr>
    <tr>
      <td>Brian Kernighan</td>
      <td>Yalnızca bir set bitini siler</td>
      <td style="text-align: right">Set bit sayısı kadar</td>
      <td style="text-align: right">$O(k)$</td>
    </tr>
    <tr>
      <td>Donanımsal <code class="language-plaintext highlighter-rouge">popcount</code></td>
      <td>İşlemci komutu kullanır</td>
      <td style="text-align: right">Mimariye bağlı</td>
      <td style="text-align: right">Genellikle sabit</td>
    </tr>
  </tbody>
</table>

<p><img src="/img/brian-kernighan-algoritmasiyla-11.svg" alt="brian-kernighan-algoritmasiyla-11" /></p>

<p>Burada $k$, sayının içindeki <code class="language-plaintext highlighter-rouge">1</code> bitlerinin sayısıdır. Seyrek bitli sayılarda $k \ll b$ olduğundan fark oldukça belirgin olabilir.</p>

<h2 id="hilenin-kalbi-n--n---1">Hilenin kalbi: <code class="language-plaintext highlighter-rouge">n &amp; (n - 1)</code></h2>

<p>Algoritmanın temel ifadesi şudur:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>n = n &amp; (n - 1)
</code></pre></div></div>

<p>Bir sayıdan 1 çıkarıldığında, en sağdaki set bit <code class="language-plaintext highlighter-rouge">0</code> olur; onun sağındaki bütün <code class="language-plaintext highlighter-rouge">0</code> bitleri ise <code class="language-plaintext highlighter-rouge">1</code> olur. Ardından eski sayı ile yeni sayı arasında AND işlemi yapıldığında bu değişen alt bitler temizlenir. Sonuç olarak sayının en sağındaki tek bir <code class="language-plaintext highlighter-rouge">1</code> yok edilir.</p>

<p>Örneğin $52$ üzerinden ilerleyelim:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">Tur</th>
      <th style="text-align: right"><code class="language-plaintext highlighter-rouge">n</code> değeri</th>
      <th>İkili gösterim</th>
      <th>Temizlenen bit sonrası</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">1</td>
      <td style="text-align: right">52</td>
      <td><code class="language-plaintext highlighter-rouge">110100</code></td>
      <td><code class="language-plaintext highlighter-rouge">110000</code></td>
    </tr>
    <tr>
      <td style="text-align: right">2</td>
      <td style="text-align: right">48</td>
      <td><code class="language-plaintext highlighter-rouge">110000</code></td>
      <td><code class="language-plaintext highlighter-rouge">100000</code></td>
    </tr>
    <tr>
      <td style="text-align: right">3</td>
      <td style="text-align: right">32</td>
      <td><code class="language-plaintext highlighter-rouge">100000</code></td>
      <td><code class="language-plaintext highlighter-rouge">000000</code></td>
    </tr>
  </tbody>
</table>

<p>Sayı sıfıra üç turda ulaştığı için set bit sayısı da 3’tür. Algoritma, aradaki sıfırlarla hiç vakit kaybetmez.</p>

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

<p>Aşağıdaki fonksiyon, işaretsiz bir tam sayıdaki set bitlerini sayar:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">unsigned</span> <span class="kt">int</span> <span class="nf">count_set_bits</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">n</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">n</span> <span class="o">&amp;=</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>  <span class="c1">// En sağdaki set bitini temizler.</span>
        <span class="n">count</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">count</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">unsigned int</code> kullanılması önemlidir. İşaretli negatif sayıların gösterimi ve taşma davranışları dile göre kafa karıştırabilir. İşaretsiz tür, işlemin sabit genişlikte bir bit deseni üzerinde gerçekleştiğini daha açık biçimde ifade eder.</p>

<p>Python’da aynı fikir neredeyse birebir yazılabilir:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">count_set_bits</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</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">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">"</span><span class="s">n negatif olmamalıdır</span><span class="sh">"</span><span class="p">)</span>

    <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">while</span> <span class="n">n</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">&amp;=</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">count</span>
</code></pre></div></div>

<h2 id="ne-zaman-tercih-edilmeli">Ne zaman tercih edilmeli?</h2>

<p>Bu yöntem; bit bayraklarını analiz ederken, izin sistemlerinde etkin seçenekleri sayarken, grafik algoritmalarındaki bit kümelerinde ve gömülü sistemlerde oldukça kullanışlıdır. Yine de modern dillerin <code class="language-plaintext highlighter-rouge">bit_count</code>, <code class="language-plaintext highlighter-rouge">popcount</code> veya benzeri yerleşik fonksiyonları işlemcinin özel komutlarından yararlanabilir. Python’da örneğin <code class="language-plaintext highlighter-rouge">n.bit_count()</code> çoğunlukla elle yazılan döngüden daha hızlıdır.</p>

<p>Brian Kernighan yaklaşımının asıl değeri yalnızca performans değildir. <code class="language-plaintext highlighter-rouge">n &amp; (n - 1)</code> ifadesi, bitlerin matematiksel yapısını kullanarak problemi dönüştürür: bütün bitleri aramak yerine bulunan her <code class="language-plaintext highlighter-rouge">1</code> doğrudan silinir. Kısacası algoritma samanlıkta iğne aramaz; iğneleri sırayla ortadan kaldırır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="bit-maskeleme" /><category term="brian-kernighan" /><category term="algoritma" /><category term="popcount" /><category term="düşük-seviye-programlama" /><summary type="html"><![CDATA[Bir tam sayının ikili gösteriminde kaç tane 1 bulunduğunu saymak ilk bakışta sıradan bir döngü problemi gibi görünür. Ancak düşük seviyeli programlamanın büyüsü, bazen bütün bitleri tek tek dolaşmak yerine yalnızca ilgilendiğimiz bitlere dokunabilmemizdir. Brian Kernighan algoritması, her turda sağdaki bir adet set bitini temizleyerek gereksiz döngüleri ortadan kaldıran zarif bir bit maskeleme hilesidir.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/brian-kernighan-algoritmasiyla-11.png" /><media:content medium="image" url="https://program.sonsuz.us/img/brian-kernighan-algoritmasiyla-11.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Circuit Breaker Pattern: Çöken Servisin Sistemi Peşinden Sürüklemesini Önlemek</title><link href="https://program.sonsuz.us/posts/circuit-breaker-pattern-coken-servisin-sistemi-pesinden-suruklemesini-onlemek/" rel="alternate" type="text/html" title="Circuit Breaker Pattern: Çöken Servisin Sistemi Peşinden Sürüklemesini Önlemek" /><published>2026-09-24T00:00:00+00:00</published><updated>2026-09-24T00:00:00+00:00</updated><id>https://program.sonsuz.us/posts/circuit-breaker-pattern-coken-servisin-sistemi-pesinden-suruklemesini-onlemek</id><content type="html" xml:base="https://program.sonsuz.us/posts/circuit-breaker-pattern-coken-servisin-sistemi-pesinden-suruklemesini-onlemek/"><![CDATA[<p>Bir mikroservis çöktüğünde sorun çoğu zaman yalnızca o servisle sınırlı kalmaz. Ona istek gönderen uygulamalar yanıt bekler, bağlantı havuzları dolar, iş parçacıkları tükenir ve masum servisler de domino taşları gibi devrilmeye başlar. <strong>Circuit Breaker Pattern</strong>, elektrik sigortasına benzeyen bir koruma mekanizması kurarak bu zincirleme felaketi durdurur.
``</p>
<h2 id="circuit-breaker-neyi-çözer">Circuit Breaker neyi çözer?</h2>

<p>Bir sipariş servisinin ödeme servisini çağırdığını düşünelim. Ödeme servisi yanıt veremiyorsa her isteği tekrar tekrar ona göndermek problemi çözmez; aksine ağ trafiğini ve kaynak tüketimini artırır. Circuit Breaker, belirli sayıdaki başarısızlıktan sonra çağrıları geçici olarak engeller ve hızlı hata döndürür.</p>

<p>Bir çağrının ortalama bekleme süresi $T$, eş zamanlı istek sayısı $N$ ise bloke edilen toplam kaynak baskısını kabaca şöyle düşünebiliriz:</p>

\[Yuk = N \times T\]

<p>Örneğin 500 istek 10 saniye boyunca beklerse sistemde $5000$ istek-saniyelik baskı oluşur. Devre açıkken çağrının 10 milisaniyede reddedilmesi bu maliyeti dramatik biçimde azaltır.</p>

<h2 id="üç-temel-durum">Üç temel durum</h2>

<p>Circuit Breaker küçük bir durum makinesi olarak çalışır:</p>

<table>
  <thead>
    <tr>
      <th>Durum</th>
      <th>Davranış</th>
      <th>Geçiş koşulu</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Closed</strong></td>
      <td>İstekler hedef servise gider</td>
      <td>Hata eşiği aşılırsa Open</td>
    </tr>
    <tr>
      <td><strong>Open</strong></td>
      <td>İstekler gönderilmeden reddedilir</td>
      <td>Bekleme süresi dolunca Half-Open</td>
    </tr>
    <tr>
      <td><strong>Half-Open</strong></td>
      <td>Sınırlı sayıda deneme yapılır</td>
      <td>Başarılıysa Closed, hatalıysa Open</td>
    </tr>
  </tbody>
</table>

<p><img src="/img/circuit-breaker-pattern-70.svg" alt="circuit-breaker-pattern-70" /></p>

<p>İsimler ilk bakışta ters gelebilir. Elektrik devresinde <strong>closed</strong>, akımın geçtiği; <strong>open</strong> ise akımın kesildiği durumdur. Burada “akım”, servis çağrılarıdır.</p>

<p>Hata oranı $E$, başarısız çağrı sayısı $F$ ve toplam çağrı sayısı $R$ ile hesaplanabilir:</p>

\[E = \frac{F}{R} \times 100\]

<p>Son 20 çağrının 12’si başarısızsa hata oranı yüzde 60’tır. Eşik yüzde 50 olarak ayarlanmışsa devre açılabilir. Ancak yalnızca oran kullanmak risklidir: İki çağrıdan birinin başarısız olması da yüzde 50 eder. Bu nedenle minimum örnek sayısı belirlemek önemlidir.</p>

<h2 id="javascript-ile-sade-bir-uygulama">JavaScript ile sade bir uygulama</h2>

<p>Aşağıdaki sınıf, art arda gelen hataları sayar; eşik aşılınca çağrıları durdurur ve belirlenen sürenin ardından kontrollü bir denemeye izin verir:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">CircuitBreaker</span> <span class="p">{</span>
  <span class="nf">constructor</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">threshold</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span> <span class="nx">resetTime</span> <span class="o">=</span> <span class="mi">5000</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">action</span> <span class="o">=</span> <span class="nx">action</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">threshold</span> <span class="o">=</span> <span class="nx">threshold</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">resetTime</span> <span class="o">=</span> <span class="nx">resetTime</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">failures</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">CLOSED</span><span class="dl">"</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">openedAt</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">async</span> <span class="nf">execute</span><span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if </span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">OPEN</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">elapsed</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span> <span class="o">-</span> <span class="k">this</span><span class="p">.</span><span class="nx">openedAt</span><span class="p">;</span>
      <span class="k">if </span><span class="p">(</span><span class="nx">elapsed</span> <span class="o">&lt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">resetTime</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Devre açık: çağrı engellendi</span><span class="dl">"</span><span class="p">);</span>
      <span class="p">}</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">HALF_OPEN</span><span class="dl">"</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">try</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nf">action</span><span class="p">(...</span><span class="nx">args</span><span class="p">);</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">failures</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">CLOSED</span><span class="dl">"</span><span class="p">;</span>
      <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">failures</span><span class="o">++</span><span class="p">;</span>
      <span class="k">if </span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">HALF_OPEN</span><span class="dl">"</span> <span class="o">||</span> <span class="k">this</span><span class="p">.</span><span class="nx">failures</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">threshold</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">OPEN</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">openedAt</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nf">now</span><span class="p">();</span>
      <span class="p">}</span>
      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Sınıf gerçek projelerde zaman aşımı, kayan pencere, eş zamanlı Half-Open denemelerini sınırlama ve metrik toplama gibi özelliklerle genişletilmelidir. Hazır çözümler için Java ekosisteminde Resilience4j, .NET tarafında Polly kullanılabilir.</p>

<h2 id="retry-ile-aynı-şey-mi">Retry ile aynı şey mi?</h2>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Temel amaç</th>
      <th>Hatalı kullanım riski</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Retry</td>
      <td>Geçici hatada çağrıyı yinelemek</td>
      <td>Çöken servise daha fazla yük bindirmek</td>
    </tr>
    <tr>
      <td>Timeout</td>
      <td>Sonsuz beklemeyi engellemek</td>
      <td>Çok kısa sürede geçerli çağrıları kesmek</td>
    </tr>
    <tr>
      <td>Circuit Breaker</td>
      <td>Arızalı hedefe trafiği durdurmak</td>
      <td>Yanlış eşikle devreyi gereksiz açmak</td>
    </tr>
    <tr>
      <td>Fallback</td>
      <td>Alternatif yanıt üretmek</td>
      <td>Eski veya eksik veri sunmak</td>
    </tr>
  </tbody>
</table>

<p>Bu desenler rakip değil, ekip arkadaşıdır. İyi bir akışta önce timeout uygulanır, yalnızca geçici hatalar dikkatli biçimde retry edilir, hata yoğunluğu artarsa devre açılır ve mümkünse önbellekten fallback yanıtı sunulur.</p>

<h2 id="sonuç">Sonuç</h2>

<p>Circuit Breaker bozuk servisi tamir etmez; sistemin geri kalanına nefes alacak alan kazandırır. Başarı için eşikleri trafik karakterine göre ayarlamak, devre durumlarını izlemek ve alarm üretmek gerekir. Çünkü dağıtık sistemlerde soru “Bir servis çöker mi?” değil, “Çöktüğünde sistem ne kadar zarif davranır?” olmalıdır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="circuit breaker" /><category term="mikroservis" /><category term="dağıtık sistemler" /><category term="dayanıklılık" /><category term="hata yönetimi" /><category term="javascript" /><summary type="html"><![CDATA[Bir mikroservis çöktüğünde sorun çoğu zaman yalnızca o servisle sınırlı kalmaz. Ona istek gönderen uygulamalar yanıt bekler, bağlantı havuzları dolar, iş parçacıkları tükenir ve masum servisler de domino taşları gibi devrilmeye başlar. Circuit Breaker Pattern, elektrik sigortasına benzeyen bir koruma mekanizması kurarak bu zincirleme felaketi durdurur.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://program.sonsuz.us/img/circuit-breaker-pattern-70.png" /><media:content medium="image" url="https://program.sonsuz.us/img/circuit-breaker-pattern-70.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>