<?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://sonsuzus.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sonsuzus.github.io/" rel="alternate" type="text/html" /><updated>2026-08-05T10:13:21+00:00</updated><id>https://sonsuzus.github.io/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">Açgözlü Algoritmalar: Ne Zaman En İyiyi Bulurlar?</title><link href="https://sonsuzus.github.io/posts/acgozlu-algoritmalar-ne-zaman-en-iyiyi-bulurlar/" rel="alternate" type="text/html" title="Açgözlü Algoritmalar: Ne Zaman En İyiyi Bulurlar?" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/acgozlu-algoritmalar-ne-zaman-en-iyiyi-bulurlar</id><content type="html" xml:base="https://sonsuzus.github.io/posts/acgozlu-algoritmalar-ne-zaman-en-iyiyi-bulurlar/"><![CDATA[<p>Açgözlü algoritmalar, her adımda o an için en cazip seçeneği tercih eder. Geleceği ayrıntılı biçimde hesaplamaz, geçmiş kararlarını da değiştirmezler. Bu yaklaşım biraz “önce en güzel kurabiyeyi kap, gerisini sonra düşünürüz” tavrına benzer. Şaşırtıcı biçimde bazı problemlerde bu basit strateji gerçekten optimal sonucu verirken, bazılarında bizi kurabiye kırıntılarıyla baş başa bırakır.</p>

<p>``</p>

<h2 id="açgözlü-yaklaşımın-temel-mantığı">Açgözlü yaklaşımın temel mantığı</h2>

<p>Bir optimizasyon problemi, belirli kısıtlar altında bir amaç fonksiyonunu en küçük veya en büyük yapmayı hedefler. Açgözlü algoritma ise çözümü adım adım kurar ve her aşamada yerel olarak en iyi görünen kararı verir.</p>

<p>Bir çözüm $S$ ve seçilebilecek adaylar kümesi $C$ olsun. Algoritma her adımda yaklaşık olarak şu seçimi yapar:</p>

\[x^* = \arg\max_{x \in C} kazanç(x)\]

<p>Ancak yerel maksimumun küresel maksimuma eşit olması garanti değildir. Açgözlü yöntemin optimal olması için problemde genellikle iki önemli özellik aranır:</p>

<ol>
  <li><strong>Açgözlü seçim özelliği:</strong> Yerel olarak en iyi seçim, en az bir optimal çözümün parçası olmalıdır.</li>
  <li><strong>Optimal alt yapı:</strong> Optimal çözümün içinde kalan alt problemler de optimal biçimde çözülmelidir.</li>
</ol>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Sorduğumuz soru</th>
      <th>Anlamı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Açgözlü seçim</td>
      <td>İlk yerel seçim güvenli mi?</td>
      <td>Bu seçim optimal çözüme dönüştürülebilir.</td>
    </tr>
    <tr>
      <td>Optimal alt yapı</td>
      <td>Kalan problem bağımsız mı?</td>
      <td>Kalan kısmın optimal çözümü kullanılabilir.</td>
    </tr>
    <tr>
      <td>Geri dönmeme</td>
      <td>Karar değiştirilecek mi?</td>
      <td>Açgözlü algoritma seçimini kalıcı yapar.</td>
    </tr>
  </tbody>
</table>

<h2 id="optimal-olduğunu-nasıl-kanıtlarız">Optimal olduğunu nasıl kanıtlarız?</h2>

<p>En yaygın yöntemlerden biri <strong>değiş tokuş argümanıdır</strong>. Önce herhangi bir optimal çözüm düşünülür. Eğer bu çözüm açgözlü seçimi içermiyorsa, çözümdeki başka bir seçim açgözlü seçimle değiştirilir. Değişiklik çözümün geçerliliğini ve değerini bozmuyorsa, açgözlü seçimi içeren bir optimal çözüm bulunduğu kanıtlanır.</p>

<p>İkinci adımda problem küçültülür. İlk güvenli seçim yapıldıktan sonra kalan bölümün aynı yapıda bir alt problem olduğu gösterilir. Böylece tümevarım kullanılarak algoritmanın tamamının optimal olduğu sonucuna ulaşılır.</p>

<h2 id="klasik-örnek-aktivite-seçimi">Klasik örnek: Aktivite seçimi</h2>

<p>Her aktivitenin başlangıç zamanı $s_i$ ve bitiş zamanı $f_i$ olsun. Amaç, zamanları çakışmayan en fazla sayıda aktiviteyi seçmektir. Doğru açgözlü kural şudur: <strong>En erken biten uygun aktiviteyi seç.</strong></p>

<p>Neden en kısa süreni veya en erken başlayanı seçmiyoruz?</p>

<table>
  <thead>
    <tr>
      <th>Strateji</th>
      <th style="text-align: right">Optimal mi?</th>
      <th>Sorun</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>En erken biteni seç</td>
      <td style="text-align: right">Evet</td>
      <td>Gelecek aktiviteler için en fazla alanı bırakır.</td>
    </tr>
    <tr>
      <td>En erken başlayanı seç</td>
      <td style="text-align: right">Hayır</td>
      <td>Çok geç biten bir aktivite seçilebilir.</td>
    </tr>
    <tr>
      <td>En kısa süreliyi seç</td>
      <td style="text-align: right">Hayır</td>
      <td>Konumu kötü olduğu için iki aktiviteyi engelleyebilir.</td>
    </tr>
  </tbody>
</table>

<p>Örneğin aktiviteler $(1,4)$, $(3,5)$, $(0,6)$, $(5,7)$ ve $(8,9)$ ise önce $(1,4)$ seçilir. Ardından başlangıcı 4 veya daha büyük olanlar arasından $(5,7)$, sonra $(8,9)$ alınır. Sonuç üç aktivitedir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">aktivite_sec</span><span class="p">(</span><span class="n">aktiviteler</span><span class="p">):</span>
    <span class="c1"># Aktiviteleri bitiş zamanına göre sıralar.
</span>    <span class="n">aktiviteler</span><span class="p">.</span><span class="nf">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">aktivite</span><span class="p">:</span> <span class="n">aktivite</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>

    <span class="n">secilenler</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">son_bitis</span> <span class="o">=</span> <span class="nf">float</span><span class="p">(</span><span class="sh">"</span><span class="s">-inf</span><span class="sh">"</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">baslangic</span><span class="p">,</span> <span class="n">bitis</span> <span class="ow">in</span> <span class="n">aktiviteler</span><span class="p">:</span>
        <span class="c1"># Önceki aktivite bittiyse bu seçim güvenlidir.
</span>        <span class="k">if</span> <span class="n">baslangic</span> <span class="o">&gt;=</span> <span class="n">son_bitis</span><span class="p">:</span>
            <span class="n">secilenler</span><span class="p">.</span><span class="nf">append</span><span class="p">((</span><span class="n">baslangic</span><span class="p">,</span> <span class="n">bitis</span><span class="p">))</span>
            <span class="n">son_bitis</span> <span class="o">=</span> <span class="n">bitis</span>

    <span class="k">return</span> <span class="n">secilenler</span>
</code></pre></div></div>

<p>Sıralama $O(n \log n)$, tarama ise $O(n)$ sürdüğünden toplam karmaşıklık $O(n \log n)$ olur. Veriler zaten bitiş zamanına göre sıralıysa süre $O(n)$ seviyesine iner.</p>

<h2 id="açgözlülüğün-tökezlediği-yerler">Açgözlülüğün tökezlediği yerler</h2>

<p>Para üstü probleminde standart madeni paralarla en büyük değeri seçmek işe yarayabilir. Fakat para değerleri ${1,3,4}$ ve hedef $6$ olduğunda açgözlü yöntem $4+1+1$ ile üç para kullanır; optimal çözüm $3+3$ ile iki paradır. Demek ki hızlı ve sezgisel görünmek kanıt yerine geçmez.</p>

<p>Kısacası bir açgözlü algoritmaya güvenmeden önce güvenli seçimi tanımlayın, değiş tokuş argümanını kurun ve kalan problemin optimal alt yapıya sahip olduğunu gösterin. Bu üçlü sağlanıyorsa açgözlü yaklaşım yalnızca hızlı değil, matematiksel olarak da iştah açıcıdır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="açgözlü algoritmalar" /><category term="aktivite seçimi" /><category term="algoritma analizi" /><summary type="html"><![CDATA[Açgözlü algoritmalar, her adımda o an için en cazip seçeneği tercih eder. Geleceği ayrıntılı biçimde hesaplamaz, geçmiş kararlarını da değiştirmezler. Bu yaklaşım biraz “önce en güzel kurabiyeyi kap, gerisini sonra düşünürüz” tavrına benzer. Şaşırtıcı biçimde bazı problemlerde bu basit strateji gerçekten optimal sonucu verirken, bazılarında bizi kurabiye kırıntılarıyla baş başa bırakır.]]></summary></entry><entry><title type="html">Alt Dizilerin Dedektifliği: LIS, LCS ve Metin Benzerliği</title><link href="https://sonsuzus.github.io/posts/alt-dizilerin-dedektifligi-lis-lcs-ve-metin-benzerligi/" rel="alternate" type="text/html" title="Alt Dizilerin Dedektifliği: LIS, LCS ve Metin Benzerliği" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/alt-dizilerin-dedektifligi-lis-lcs-ve-metin-benzerligi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/alt-dizilerin-dedektifligi-lis-lcs-ve-metin-benzerligi/"><![CDATA[<p>Bir sayı dizisindeki yükselişleri bulmakla iki metnin ne kadar benzediğini ölçmek ilk bakışta ayrı dünyaların işi gibi görünebilir. Oysa En Uzun Artan Alt Dizi (LIS) ve En Uzun Ortak Alt Dizi (LCS), aynı temel sorunun akrabalarıdır: Sırayı bozmadan hangi elemanları seçebiliriz? Bu yaklaşım; sürüm karşılaştırma, intihal tespiti, DNA analizi ve yazım düzeltme gibi birçok alanda karşımıza çıkar.
``</p>

<h2 id="alt-dizi-tam-olarak-nedir">Alt dizi tam olarak nedir?</h2>

<p>Bir <strong>alt dizi (subsequence)</strong> oluştururken eleman silebiliriz ancak kalan elemanların sırasını değiştiremeyiz. Elemanların yan yana bulunması gerekmez. Buna karşılık <strong>alt metin veya alt dizi parçası (substring/subarray)</strong> kesintisiz olmalıdır.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th style="text-align: right">Elemanlar bitişik mi?</th>
      <th style="text-align: right">Sıra korunur mu?</th>
      <th>Örnek</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alt dizi</td>
      <td style="text-align: right">Hayır</td>
      <td style="text-align: right">Evet</td>
      <td><code class="language-plaintext highlighter-rouge">ABCDEF → ACE</code></td>
    </tr>
    <tr>
      <td>Alt metin</td>
      <td style="text-align: right">Evet</td>
      <td style="text-align: right">Evet</td>
      <td><code class="language-plaintext highlighter-rouge">ABCDEF → BCD</code></td>
    </tr>
    <tr>
      <td>Alt küme</td>
      <td style="text-align: right">Hayır</td>
      <td style="text-align: right">Hayır</td>
      <td><code class="language-plaintext highlighter-rouge">{A,B,C} → {C,A}</code></td>
    </tr>
  </tbody>
</table>

<p>Bu ayrım önemlidir; yanlış kavramı seçmek, çalışan ama bambaşka bir problemi çözen algoritmalar yazmamıza yol açar.</p>

<h2 id="en-uzun-artan-alt-dizi-lis">En Uzun Artan Alt Dizi (LIS)</h2>

<p>LIS, sayılar arasından sırası korunacak biçimde seçilebilen en uzun artan diziyi arar. Örneğin <code class="language-plaintext highlighter-rouge">[3, 1, 5, 2, 6, 4, 9]</code> için <code class="language-plaintext highlighter-rouge">[1, 2, 4, 9]</code> geçerli bir sonuçtur.</p>

<p>Klasik dinamik programlama yaklaşımında $dp[i]$, <code class="language-plaintext highlighter-rouge">i</code> konumunda biten en uzun artan alt dizinin uzunluğudur:</p>

\[dp[i] = 1 + \max(dp[j]) \quad \text{öyle ki } j&lt;i \text{ ve } a[j]&lt;a[i]\]

<p>Uygun bir $j$ yoksa $dp[i]=1$ olur. İki iç içe döngü nedeniyle zaman karmaşıklığı $O(n^2)$, bellek karmaşıklığı ise $O(n)$ seviyesindedir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">lis_length</span><span class="p">(</span><span class="n">numbers</span><span class="p">):</span>
    <span class="c1"># dp[i], i. elemanda biten en uzun artan alt diziyi tutar.
</span>    <span class="n">dp</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="nf">len</span><span class="p">(</span><span class="n">numbers</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="nf">len</span><span class="p">(</span><span class="n">numbers</span><span class="p">)):</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">i</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">numbers</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">numbers</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
                <span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">dp</span><span class="p">[</span><span class="n">j</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="nf">max</span><span class="p">(</span><span class="n">dp</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>Daha büyük girdilerde ikili arama kullanan “sabır sıralaması” yaklaşımı süreyi $O(n\log n)$ düzeyine indirir. Ancak bu yöntemde tutulan yardımcı dizi, her zaman LIS’in kendisi değildir; öncelikle mümkün olan en küçük kuyruk değerlerini temsil eder.</p>

<h2 id="en-uzun-ortak-alt-dizi-lcs">En Uzun Ortak Alt Dizi (LCS)</h2>

<p>LCS, iki dizide veya metinde sırayı bozmadan ortak kalan en uzun yapıyı bulur. <code class="language-plaintext highlighter-rouge">KODLAMA</code> ve <code class="language-plaintext highlighter-rouge">KAPLAMA</code> sözcükleri karşılaştırıldığında ortak karakterlerin sırası, benzerliğin iskeletini oluşturur.</p>

<p>$dp[i][j]$, ilk metnin ilk $i$, ikinci metnin ilk $j$ karakteri için LCS uzunluğu olsun:</p>

\[dp[i][j] = \begin{cases}dp[i-1][j-1]+1, &amp; x_i=y_j \\ \max(dp[i-1][j],dp[i][j-1]), &amp; x_i\ne y_j\end{cases}\]

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">lcs_length</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">):</span>
    <span class="c1"># Tablo, metin önekleri arasındaki en iyi ortak uzunlukları saklar.
</span>    <span class="n">dp</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="nf">len</span><span class="p">(</span><span class="n">second</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="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">first</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="n">i</span><span class="p">,</span> <span class="n">left</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="mi">1</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">j</span><span class="p">,</span> <span class="n">right</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">second</span><span class="p">,</span> <span class="mi">1</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">left</span> <span class="o">==</span> <span class="n">right</span><span class="p">:</span>
                <span class="n">dp</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">dp</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">dp</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="nf">max</span><span class="p">(</span><span class="n">dp</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">],</span> <span class="n">dp</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">])</span>

    <span class="k">return</span> <span class="n">dp</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">1</span><span class="p">]</span>
</code></pre></div></div>

<p>Bu çözümün zaman ve bellek maliyeti $O(nm)$’dir. Yalnızca uzunluk gerekiyorsa iki satır tutularak bellek $O(\min(n,m))$ seviyesine düşürülebilir.</p>

<h2 id="lcs-ile-dizi-benzerliği">LCS ile dizi benzerliği</h2>

<p>Ham LCS değeri uzun metinleri doğal olarak avantajlı kılar. Bu nedenle normalize edilmiş bir skor kullanabiliriz:</p>

\[S(A,B)=\frac{2\cdot LCS(A,B)}{|A|+|B|}\]

<p>Skor $0$ ile $1$ arasındadır; $1$, dizilerin tamamen aynı olduğunu gösterir.</p>

<table>
  <thead>
    <tr>
      <th>Yöntem</th>
      <th style="text-align: right">Sıraya duyarlı mı?</th>
      <th>Güçlü olduğu alan</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>LCS benzerliği</td>
      <td style="text-align: right">Evet</td>
      <td>Sürüm ve değişiklik analizi</td>
    </tr>
    <tr>
      <td>Jaccard</td>
      <td style="text-align: right">Genellikle hayır</td>
      <td>Kelime veya etiket kümeleri</td>
    </tr>
    <tr>
      <td>Levenshtein</td>
      <td style="text-align: right">Evet</td>
      <td>Yazım hataları ve düzenleme maliyeti</td>
    </tr>
    <tr>
      <td>Kosinüs benzerliği</td>
      <td style="text-align: right">Hayır</td>
      <td>Büyük belgelerde konu benzerliği</td>
    </tr>
  </tbody>
</table>

<p>Gerçek metinlerde karşılaştırmadan önce küçük harfe dönüştürme, noktalama temizleme ve kelimelere ayırma yapılabilir. Karakter tabanlı LCS küçük yazım değişikliklerini, kelime tabanlı LCS ise cümle yapısını daha iyi yakalar. Kısacası LIS yükselişleri, LCS ortak düzeni bulur; benzerlik skoru da bu düzeni yorumlanabilir bir sayıya dönüştürür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="dinamik programlama" /><category term="alt dizi" /><category term="metin işleme" /><summary type="html"><![CDATA[Bir sayı dizisindeki yükselişleri bulmakla iki metnin ne kadar benzediğini ölçmek ilk bakışta ayrı dünyaların işi gibi görünebilir. Oysa En Uzun Artan Alt Dizi (LIS) ve En Uzun Ortak Alt Dizi (LCS), aynı temel sorunun akrabalarıdır: Sırayı bozmadan hangi elemanları seçebiliriz? Bu yaklaşım; sürüm karşılaştırma, intihal tespiti, DNA analizi ve yazım düzeltme gibi birçok alanda karşımıza çıkar.]]></summary></entry><entry><title type="html">Aralık Sorgularının İki Ustası: Segment Ağacı ve Fenwick Ağacı</title><link href="https://sonsuzus.github.io/posts/aralik-sorgularinin-iki-ustasi-segment-agaci-ve-fenwick-agaci/" rel="alternate" type="text/html" title="Aralık Sorgularının İki Ustası: Segment Ağacı ve Fenwick Ağacı" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/aralik-sorgularinin-iki-ustasi-segment-agaci-ve-fenwick-agaci</id><content type="html" xml:base="https://sonsuzus.github.io/posts/aralik-sorgularinin-iki-ustasi-segment-agaci-ve-fenwick-agaci/"><![CDATA[<p>Bir dizideki elemanlar sürekli değişirken belirli bir aralığın toplamını hızlıca bulmak istediğimizi düşünelim. Her sorguda aralığı baştan dolaşmak kolaydır; ancak veri büyüdüğünde işlemciniz küçük bir maraton koşmaya başlar. Segment ağacı ve Fenwick ağacı, aralık sorguları ile nokta güncellemelerini verimli biçimde birleştirerek bu sorunu çözer.</p>

<p>``</p>

<h2 id="problem-neden-zor">Problem neden zor?</h2>

<p>$n$ elemanlı bir dizide <code class="language-plaintext highlighter-rouge">a[i]</code> değerini değiştiren nokta güncellemeleri ve $[l,r]$ aralığının toplamını isteyen sorgular bulunsun. Doğrudan yaklaşımda güncelleme $O(1)$, sorgu ise $O(n)$ sürer. Prefix toplam dizisi kullanırsak sorguyu $O(1)$ yapabiliriz; fakat tek bir değişiklikten sonra birçok prefix değeri yenileneceği için güncelleme $O(n)$ olur.</p>

<p>İdeal hedef, iki işlemi de yaklaşık $O(\log n)$ zamanda gerçekleştirmektir. Dengeli ağaç mantığı burada devreye girer: Dizinin tamamını değil, değişiklikten etkilenen az sayıdaki özet bilgiyi güncelleriz.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th style="text-align: right">Aralık toplamı</th>
      <th style="text-align: right">Nokta güncelleme</th>
      <th style="text-align: right">Bellek</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Doğrudan dizi</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(1)$</td>
      <td style="text-align: right">$O(n)$</td>
    </tr>
    <tr>
      <td>Prefix toplam</td>
      <td style="text-align: right">$O(1)$</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(n)$</td>
    </tr>
    <tr>
      <td>Fenwick ağacı</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)$</td>
    </tr>
    <tr>
      <td>Segment ağacı</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)$</td>
    </tr>
  </tbody>
</table>

<h2 id="fenwick-ağacı-bitlerle-çalışan-kompakt-çözüm">Fenwick ağacı: Bitlerle çalışan kompakt çözüm</h2>

<p>Binary Indexed Tree olarak da bilinen Fenwick ağacı, her hücrede belirli uzunluktaki bir aralığın toplamını saklar. Bir indeksin kapsadığı bölüm, en düşük anlamlı bitiyle belirlenir:</p>

\[\operatorname{lowbit}(x)=x\mathbin{\&amp;}(-x)\]

<p>Örneğin <code class="language-plaintext highlighter-rouge">12</code> sayısının ikilik gösterimi <code class="language-plaintext highlighter-rouge">1100</code> olduğundan <code class="language-plaintext highlighter-rouge">lowbit(12)=4</code> olur. Güncelleme sırasında <code class="language-plaintext highlighter-rouge">i += lowbit(i)</code>, prefix sorgusunda ise <code class="language-plaintext highlighter-rouge">i -= lowbit(i)</code> adımları izlenir.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Fenwick</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="kt">long</span> <span class="kt">long</span><span class="o">&gt;</span> <span class="n">bit</span><span class="p">;</span>
<span class="nl">public:</span>
    <span class="n">Fenwick</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">bit</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="mi">0</span><span class="p">)</span> <span class="p">{}</span>

    <span class="kt">void</span> <span class="nf">add</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</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="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="n">bit</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">delta</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">long</span> <span class="kt">long</span> <span class="nf">prefixSum</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</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="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="n">result</span> <span class="o">+=</span> <span class="n">bit</span><span class="p">[</span><span class="n">i</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">rangeSum</span><span class="p">(</span><span class="kt">int</span> <span class="n">l</span><span class="p">,</span> <span class="kt">int</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">prefixSum</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="o">-</span> <span class="n">prefixSum</span><span class="p">(</span><span class="n">l</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>Kod 1 tabanlı indeksleme kullanır. <code class="language-plaintext highlighter-rouge">[l,r]</code> toplamı, iki prefix toplamının farkıdır:</p>

\[S(l,r)=S(1,r)-S(1,l-1)\]

<p>Bir değeri <code class="language-plaintext highlighter-rouge">yeniDeger</code> yapmak için önce <code class="language-plaintext highlighter-rouge">delta = yeniDeger - eskiDeger</code> hesaplanıp <code class="language-plaintext highlighter-rouge">add(i, delta)</code> çağrılmalıdır.</p>

<h2 id="segment-ağacı-daha-esnek-ağır-iş-makinesi">Segment ağacı: Daha esnek ağır iş makinesi</h2>

<p>Segment ağacı, kökünde tüm diziyi; çocuklarında ise aralığın sol ve sağ yarılarını saklar. Yapraklar tek elemanları temsil eder. Bir düğümün değeri, toplama probleminde çocuklarının toplamıdır:</p>

\[tree[v]=tree[2v]+tree[2v+1]\]

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">long</span> <span class="kt">long</span> <span class="nf">query</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">tl</span><span class="p">,</span> <span class="kt">int</span> <span class="n">tr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">l</span><span class="p">,</span> <span class="kt">int</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l</span> <span class="o">&gt;</span> <span class="n">r</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l</span> <span class="o">==</span> <span class="n">tl</span> <span class="o">&amp;&amp;</span> <span class="n">r</span> <span class="o">==</span> <span class="n">tr</span><span class="p">)</span> <span class="k">return</span> <span class="n">tree</span><span class="p">[</span><span class="n">v</span><span class="p">];</span>

    <span class="kt">int</span> <span class="n">tm</span> <span class="o">=</span> <span class="p">(</span><span class="n">tl</span> <span class="o">+</span> <span class="n">tr</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">query</span><span class="p">(</span><span class="n">v</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="n">tl</span><span class="p">,</span> <span class="n">tm</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">min</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">tm</span><span class="p">))</span>
         <span class="o">+</span> <span class="n">query</span><span class="p">(</span><span class="n">v</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">tm</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">tr</span><span class="p">,</span> <span class="n">max</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">tm</span> <span class="o">+</span> <span class="mi">1</span><span class="p">),</span> <span class="n">r</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">update</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">tl</span><span class="p">,</span> <span class="kt">int</span> <span class="n">tr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">pos</span><span class="p">,</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">tl</span> <span class="o">==</span> <span class="n">tr</span><span class="p">)</span> <span class="n">tree</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">tm</span> <span class="o">=</span> <span class="p">(</span><span class="n">tl</span> <span class="o">+</span> <span class="n">tr</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">pos</span> <span class="o">&lt;=</span> <span class="n">tm</span><span class="p">)</span> <span class="n">update</span><span class="p">(</span><span class="n">v</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="n">tl</span><span class="p">,</span> <span class="n">tm</span><span class="p">,</span> <span class="n">pos</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
        <span class="k">else</span> <span class="n">update</span><span class="p">(</span><span class="n">v</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">tm</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">tr</span><span class="p">,</span> <span class="n">pos</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
        <span class="n">tree</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">tree</span><span class="p">[</span><span class="n">v</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="n">tree</span><span class="p">[</span><span class="n">v</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="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Sorgu yalnızca hedef aralıkla kesişen düğümlere iner. Güncelleme de yapraktan köke kadar tek bir yol izler; ağacın yüksekliği $\log_2 n$ civarındadır.</p>

<h2 id="hangisini-seçmeliyiz">Hangisini seçmeliyiz?</h2>

<p>Yalnızca toplam, XOR veya benzeri prefix farkıyla çözülebilen işlemler varsa Fenwick ağacı kısa, hızlı ve az bellek tüketen seçenektir. Minimum, maksimum, maksimum alt dizi toplamı ya da ileride lazy propagation ile aralık güncellemesi gerekiyorsa segment ağacı daha uygundur.</p>

<p>Kısacası Fenwick ağacı çevik bir motosiklet, segment ağacı ise donanımlı bir arazi aracıdır. Yol düzse motosiklet harikadır; sorgular çamurlanmaya başladığında arazi aracı gülümseyerek öne çıkar.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="veri yapıları" /><category term="segment ağacı" /><category term="fenwick ağacı" /><summary type="html"><![CDATA[Bir dizideki elemanlar sürekli değişirken belirli bir aralığın toplamını hızlıca bulmak istediğimizi düşünelim. Her sorguda aralığı baştan dolaşmak kolaydır; ancak veri büyüdüğünde işlemciniz küçük bir maraton koşmaya başlar. Segment ağacı ve Fenwick ağacı, aralık sorguları ile nokta güncellemelerini verimli biçimde birleştirerek bu sorunu çözer.]]></summary></entry><entry><title type="html">Asal Sayılardan Hızlı Üs Almaya: Matematiğin Programlama Motoru</title><link href="https://sonsuzus.github.io/posts/asal-sayilardan-hizli-us-almaya-matematigin-programlama-motoru/" rel="alternate" type="text/html" title="Asal Sayılardan Hızlı Üs Almaya: Matematiğin Programlama Motoru" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/asal-sayilardan-hizli-us-almaya-matematigin-programlama-motoru</id><content type="html" xml:base="https://sonsuzus.github.io/posts/asal-sayilardan-hizli-us-almaya-matematigin-programlama-motoru/"><![CDATA[<p>Bilgisayarlar matematik konusunda hızlıdır; ancak sonsuz sabırlı değillerdir. Sayılar büyüdükçe sıradan yöntemler yavaşlar, bellek tüketimi artar ve taşma sorunları ortaya çıkar. Asal sayılar, modüler aritmetik ve hızlı üs alma teknikleri; kriptografiden algoritma yarışmalarına kadar bu sorunları çözmemizi sağlayan güçlü araçlardır.
``</p>
<h2 id="asal-sayılar-aritmetiğin-atomları">Asal Sayılar: Aritmetiğin Atomları</h2>

<p>Yalnızca $1$ ve kendisine tam bölünebilen, $1$’den büyük doğal sayılara <strong>asal sayı</strong> denir. Örneğin $2, 3, 5, 7$ asaldır; $12$ ise $2 \times 2 \times 3$ biçiminde ayrılabildiği için bileşiktir.</p>

<p>Aritmetiğin Temel Teoremi’ne göre $1$’den büyük her tam sayı, asal sayıların çarpımı olarak tek bir biçimde yazılabilir. Çarpanların sırası sonucu değiştirmez:</p>

\[360 = 2^3 \times 3^2 \times 5\]

<p>Bu özellik, asal sayıları doğal sayıların yapı taşları hâline getirir. Bir sayının asal olup olmadığını kontrol etmek için $2$’den başlayıp sayının kendisine kadar ilerlemek gereksizdir. Eğer $n$ bileşikse çarpanlarından en az biri $\sqrt{n}$ değerinden küçük veya ona eşittir. Dolayısıyla kontrol sınırı $\sqrt{n}$ olabilir.</p>

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

<span class="k">def</span> <span class="nf">asal_mi</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">for</span> <span class="n">bolen</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="nf">isqrt</span><span class="p">(</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="k">if</span> <span class="n">n</span> <span class="o">%</span> <span class="n">bolen</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="bp">True</span>
</code></pre></div></div>

<p>Bu fonksiyonun zaman karmaşıklığı yaklaşık $O(\sqrt{n})$ olur. Belirli bir sınıra kadarki bütün asalları bulmak gerektiğinde ise Eratosthenes Eleği daha uygundur.</p>

<h2 id="modüler-aritmetik-sayıların-saat-hâli">Modüler Aritmetik: Sayıların Saat Hâli</h2>

<p>Modüler aritmetik, bölme işleminden kalanlarla ilgilenir. Saat 10’a üç saat ekleyince 13 değil, 1 denmesi bunun günlük hayattaki örneğidir. Matematiksel olarak:</p>

\[13 \equiv 1 \pmod{12}\]

<p>Bu ifade, $13$ ve $1$ sayılarının 12 ile bölündüğünde aynı kalanı verdiğini söyler. Modüler işlemler büyük değerleri kontrol altında tutar:</p>

\[10^{100} \bmod 9 = 1\]

<p>Çünkü $10 \equiv 1 \pmod 9$ olduğundan $10^{100} \equiv 1^{100} \equiv 1 \pmod 9$ elde edilir.</p>

<table>
  <thead>
    <tr>
      <th>Normal işlem</th>
      <th>Modüler karşılığı</th>
      <th>Avantajı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>$a+b$</td>
      <td>$(a \bmod m+b \bmod m)\bmod m$</td>
      <td>Sayıları küçük tutar</td>
    </tr>
    <tr>
      <td>$a\times b$</td>
      <td>$((a\bmod m)(b\bmod m))\bmod m$</td>
      <td>Dev çarpımları sınırlar</td>
    </tr>
    <tr>
      <td>$a^b$</td>
      <td>$a^b\bmod m$</td>
      <td>Kriptografide kullanılır</td>
    </tr>
    <tr>
      <td>$a-b$</td>
      <td>$(a-b+m)\bmod m$</td>
      <td>Negatif kalanı önler</td>
    </tr>
  </tbody>
</table>

<p>Programlama dillerinin negatif sayılarda kalan davranışı farklı olabileceği için güvenli normalleştirme genellikle <code class="language-plaintext highlighter-rouge">((x % m) + m) % m</code> biçimindedir.</p>

<h2 id="büyük-sayılarla-i̇şlemler">Büyük Sayılarla İşlemler</h2>

<p>Python tam sayıları ihtiyaç oldukça büyüyebilir. C, C++ ve Java gibi dillerdeyse sabit genişlikli türler taşabilir. Örneğin işaretli 64 bit bir tam sayının üst sınırı $2^{63}-1$ değeridir.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th>Kullanım alanı</th>
      <th>Dezavantajı</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>64 bit tam sayı</td>
      <td>Hızlı genel hesaplama</td>
      <td>Taşma riski</td>
    </tr>
    <tr>
      <td>BigInteger</td>
      <td>Kesin ve dev değerler</td>
      <td>Daha yavaş çalışma</td>
    </tr>
    <tr>
      <td>Modüler hesaplama</td>
      <td>Yalnızca kalan gerekiyorsa</td>
      <td>Asıl sayı korunmaz</td>
    </tr>
  </tbody>
</table>

<p>Sonuç yalnızca belirli bir modüle göre isteniyorsa dev sayıyı üretmek yerine her adımda mod almak en verimli çözümdür.</p>

<h2 id="hızlı-üs-alma-üssü-i̇kiye-böl">Hızlı Üs Alma: Üssü İkiye Böl</h2>

<p>$a^b$ değerini $b$ kez çarpmak $O(b)$ işlem gerektirir. <strong>Binary exponentiation</strong>, üssü sürekli ikiye bölerek bunu $O(\log b)$ seviyesine indirir. Temel fikir şudur:</p>

\[a^{2k}=(a^k)^2, \qquad a^{2k+1}=a(a^k)^2\]

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">hizli_us</span><span class="p">(</span><span class="n">taban</span><span class="p">,</span> <span class="n">us</span><span class="p">,</span> <span class="n">mod</span><span class="p">):</span>
    <span class="n">sonuc</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="n">taban</span> <span class="o">%=</span> <span class="n">mod</span>

    <span class="k">while</span> <span class="n">us</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">us</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">sonuc</span> <span class="o">=</span> <span class="p">(</span><span class="n">sonuc</span> <span class="o">*</span> <span class="n">taban</span><span class="p">)</span> <span class="o">%</span> <span class="n">mod</span>
        <span class="n">taban</span> <span class="o">=</span> <span class="p">(</span><span class="n">taban</span> <span class="o">*</span> <span class="n">taban</span><span class="p">)</span> <span class="o">%</span> <span class="n">mod</span>
        <span class="n">us</span> <span class="o">//=</span> <span class="mi">2</span>

    <span class="k">return</span> <span class="n">sonuc</span>
</code></pre></div></div>

<p>Kod, üssün ikilik gösterimindeki her biti işler. Bit 1 olduğunda mevcut taban sonuca katılır; ardından taban karesine yükseltilir. Böylece örneğin $3^{1\,000\,000}\bmod 1\,000\,000\,007$ saniyeler beklemeden hesaplanabilir.</p>

<p>Bu kavramlar birlikte düşünüldüğünde güçlü bir algoritmik araç kutusu oluşur: asal sayılar çarpan yapısını açıklar, modüler aritmetik değerleri dizginler, büyük sayı stratejileri taşmayı önler ve hızlı üs alma astronomik görünen hesapları birkaç adıma indirir. Matematiğin programlamadaki süper gücü tam olarak budur.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="asal sayılar" /><category term="modüler aritmetik" /><category term="hızlı üs alma" /><summary type="html"><![CDATA[Bilgisayarlar matematik konusunda hızlıdır; ancak sonsuz sabırlı değillerdir. Sayılar büyüdükçe sıradan yöntemler yavaşlar, bellek tüketimi artar ve taşma sorunları ortaya çıkar. Asal sayılar, modüler aritmetik ve hızlı üs alma teknikleri; kriptografiden algoritma yarışmalarına kadar bu sorunları çözmemizi sağlayan güçlü araçlardır.]]></summary></entry><entry><title type="html">Big O ile Doğru Algoritmayı Seçmek: Hız ve Bellek Dengesi</title><link href="https://sonsuzus.github.io/posts/big-o-ile-dogru-algoritmayi-secmek-hiz-ve-bellek-dengesi/" rel="alternate" type="text/html" title="Big O ile Doğru Algoritmayı Seçmek: Hız ve Bellek Dengesi" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/big-o-ile-dogru-algoritmayi-secmek-hiz-ve-bellek-dengesi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/big-o-ile-dogru-algoritmayi-secmek-hiz-ve-bellek-dengesi/"><![CDATA[<p>Bir problemi çözen ilk algoritmayı bulmak güzeldir; ancak milyonlarca veri geldiğinde hâlâ çalışan algoritmayı bulmak çok daha güzeldir. Big O notasyonu, farklı algoritmaların veri büyüdükçe nasıl davranacağını karşılaştırmamızı sağlar. Böylece yalnızca çalışan değil, zaman ve bellek açısından sürdürülebilir çözümler seçebiliriz.
``</p>

<h2 id="big-o-neyi-ölçer">Big O neyi ölçer?</h2>

<p>Big O, bir algoritmanın girdi boyutu $n$ büyürken kaynak tüketiminin hangi hızla arttığını ifade eder. Tam çalışma süresini saniye cinsinden söylemez; donanım, programlama dili ve derleyici gibi ayrıntıları göz ardı ederek büyüme eğilimini gösterir.</p>

<p>Örneğin bir döngü $n$ elemanın tamamını geziyorsa zaman karmaşıklığı $O(n)$ olur. İç içe iki döngünün her biri $n$ kez çalışıyorsa yaklaşık $n \times n=n^2$ işlem yapılır ve sonuç $O(n^2)$ olarak gösterilir.</p>

<p>Big O hesaplanırken sabit katsayılar ve düşük dereceli terimler atılır:</p>

\[T(n)=4n^2+7n+12 \Rightarrow O(n^2)\]

<p>Çünkü büyük $n$ değerlerinde davranışı belirleyen baskın terim $n^2$ olur.</p>

<p>| Karmaşıklık | Tipik örnek | Büyüme davranışı |
|—|—|—|
| $O(1)$ | Dizide indeksle erişim | Girdiden bağımsız |
| $O(\log n)$ grid | İkili arama | Çok yavaş büyür |
| $O(n)$ | Doğrusal arama | Girdiyle aynı oranda büyür |
| $O(n\log n)$ | Merge Sort | Büyük veriler için genellikle uygundur |
| $O(n^2)$ | İç içe döngüler | Büyük girdilerde pahalıdır |
l
| $O(2^n)$ ring | Alt kümeleri denemek | Küçük girdilerde bile patlayabilir |</p>

<h2 id="zaman-karmaşıklığı-nasıl-analiz-edilir">Zaman karmaşıklığı nasıl analiz edilir?</h2>

<p>Önce temel işlemin kaç kez çalıştığını belirlemek gerekir. Ardışık işlemlerin maliyetleri toplanır, iç içe işlemlerin maliyetleri ise çarpılır. Ardından baskın terim seçilir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">tekrar_var_mi</span><span class="p">(</span><span class="n">liste</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="nf">len</span><span class="p">(</span><span class="n">liste</span><span class="p">)):</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">liste</span><span class="p">)):</span>
            <span class="k">if</span> <span class="n">liste</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">liste</span><span class="p">[</span><span class="n">j</span><span class="p">]:</span>
                <span class="k">return</span> <span class="bp">True</span>
    <span class="k">return</span> <span class="bp">False</span>
</code></pre></div></div>

<p>Bu kod, en kötü durumda her elemanı diğer elemanlarla karşılaştırır. Karşılaştırma sayısı yaklaşık $n(n-1)/2$ olduğundan zaman karmaşıklığı $O(n^2)$, ek mekan karmaşıklığı ise $O(1)$ olur.</p>

<p>Bir küme kullanarak zamanı iyileştirebiliriz:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">tekrar_var_mi</span><span class="p">(</span><span class="n">liste</span><span class="p">):</span>
    <span class="n">gorulenler</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">eleman</span> <span class="ow">in</span> <span class="n">liste</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">eleman</span> <span class="ow">in</span> <span class="n">gorulenler</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">True</span>
        <span class="n">gorulenler</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">eleman</span><span class="p">)</span>
    <span class="k">return</span> <span class="bp">False</span>
</code></pre></div></div>

<p>Kümede arama ve ekleme ortalama $O(1)$ kabul edilir. Bu nedenle toplam süre $O(n)$ seviyesine iner; fakat küme en kötü durumda $n$ mans eleman sakladığı için mekan tüketimi $O(n)$ olur. Yani zamanı kazanırken belleği harcadık.</p>

<h2 id="mekan-karmaşıklığını-unutma">Mekan karmaşıklığını unutma</h2>

<p>Mekan karmaşıklığı,/algo algoritmanın giriş dışında kullandığı ek belleği inceler. Değişkenler, geçici diziler, veri yapıları ve özyinelemeli çağrı yığını hesaba katılmalıdır.</p>

<table>
  <thead>
    <tr>
      <th>ring</th>
      <th style="text-align: right">Yaklaşım</th>
      <th style="text-align: right">Zaman</th>
      <th>Ek mekan</th>
      <th>Tercih nedeni</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td> </td>
      <td style="text-align: right">Çift döngü</td>
      <td style="text-align: right">$O(n-ni^2)$</td>
      <td>$O(1)$</td>
      <td>Bellek çok sınırlıysa</td>
    </tr>
    <tr>
      <td> </td>
      <td style="text-align: right">Küme kullanımı</td>
      <td style="text-align: right">$O(n)$ ortalama</td>
      <td>$O(n)$</td>
      <td>Hız slot betvikiubilrse phrase ya da büyük veri işleniyorsa</td>
    </tr>
  </tbody>
</table>

<p>Özyinelemeli bir fonksiyon da görünürde ek koleksiyon oluşturmasa bile çağrı yığını tüketir. Örneğin dengeli bir ikili ağaçta arama derinliği $O(\log n)$ ise yığın alanı da $O(\log n)$ olabilir.</p>

<p>plant ## En verimli algoritmayı seçme yöntemi</p>

<p>Öncelikle gerçek kısıtları belirle: En büyük girdi kaç elemanlı? Bellek sınırı nedir? Sonuç anlık mı üretilmeli? Ardından aday algoritmaların en kötü, ortalama ve gerekiyorsa en iyi durumlarını karşılaştır.</p>

<p>Küçük $n$ değerlerinde basit bir $O(n^2)$ çözüm, düşük sabit maliyetleri sayesinde yeterli olabilir. Buna karşılık $n=1\,000\,000$ olduğunda karesel bir yaklaşım yaklaşık $10^{12}$ işlem anlamına gelir. Bu noktada $O(n\log n)$ veya $O(n)$ seçenekleri belirgin biçimde avantajlıdır.</p>

<p>Son olarak teorik analizi gerçek ölçümlerle doğrula. Profil çıkarma araçları, önbellek davranışı ve veri dağılımı gibi Big O’nun göstermediği ayrıntıları ortaya çıkarır. En iyi seçim her zaman en küçük notasyon değildir; ihtiyaçları karşılayan, anlaşılır ve kaynak dengesini doğru kuran algoritmadır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="Big O" /><category term="algoritma analizi" /><category term="zaman karmaşıklığı" /><summary type="html"><![CDATA[Bir problemi çözen ilk algoritmayı bulmak güzeldir; ancak milyonlarca veri geldiğinde hâlâ çalışan algoritmayı bulmak çok daha güzeldir. Big O notasyonu, farklı algoritmaların veri büyüdükçe nasıl davranacağını karşılaştırmamızı sağlar. Böylece yalnızca çalışan değil, zaman ve bellek açısından sürdürülebilir çözümler seçebiliriz.]]></summary></entry><entry><title type="html">Bit Maskeleme: Küçücük Bitlerle Kümeler, Durumlar ve Altkümeler</title><link href="https://sonsuzus.github.io/posts/bit-maskeleme-kucucuk-bitlerle-kumeler-durumlar-ve-altkumeler/" rel="alternate" type="text/html" title="Bit Maskeleme: Küçücük Bitlerle Kümeler, Durumlar ve Altkümeler" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/bit-maskeleme-kucucuk-bitlerle-kumeler-durumlar-ve-altkumeler</id><content type="html" xml:base="https://sonsuzus.github.io/posts/bit-maskeleme-kucucuk-bitlerle-kumeler-durumlar-ve-altkumeler/"><![CDATA[<p>Bir grup anahtarı, özelliği veya seçimi tek bir tamsayı içinde saklamak kulağa sihir gibi gelebilir. Bit maskeleme tam olarak bunu yapar: Her biti bir elemanın varlığına ya da bir durumun açık olup olmadığına ayırır. Böylece klasik veri yapılarına göre daha az bellek kullanabilir, küme işlemlerini birkaç işlemci komutuyla gerçekleştirebilir ve özellikle kombinasyon problemlerini zarif biçimde çözebiliriz.</p>

<p>``</p>

<h2 id="temel-fikir-her-bit-bir-eleman">Temel fikir: Her bit bir eleman</h2>

<p>$n$ elemanlı bir kümenin her altkümesi, $n$ bitlik bir sayı ile temsil edilebilir. Sağdan $i$. bit 1 ise $i$. eleman kümede, 0 ise kümede değildir. Örneğin <code class="language-plaintext highlighter-rouge">{A, B, C, D}</code> evreninde <code class="language-plaintext highlighter-rouge">0101</code> maskesi <code class="language-plaintext highlighter-rouge">{A, C}</code> kümesini gösterir.</p>

<p>$n$ bit ile ifade edilebilen farklı durum sayısı:</p>

\[2^n\]

<p>olur. Bu nedenle bit maskeleri genellikle $n$ değerinin küçük olduğu, fakat çok sayıda durumun incelendiği algoritmalarda kullanılır.</p>

<table>
  <thead>
    <tr>
      <th>Operatör</th>
      <th>Anlamı</th>
      <th>Küme yorumu</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&amp;</code></td>
      <td>Bitsel VE</td>
      <td>Kesişim</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">\|</code></td>
      <td>Bitsel VEYA</td>
      <td>Birleşim</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">^</code></td>
      <td>Bitsel XOR</td>
      <td>Simetrik fark</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">~</code></td>
      <td>Bitsel DEĞİL</td>
      <td>Tümleyen</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&lt;&lt;</code></td>
      <td>Sola kaydırma</td>
      <td>İstenen bit için maske üretme</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&gt;&gt;</code></td>
      <td>Sağa kaydırma</td>
      <td>Bitleri konumlandırma veya okuma</td>
    </tr>
  </tbody>
</table>

<h2 id="eleman-ekleme-silme-ve-sorgulama">Eleman ekleme, silme ve sorgulama</h2>

<p>Bir elemanın biti <code class="language-plaintext highlighter-rouge">1 &lt;&lt; i</code> ifadesiyle oluşturulur. Ardından standart küme işlemleri birkaç satıra iner:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">mask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>

<span class="n">mask</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>      <span class="c1">// 2 numaralı elemanı ekler</span>
<span class="kt">bool</span> <span class="n">var</span> <span class="o">=</span> <span class="n">mask</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span> <span class="c1">// Eleman kümede mi?</span>
<span class="n">mask</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>     <span class="c1">// Elemanı siler</span>
<span class="n">mask</span> <span class="o">^=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>      <span class="c1">// Varlık durumunu tersine çevirir</span>
</code></pre></div></div>

<p>Birleşim için <code class="language-plaintext highlighter-rouge">a | b</code>, kesişim için <code class="language-plaintext highlighter-rouge">a &amp; b</code>, fark için ise <code class="language-plaintext highlighter-rouge">a &amp; ~b</code> kullanılabilir. Tümleyen alınırken tamsayının kullanılmayan üst bitleri de ters döneceğinden, sonucu evrenle sınırlamak gerekir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">full</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</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="kt">int</span> <span class="n">tumleyen</span> <span class="o">=</span> <span class="p">(</span><span class="o">~</span><span class="n">mask</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">full</span><span class="p">;</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">full</code>, ilk $n$ biti 1 olan evrensel kümedir. Bu küçük ayrıntı unutulursa masum görünen tümleyen işlemi bitlerden oluşan bir canavara dönüşebilir.</p>

<h2 id="durum-sıkıştırma">Durum sıkıştırma</h2>

<p>Bit maskeleri yalnızca kümeler için değildir. Açık-kapalı seçenekler de tek sayıda saklanabilir. Bir oyunda kapının açık olması, oyuncunun anahtarı taşıması ve alarmın çalışması ayrı bitlerle temsil edilebilir.</p>

<table>
  <thead>
    <tr>
      <th>Yaklaşım</th>
      <th style="text-align: right">Bellek</th>
      <th style="text-align: right">Karşılaştırma</th>
      <th>Uygun kullanım</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Ayrı <code class="language-plaintext highlighter-rouge">bool</code> değişkenleri</td>
      <td style="text-align: right">Görece yüksek</td>
      <td style="text-align: right">Alan alan yapılır</td>
      <td>Okunabilir iş mantığı</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">vector&lt;bool&gt;</code></td>
      <td style="text-align: right">Dinamik</td>
      <td style="text-align: right">Döngü gerekebilir</td>
      <td>Çok sayıda özellik</td>
    </tr>
    <tr>
      <td>Bit maskesi</td>
      <td style="text-align: right">Çok düşük</td>
      <td style="text-align: right">Tek tamsayı işlemi</td>
      <td>Küçük ve yoğun durum uzayı</td>
    </tr>
  </tbody>
</table>

<p>Örneğin dinamik programlamada <code class="language-plaintext highlighter-rouge">dp[mask]</code>, <code class="language-plaintext highlighter-rouge">mask</code> tarafından belirtilen seçimlere ulaşıldığında elde edilen en iyi sonucu tutabilir. Gezgin satıcı problemindeki yaygın durum $dp[mask][v]$ biçimindedir: Ziyaret edilen şehirler <code class="language-plaintext highlighter-rouge">mask</code>, son şehir ise $v$ ile gösterilir. Yaklaşık durum sayısı $O(2^n n)$ olur.</p>

<h2 id="bütün-altkümeleri-üretmek">Bütün altkümeleri üretmek</h2>

<p><code class="language-plaintext highlighter-rouge">0</code> ile $2^n-1$ arasındaki her sayı farklı bir altkümedir:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">mask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">mask</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">n</span><span class="p">);</span> <span class="o">++</span><span class="n">mask</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</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="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">mask</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">))</span> <span class="p">{</span>
            <span class="c1">// i numaralı eleman bu altkümede bulunur.</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bu yöntem $O(n2^n)$ zamanda çalışır. Yalnızca belirli bir <code class="language-plaintext highlighter-rouge">mask</code> kümesinin altkümelerini gezmek için daha şık bir numara vardır:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">sub</span> <span class="o">=</span> <span class="n">mask</span><span class="p">;</span> <span class="n">sub</span><span class="p">;</span> <span class="n">sub</span> <span class="o">=</span> <span class="p">(</span><span class="n">sub</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">mask</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// sub, mask kümesinin boş olmayan bir altkümesidir.</span>
<span class="p">}</span>
<span class="c1">// Boş altküme gerekiyorsa ayrıca işlenir.</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">sub - 1</code>, en sağdaki 1 bitini düşürüp sağını değiştirir; <code class="language-plaintext highlighter-rouge">&amp; mask</code> ise maskeye ait olmayan bitleri temizler. Tüm maskeler ve onların altmaskeleri birlikte gezildiğinde toplam karmaşıklık $O(3^n)$ olur.</p>

<p>Bit maskeleme ilk bakışta sayıların içinde saklambaç oynamak gibidir. Ancak bitleri eleman olarak düşünmeye başladığınızda küme işlemleri, durum sıkıştırma ve altküme tarama aynı güçlü fikrin farklı yüzlerine dönüşür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="bit-maskeleme" /><category term="bitsel-operatorler" /><category term="algoritmalar" /><summary type="html"><![CDATA[Bir grup anahtarı, özelliği veya seçimi tek bir tamsayı içinde saklamak kulağa sihir gibi gelebilir. Bit maskeleme tam olarak bunu yapar: Her biti bir elemanın varlığına ya da bir durumun açık olup olmadığına ayırır. Böylece klasik veri yapılarına göre daha az bellek kullanabilir, küme işlemlerini birkaç işlemci komutuyla gerçekleştirebilir ve özellikle kombinasyon problemlerini zarif biçimde çözebiliriz.]]></summary></entry><entry><title type="html">Dinamik Programlamanın Kalbi: Alt Problemler, Durumlar ve Memoization</title><link href="https://sonsuzus.github.io/posts/dinamik-programlamanin-kalbi-alt-problemler-durumlar-ve-memoization/" rel="alternate" type="text/html" title="Dinamik Programlamanın Kalbi: Alt Problemler, Durumlar ve Memoization" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/dinamik-programlamanin-kalbi-alt-problemler-durumlar-ve-memoization</id><content type="html" xml:base="https://sonsuzus.github.io/posts/dinamik-programlamanin-kalbi-alt-problemler-durumlar-ve-memoization/"><![CDATA[<p>Bazı algoritmalar aynı hesabı tekrar tekrar yaparak işlemciyi küçük bir hamster çarkına sokar. Dinamik programlama, bu gereksiz tekrarları fark edip sonuçları saklayan güçlü bir problem çözme yaklaşımıdır. Temel fikir; büyük bir problemi daha küçük alt problemlere ayırmak, her alt problemi doğru biçimde temsil eden durumları tanımlamak ve hesaplanan sonuçları yeniden kullanmaktır.</p>

<p>``</p>

<h2 id="dinamik-programlama-ne-zaman-kullanılır">Dinamik programlama ne zaman kullanılır?</h2>

<p>Bir problemi parçalara bölmek tek başına dinamik programlama değildir. Yaklaşımın etkili olabilmesi için genellikle iki özellik aranır:</p>

<ol>
  <li><strong>Örtüşen alt problemler:</strong> Aynı alt problem farklı hesaplama yollarında tekrar ortaya çıkar.</li>
  <li><strong>Optimal alt yapı:</strong> Büyük problemin en iyi çözümü, alt problemlerin en iyi çözümlerinden oluşturulabilir.</li>
</ol>

<p>Örneğin Fibonacci dizisi şu bağıntıyla tanımlanır:</p>

\[F(n) = F(n-1) + F(n-2)\]

<p>ve başlangıç durumları şöyledir:</p>

\[F(0)=0, \qquad F(1)=1\]

<p>Saf özyinelemeli çözümde <code class="language-plaintext highlighter-rouge">F(n-2)</code> gibi değerler birçok kez hesaplanır. Çağrı ağacı büyüdükçe çalışma süresi yaklaşık $O(2^n)$ seviyesine çıkar. Dinamik programlama sayesinde her değer yalnızca bir kez hesaplanarak süre $O(n)$ olur.</p>

<h2 id="birinci-adım-alt-problemleri-belirlemek">Birinci adım: Alt problemleri belirlemek</h2>

<p>Büyük problemin daha küçük sürümleri alt problem olarak seçilir. Fibonacci örneğinde “<code class="language-plaintext highlighter-rouge">n</code> sayısının Fibonacci değerini bulmak” ana problemse <code class="language-plaintext highlighter-rouge">F(n-1)</code> ve <code class="language-plaintext highlighter-rouge">F(n-2)</code> onun alt problemleridir.</p>

<p>Daha gerçekçi bir örnek olan sırt çantası probleminde ise alt problem yalnızca kaç eşyanın incelendiğiyle açıklanamaz. Kalan kapasite de sonucu etkiler. Bu nedenle alt problemleri tanımlarken gelecekteki kararları etkileyen bütün bilgileri korumak gerekir.</p>

<h2 id="i̇kinci-adım-durumu-tanımlamak">İkinci adım: Durumu tanımlamak</h2>

<p><strong>Durum</strong>, bir alt problemi benzersiz biçimde ifade eden en küçük bilgi kümesidir. İyi durum tanımı, dinamik programlamanın en kritik bölümüdür.</p>

<p>Sırt çantası problemi için şu durum kullanılabilir:</p>

\[DP(i, c) = \text{İlk } i \text{ eşya ve } c \text{ kapasiteyle elde edilen en yüksek değer}\]

<p>Burada <code class="language-plaintext highlighter-rouge">i</code> hangi eşyalara bakabildiğimizi, <code class="language-plaintext highlighter-rouge">c</code> ise kalan kapasiteyi belirtir. Gereksiz bilgi eklemek durum sayısını artırır; eksik bilgi kullanmak ise farklı alt problemleri yanlışlıkla aynı kabul eder.</p>

<table>
  <thead>
    <tr>
      <th>Kavram</th>
      <th>Sorduğu soru</th>
      <th>Fibonacci örneği</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alt problem</td>
      <td>Daha küçük hangi problemi çözüyorum?</td>
      <td><code class="language-plaintext highlighter-rouge">F(n-1)</code></td>
    </tr>
    <tr>
      <td>Durum</td>
      <td>Alt problemi hangi bilgiler tanımlar?</td>
      <td><code class="language-plaintext highlighter-rouge">n</code></td>
    </tr>
    <tr>
      <td>Geçiş</td>
      <td>Sonraki sonuç nasıl üretilir?</td>
      <td><code class="language-plaintext highlighter-rouge">F(n-1) + F(n-2)</code></td>
    </tr>
    <tr>
      <td>Başlangıç</td>
      <td>Hesaplamayı nerede durdururum?</td>
      <td><code class="language-plaintext highlighter-rouge">F(0)</code> ve <code class="language-plaintext highlighter-rouge">F(1)</code></td>
    </tr>
  </tbody>
</table>

<h2 id="üçüncü-adım-memoization-ile-tekrarları-önlemek">Üçüncü adım: Memoization ile tekrarları önlemek</h2>

<p>Memoization, yukarıdan aşağıya çalışan özyinelemeli çözümün sonuçlarını bir önbellekte saklar. Fonksiyon aynı durumla yeniden çağrıldığında hesap yapmak yerine kayıtlı sonuç döndürülür.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">memo</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="c1"># Sözlük, daha önce çözülen durumların sonuçlarını saklar.
</span>    <span class="k">if</span> <span class="n">memo</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">memo</span> <span class="o">=</span> <span class="p">{}</span>

    <span class="k">if</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">memo</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">memo</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>

    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">n</span>

    <span class="n">memo</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="nf">fibonacci</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">memo</span><span class="p">)</span> <span class="o">+</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="n">memo</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">memo</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">fibonacci</span><span class="p">(</span><span class="mi">40</span><span class="p">))</span>
</code></pre></div></div>

<p>Bu kodda sözlüğün anahtarı durumu, yani <code class="language-plaintext highlighter-rouge">n</code> değerini temsil eder. Her durum bir kez çözüldüğü için toplam $n+1$ farklı durum bulunur. Her durumdaki işlem sabit zamanlı olduğundan karmaşıklık $O(n)$ zaman ve $O(n)$ bellek olur.</p>

<h2 id="memoization-ve-tabulation-farkı">Memoization ve tabulation farkı</h2>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Memoization</th>
      <th>Tabulation</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Yön</td>
      <td>Yukarıdan aşağıya</td>
      <td>Aşağıdan yukarıya</td>
    </tr>
    <tr>
      <td>Yapı</td>
      <td>Özyineleme</td>
      <td>Döngü</td>
    </tr>
    <tr>
      <td>Hesaplanan durumlar</td>
      <td>Yalnızca ihtiyaç duyulanlar</td>
      <td>Genellikle tüm tablo</td>
    </tr>
    <tr>
      <td>Risk</td>
      <td>Çağrı yığını taşabilir</td>
      <td>Sıralama hatası yapılabilir</td>
    </tr>
  </tbody>
</table>

<p>Dinamik programlama ezberlenecek bir kod kalıbı değil, düşünme disiplinidir: Önce alt problemleri keşfet, sonra durumu mümkün olduğunca sade tanımla, geçiş bağıntısını kur ve tekrarları saklayarak ortadan kaldır. Bu adımlar oturduğunda korkutucu görünen pek çok algoritma, düzenli doldurulan bir tabloya dönüşür.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="dinamik programlama" /><category term="algoritmalar" /><category term="memoization" /><summary type="html"><![CDATA[Bazı algoritmalar aynı hesabı tekrar tekrar yaparak işlemciyi küçük bir hamster çarkına sokar. Dinamik programlama, bu gereksiz tekrarları fark edip sonuçları saklayan güçlü bir problem çözme yaklaşımıdır. Temel fikir; büyük bir problemi daha küçük alt problemlere ayırmak, her alt problemi doğru biçimde temsil eden durumları tanımlamak ve hesaplanan sonuçları yeniden kullanmaktır.]]></summary></entry><entry><title type="html">Diziden Kuyruğa: Veri Yapılarıyla Akıllı Problem Çözme</title><link href="https://sonsuzus.github.io/posts/diziden-kuyruga-veri-yapilariyla-akilli-problem-cozme/" rel="alternate" type="text/html" title="Diziden Kuyruğa: Veri Yapılarıyla Akıllı Problem Çözme" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/diziden-kuyruga-veri-yapilariyla-akilli-problem-cozme</id><content type="html" xml:base="https://sonsuzus.github.io/posts/diziden-kuyruga-veri-yapilariyla-akilli-problem-cozme/"><![CDATA[<p>Bir algoritmanın başarısı yalnızca doğru sonucu üretmesine değil, bunu ne kadar hızlı ve az bellek kullanarak yaptığına da bağlıdır. Diziler, bağlı listeler, yığınlar ve kuyruklar bu noktada programcının alet çantasındaki temel araçlardır. Doğru veri yapısını seçmek bazen yüzlerce satır kod yazmaktan daha değerlidir; yanlış seçim ise hızlı görünen bir çözümü kaplumbağaya çevirebilir.
``</p>

<h2 id="veri-yapısı-seçimi-neden-önemlidir">Veri yapısı seçimi neden önemlidir?</h2>

<p>Bir veri yapısını değerlendirirken genellikle zaman ve alan karmaşıklığına bakarız. Bir algoritmanın çalışma süresi, girdi büyüklüğü $n$ arttıkça değişir. Örneğin bir dizinin bütün elemanlarını gezmek $T(n)=an+b$ biçiminde modellenebilir ve bu işlem $O(n)$ karmaşıklığındadır.</p>

<p>Sık kullanılan işlemlerin genel karşılaştırması şöyledir:</p>

<table>
  <thead>
    <tr>
      <th>Veri yapısı</th>
      <th style="text-align: right">İndeksle erişim</th>
      <th style="text-align: right">Başa ekleme</th>
      <th style="text-align: right">Sona ekleme</th>
      <th>Tipik kullanım</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Dizi</td>
      <td style="text-align: right">$O(1)$</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(1)$ amortize</td>
      <td>Hızlı erişim, sıralı veriler</td>
    </tr>
    <tr>
      <td>Bağlı liste</td>
      <td style="text-align: right">$O(n)$</td>
      <td style="text-align: right">$O(1)$</td>
      <td style="text-align: right">$O(1)$*</td>
      <td>Sık ekleme ve silme</td>
    </tr>
    <tr>
      <td>Yığın</td>
      <td style="text-align: right">Yalnızca tepe</td>
      <td style="text-align: right">$O(1)$</td>
      <td style="text-align: right">—</td>
      <td>Geri alma, parantez kontrolü</td>
    </tr>
    <tr>
      <td>Kuyruk</td>
      <td style="text-align: right">Yalnızca ön</td>
      <td style="text-align: right">—</td>
      <td style="text-align: right">$O(1)$</td>
      <td>İş sıralama, genişlik öncelikli arama</td>
    </tr>
  </tbody>
</table>

<p>* Son düğümün adresi ayrıca tutuluyorsa.</p>

<h2 id="diziler-hızlı-erişimin-yıldızı">Diziler: Hızlı erişimin yıldızı</h2>

<p>Diziler elemanları mantıksal olarak yan yana saklar. Bu nedenle $i$ numaralı elemana erişmek için başlangıç adresine belirli bir uzaklık eklemek yeterlidir:</p>

\[adres(i)=başlangıç+i\times eleman\_boyutu\]

<p>Bu hesaplama sabit zamanda yapılır. Sık sık “beşinci eleman nedir?” diye sorulan problemlerde dizi güçlü bir seçimdir. Buna karşılık dizinin başına eleman eklemek, diğer elemanların kaydırılmasını gerektirdiği için pahalıdır.</p>

<p>Örneğin iki işaretçi tekniği, sıralı bir dizide hedef toplamı ararken iç içe döngü ihtiyacını ortadan kaldırır:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">hedef_cifti_bul</span><span class="p">(</span><span class="n">sayilar</span><span class="p">,</span> <span class="n">hedef</span><span class="p">):</span>
    <span class="n">sol</span><span class="p">,</span> <span class="n">sag</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">sayilar</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>

    <span class="k">while</span> <span class="n">sol</span> <span class="o">&lt;</span> <span class="n">sag</span><span class="p">:</span>
        <span class="n">toplam</span> <span class="o">=</span> <span class="n">sayilar</span><span class="p">[</span><span class="n">sol</span><span class="p">]</span> <span class="o">+</span> <span class="n">sayilar</span><span class="p">[</span><span class="n">sag</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">toplam</span> <span class="o">==</span> <span class="n">hedef</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">sayilar</span><span class="p">[</span><span class="n">sol</span><span class="p">],</span> <span class="n">sayilar</span><span class="p">[</span><span class="n">sag</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">toplam</span> <span class="o">&lt;</span> <span class="n">hedef</span><span class="p">:</span>
            <span class="n">sol</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">sag</span> <span class="o">-=</span> <span class="mi">1</span>

    <span class="k">return</span> <span class="bp">None</span>
</code></pre></div></div>

<p>Bu kod, sıralı dizinin iki ucundan ilerleyerek uygun çifti bulur. Kaba kuvvet yaklaşımının $O(n^2)$ maliyetini $O(n)$ seviyesine indirir.</p>

<h2 id="bağlı-listeler-esnek-vagonlar">Bağlı listeler: Esnek vagonlar</h2>

<p>Bağlı listeyi, her vagonun bir sonrakinin adresini taşıdığı tren gibi düşünebiliriz. Düğümler bellekte yan yana olmak zorunda değildir. Bir düğüm biliniyorsa araya yeni düğüm eklemek yalnızca bağlantıları değiştirdiği için $O(1)$ sürer.</p>

<p>Ancak belirli bir indekse doğrudan sıçranamaz; düğümler baştan itibaren gezilmelidir. Dolayısıyla rastgele erişimin yoğun olduğu bir problemde bağlı liste kullanmak, kitabın her sayfasını birinciden başlayarak aramaya benzer.</p>

<p>Bağlı listeler; müzik listeleri, tarayıcı geçmişi ve sık ekleme-silme yapılan koleksiyonlar için uygundur. Çift bağlı listeler ileri ve geri hareket sağlarken fazladan işaretçi belleği tüketir.</p>

<h2 id="yığın-son-gelen-ilk-çıkar">Yığın: Son gelen ilk çıkar</h2>

<p>Yığın, $LIFO$ yani “son giren ilk çıkar” ilkesini uygular. Tabakları üst üste koyduğunuzu düşünün: İlk olarak en üstteki tabağı alırsınız. Fonksiyon çağrıları, geri alma işlemleri ve sözdizimi denetimi yığının doğal kullanım alanlarıdır.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">parantezler_dengeli_mi</span><span class="p">(</span><span class="n">metin</span><span class="p">):</span>
    <span class="n">yigin</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">eslesme</span> <span class="o">=</span> <span class="p">{</span><span class="sh">'</span><span class="s">)</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">(</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">]</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">[</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">}</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">{</span><span class="sh">'</span><span class="p">}</span>

    <span class="k">for</span> <span class="n">karakter</span> <span class="ow">in</span> <span class="n">metin</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">karakter</span> <span class="ow">in</span> <span class="sh">'</span><span class="s">([{</span><span class="sh">'</span><span class="p">:</span>
            <span class="n">yigin</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">karakter</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">karakter</span> <span class="ow">in</span> <span class="n">eslesme</span><span class="p">:</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">yigin</span> <span class="ow">or</span> <span class="n">yigin</span><span class="p">.</span><span class="nf">pop</span><span class="p">()</span> <span class="o">!=</span> <span class="n">eslesme</span><span class="p">[</span><span class="n">karakter</span><span class="p">]:</span>
                <span class="k">return</span> <span class="bp">False</span>

    <span class="k">return</span> <span class="nf">len</span><span class="p">(</span><span class="n">yigin</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</code></pre></div></div>

<p>Burada açılan parantezler yığına eklenir; kapanış görüldüğünde en son açılan parantez çıkarılır. Her karakter bir kez işlendiğinden süre $O(n)$, en kötü durumdaki bellek kullanımı da $O(n)$ olur.</p>

<h2 id="kuyruk-sıraya-kaynak-yapmak-yok">Kuyruk: Sıraya kaynak yapmak yok</h2>

<p>Kuyruk $FIFO$, yani “ilk giren ilk çıkar” düzeninde çalışır. Yazdırma görevleri, mesaj sistemleri ve genişlik öncelikli arama bu yapıyı kullanır. Bir grafikte başlangıç düğümüne en yakın noktaları önce keşfetmek istiyorsak kuyruğa komşuları ekler, önden sırayla çıkarırız.</p>

<p>Python’da listenin başından <code class="language-plaintext highlighter-rouge">pop(0)</code> kullanmak $O(n)$ maliyetlidir. Bunun yerine <code class="language-plaintext highlighter-rouge">collections.deque</code> tercih edilmelidir; <code class="language-plaintext highlighter-rouge">append</code> ve <code class="language-plaintext highlighter-rouge">popleft</code> işlemleri $O(1)$ zamanda gerçekleşir.</p>

<h2 id="problem-çözerken-karar-reçetesi">Problem çözerken karar reçetesi</h2>

<p>Önce problemin baskın işlemini belirleyin: İndeksle erişim gerekiyorsa dizi, sık bağlantı değişikliği varsa bağlı liste, en son eklenene dönülecekse yığın, geliş sırası korunacaksa kuyruk düşünün. Ardından en kötü durumu analiz edin ve $n$ iki katına çıktığında maliyetin nasıl değiştiğini sorun. Veri yapısı seçimi ezber değil, problemin davranışını doğru okuma sanatıdır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="veri yapıları" /><category term="algoritmalar" /><category term="python" /><summary type="html"><![CDATA[Bir algoritmanın başarısı yalnızca doğru sonucu üretmesine değil, bunu ne kadar hızlı ve az bellek kullanarak yaptığına da bağlıdır. Diziler, bağlı listeler, yığınlar ve kuyruklar bu noktada programcının alet çantasındaki temel araçlardır. Doğru veri yapısını seçmek bazen yüzlerce satır kod yazmaktan daha değerlidir; yanlış seçim ise hızlı görünen bir çözümü kaplumbağaya çevirebilir.]]></summary></entry><entry><title type="html">En Kısa Yol Arenası: Dijkstra, Bellman-Ford ve Floyd-Warshall</title><link href="https://sonsuzus.github.io/posts/en-kisa-yol-arenasi-dijkstra-bellman-ford-ve-floyd-warshall/" rel="alternate" type="text/html" title="En Kısa Yol Arenası: Dijkstra, Bellman-Ford ve Floyd-Warshall" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/en-kisa-yol-arenasi-dijkstra-bellman-ford-ve-floyd-warshall</id><content type="html" xml:base="https://sonsuzus.github.io/posts/en-kisa-yol-arenasi-dijkstra-bellman-ford-ve-floyd-warshall/"><![CDATA[<p>Bir şehir haritasında en yakın kafeyi aramak, ağ paketlerini yönlendirmek veya bir oyundaki karaktere güvenli rota çizmek aynı temel soruya dayanır: Bir noktadan diğerine en düşük maliyetle nasıl gideriz? Dijkstra, Bellman-Ford ve Floyd-Warshall bu soruya farklı koşullarda cevap veren üç klasik algoritmadır. Ancak yanlış algoritmayı seçmek, navigasyon uygulamasını macera oyununa çevirebilir!</p>

<p>``</p>

<h2 id="problemin-matematiksel-modeli">Problemin matematiksel modeli</h2>

<p>Bir ağırlıklı grafı $G=(V,E)$ biçiminde tanımlayalım. Burada $V$ düğümleri, $E$ kenarları ve $w(u,v)$ ise $u$ ile $v$ arasındaki geçiş maliyetini gösterir. Bir $P$ yolunun toplam maliyeti:</p>

\[W(P)=\sum_{(u,v)\in P} w(u,v)\]

<p>şeklindedir. Amaç, kaynak $s$ ile hedef $t$ arasındaki yollar içinde maliyeti en küçük olanı bulmaktır:</p>

\[d(s,t)=\min_{P:s\leadsto t} W(P)\]

<p>Kenar ağırlıkları mesafe, süre, ücret veya enerji tüketimi olabilir. Kritik ayrım, negatif ağırlıkların bulunup bulunmamasıdır. Örneğin bir finans modelinde kazanç, negatif maliyet olarak temsil edilebilir.</p>

<h2 id="üç-algoritma-üç-farklı-yaklaşım">Üç algoritma, üç farklı yaklaşım</h2>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>Dijkstra</th>
      <th>Bellman-Ford</th>
      <th>Floyd-Warshall</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Hesaplanan yollar</td>
      <td>Tek kaynaktan tüm düğümlere</td>
      <td>Tek kaynaktan tüm düğümlere</td>
      <td>Her düğüm çifti arasında</td>
    </tr>
    <tr>
      <td>Negatif kenar</td>
      <td>Desteklemez</td>
      <td>Destekler</td>
      <td>Destekler</td>
    </tr>
    <tr>
      <td>Negatif döngü tespiti</td>
      <td>Hayır</td>
      <td>Evet</td>
      <td>Evet</td>
    </tr>
    <tr>
      <td>Zaman karmaşıklığı</td>
      <td>$O((V+E)\log V)$</td>
      <td>$O(VE)$</td>
      <td>$O(V^3)$</td>
    </tr>
    <tr>
      <td>İdeal kullanım</td>
      <td>Büyük ve seyrek graflar</td>
      <td>Negatif maliyetli graflar</td>
      <td>Küçük veya yoğun graflar</td>
    </tr>
  </tbody>
</table>

<h3 id="dijkstra-hızlı-ama-seçici">Dijkstra: Hızlı ama seçici</h3>

<p>Dijkstra, başlangıç düğümünden erişilebilen en düşük geçici mesafeye sahip düğümü sürekli seçer. Açgözlü çalışır: Kesinleştirilen bir mesafeye tekrar dönmez. Bu karar yalnızca tüm ağırlıklar $w(u,v)\geq 0$ olduğunda güvenlidir.</p>

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

<span class="k">def</span> <span class="nf">dijkstra</span><span class="p">(</span><span class="n">graph</span><span class="p">,</span> <span class="n">source</span><span class="p">):</span>
    <span class="n">dist</span> <span class="o">=</span> <span class="p">{</span><span class="n">node</span><span class="p">:</span> <span class="nf">float</span><span class="p">(</span><span class="sh">'</span><span class="s">inf</span><span class="sh">'</span><span class="p">)</span> <span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">graph</span><span class="p">}</span>
    <span class="n">dist</span><span class="p">[</span><span class="n">source</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">queue</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="n">source</span><span class="p">)]</span>

    <span class="k">while</span> <span class="n">queue</span><span class="p">:</span>
        <span class="n">current</span><span class="p">,</span> <span class="n">node</span> <span class="o">=</span> <span class="n">heapq</span><span class="p">.</span><span class="nf">heappop</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">current</span> <span class="o">!=</span> <span class="n">dist</span><span class="p">[</span><span class="n">node</span><span class="p">]:</span>
            <span class="k">continue</span>
        <span class="k">for</span> <span class="n">neighbor</span><span class="p">,</span> <span class="n">weight</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="n">candidate</span> <span class="o">=</span> <span class="n">current</span> <span class="o">+</span> <span class="n">weight</span>
            <span class="k">if</span> <span class="n">candidate</span> <span class="o">&lt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]:</span>
                <span class="n">dist</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span> <span class="o">=</span> <span class="n">candidate</span>
                <span class="n">heapq</span><span class="p">.</span><span class="nf">heappush</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="p">(</span><span class="n">candidate</span><span class="p">,</span> <span class="n">neighbor</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">dist</span>
</code></pre></div></div>

<p>Öncelik kuyruğu, sıradaki en yakın düğümü verimli biçimde seçer. Yol ağları ve internet yönlendirme senaryoları için genellikle en pratik tercihtir.</p>

<h3 id="bellman-ford-yavaş-ama-kuşkucu">Bellman-Ford: Yavaş ama kuşkucu</h3>

<table>
  <tbody>
    <tr>
      <td>Bellman-Ford bütün kenarları en fazla $</td>
      <td>V</td>
      <td>-1$ tur gevşetir. Gevşetme işlemi şu eşitsizliği kontrol eder:</td>
    </tr>
  </tbody>
</table>

\[d(v)&gt;d(u)+w(u,v)\]

<p>Koşul doğruysa $d(v)$ güncellenir. Bir tur daha yapıldığında hâlâ güncelleme gerçekleşiyorsa erişilebilir bir negatif döngü vardır.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">bellman_ford</span><span class="p">(</span><span class="n">vertices</span><span class="p">,</span> <span class="n">edges</span><span class="p">,</span> <span class="n">source</span><span class="p">):</span>
    <span class="n">dist</span> <span class="o">=</span> <span class="p">{</span><span class="n">v</span><span class="p">:</span> <span class="nf">float</span><span class="p">(</span><span class="sh">'</span><span class="s">inf</span><span class="sh">'</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">vertices</span><span class="p">}</span>
    <span class="n">dist</span><span class="p">[</span><span class="n">source</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">vertices</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
        <span class="n">changed</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="k">for</span> <span class="n">u</span><span class="p">,</span> <span class="n">v</span><span class="p">,</span> <span class="n">weight</span> <span class="ow">in</span> <span class="n">edges</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">dist</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">weight</span> <span class="o">&lt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">v</span><span class="p">]:</span>
                <span class="n">dist</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">=</span> <span class="n">dist</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">weight</span>
                <span class="n">changed</span> <span class="o">=</span> <span class="bp">True</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">changed</span><span class="p">:</span>
            <span class="k">break</span>

    <span class="k">for</span> <span class="n">u</span><span class="p">,</span> <span class="n">v</span><span class="p">,</span> <span class="n">weight</span> <span class="ow">in</span> <span class="n">edges</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">dist</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">+</span> <span class="n">weight</span> <span class="o">&lt;</span> <span class="n">dist</span><span class="p">[</span><span class="n">v</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">Negatif döngü bulundu</span><span class="sh">'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">dist</span>
</code></pre></div></div>

<p>Döviz arbitrajı ve maliyetlerin negatif olabildiği modeller, ek işlem süresine rağmen Bellman-Ford için uygundur.</p>

<h3 id="floyd-warshall-herkes-herkese-karşı">Floyd-Warshall: Herkes herkese karşı</h3>

<p>Floyd-Warshall, dinamik programlama kullanır. $k$ ara düğümüne izin verildiğinde güncelleme kuralı şöyledir:</p>

\[D_{ij}=\min(D_{ij},D_{ik}+D_{kj})\]

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">floyd_warshall</span><span class="p">(</span><span class="n">dist</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">dist</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">n</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="n">n</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
                <span class="n">dist</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="nf">min</span><span class="p">(</span><span class="n">dist</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="n">dist</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">dist</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="k">return</span> <span class="n">dist</span>
</code></pre></div></div>

<p>Sonuç matrisindeki herhangi bir $D_{ii}&lt;0$ değeri negatif döngüyü gösterir. Bellek maliyeti $O(V^2)$ olduğundan devasa graflarda pahalıdır; fakat tüm çiftlerin sorgulanacağı küçük ulaşım veya ilişki ağlarında son derece kullanışlıdır.</p>

<h2 id="hangisini-seçmeliyiz">Hangisini seçmeliyiz?</h2>

<p>Negatif ağırlık yoksa ve tek kaynak önemliyse <strong>Dijkstra</strong>, negatif kenarlar varsa <strong>Bellman-Ford</strong>, bütün düğüm çiftleri gerekiyorsa <strong>Floyd-Warshall</strong> seçilmelidir. Özetle Dijkstra sprinter, Bellman-Ford temkinli denetçi, Floyd-Warshall ise herkesin rotasını önceden çıkaran takıntılı haritacıdır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="graf algoritmaları" /><category term="en kısa yol" /><category term="Python" /><summary type="html"><![CDATA[Bir şehir haritasında en yakın kafeyi aramak, ağ paketlerini yönlendirmek veya bir oyundaki karaktere güvenli rota çizmek aynı temel soruya dayanır: Bir noktadan diğerine en düşük maliyetle nasıl gideriz? Dijkstra, Bellman-Ford ve Floyd-Warshall bu soruya farklı koşullarda cevap veren üç klasik algoritmadır. Ancak yanlış algoritmayı seçmek, navigasyon uygulamasını macera oyununa çevirebilir!]]></summary></entry><entry><title type="html">Graflar ve Ağaçlarda DFS ve BFS: Derine mi, Genişe mi?</title><link href="https://sonsuzus.github.io/posts/graflar-ve-agaclarda-dfs-ve-bfs-derine-mi-genise-mi/" rel="alternate" type="text/html" title="Graflar ve Ağaçlarda DFS ve BFS: Derine mi, Genişe mi?" /><published>2026-08-05T00:00:00+00:00</published><updated>2026-08-05T00:00:00+00:00</updated><id>https://sonsuzus.github.io/posts/graflar-ve-agaclarda-dfs-ve-bfs-derine-mi-genise-mi</id><content type="html" xml:base="https://sonsuzus.github.io/posts/graflar-ve-agaclarda-dfs-ve-bfs-derine-mi-genise-mi/"><![CDATA[<p>Bir labirentte çıkış aradığınızı düşünün: Bir yolu sonuna kadar takip edip çıkmazda geri mi dönersiniz, yoksa önce size bir adım uzaklıktaki bütün yolları mı incelersiniz? İlk yaklaşım <strong>derinlik öncelikli arama (DFS)</strong>, ikincisi ise <strong>genişlik öncelikli arama (BFS)</strong> mantığıdır. Bu iki kapsamlı arama tekniği; ağaçların, grafların ve birçok yapay zekâ probleminin temel araçlarıdır.
``</p>
<h2 id="önce-yapıyı-tanıyalım">Önce yapıyı tanıyalım</h2>

<p>Bir <strong>graf</strong>, düğümlerden ve bu düğümleri bağlayan kenarlardan oluşur: $G=(V,E)$. Burada $V$ düğüm, $E$ ise kenar kümesidir. Ağaç da çevrim içermeyen, bağlantılı özel bir graftır. Bir ağaçta iki düğüm arasında yalnızca tek bir basit yol bulunurken genel bir grafta birden fazla yol ve çevrim olabilir.</p>

<p>Bu ayrım uygulamada önemlidir. Ağaç üzerinde aşağı doğru ilerlemek çoğunlukla güvenlidir; ancak çevrimli bir grafta ziyaret edilen düğümler kaydedilmezse algoritma <code class="language-plaintext highlighter-rouge">A → B → A → B</code> döngüsünde sonsuza kadar kalabilir.</p>

<h2 id="derinlik-öncelikli-arama-yolun-sonunu-gör">Derinlik öncelikli arama: Yolun sonunu gör</h2>

<p>DFS, başlangıç düğümünden hareket ederek erişebildiği en derin noktaya ilerler. Gidecek yeni komşu kalmadığında geri izleme yapar. Bu davranış doğal olarak bir <strong>yığın (stack)</strong> ile modellenir. Özyinelemeli fonksiyonlarda çağrı yığını aynı görevi üstlenir.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">graph</span><span class="p">,</span> <span class="n">start</span><span class="p">):</span>
    <span class="n">visited</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>
    <span class="n">stack</span> <span class="o">=</span> <span class="p">[</span><span class="n">start</span><span class="p">]</span>

    <span class="k">while</span> <span class="n">stack</span><span class="p">:</span>
        <span class="n">node</span> <span class="o">=</span> <span class="n">stack</span><span class="p">.</span><span class="nf">pop</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
            <span class="k">continue</span>

        <span class="n">visited</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>  <span class="c1"># Düğüm burada işlenir.
</span>
        <span class="c1"># Ters ekleme, soldaki komşunun önce gezilmesini sağlar.
</span>        <span class="k">for</span> <span class="n">neighbor</span> <span class="ow">in</span> <span class="nf">reversed</span><span class="p">(</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">neighbor</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
                <span class="n">stack</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">neighbor</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">visited</span>
</code></pre></div></div>

<p>Kod, sıradaki düğümü yığının sonundan alır. Komşular yığına eklendiği için algoritma diğer seçeneklere geçmeden önce bir dal boyunca derinleşir. DFS; çevrim tespiti, topolojik sıralama, bağlı bileşen bulma ve labirent çözme gibi problemlerde kullanışlıdır.</p>

<h2 id="genişlik-öncelikli-arama-katman-katman-ilerle">Genişlik öncelikli arama: Katman katman ilerle</h2>

<p>BFS, önce başlangıç düğümüne bir kenar uzaklıktaki düğümleri, ardından iki kenar uzaklıktakileri inceler. Bu nedenle <strong>kuyruk (queue)</strong> kullanır. Ağırlıksız bir grafta en kısa yolu bulabilmesinin nedeni de düğümleri artan uzaklık sırasıyla ziyaret etmesidir.</p>

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

<span class="k">def</span> <span class="nf">bfs</span><span class="p">(</span><span class="n">graph</span><span class="p">,</span> <span class="n">start</span><span class="p">):</span>
    <span class="n">visited</span> <span class="o">=</span> <span class="p">{</span><span class="n">start</span><span class="p">}</span>
    <span class="n">queue</span> <span class="o">=</span> <span class="nf">deque</span><span class="p">([(</span><span class="n">start</span><span class="p">,</span> <span class="mi">0</span><span class="p">)])</span>

    <span class="k">while</span> <span class="n">queue</span><span class="p">:</span>
        <span class="n">node</span><span class="p">,</span> <span class="n">distance</span> <span class="o">=</span> <span class="n">queue</span><span class="p">.</span><span class="nf">popleft</span><span class="p">()</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">distance</span><span class="p">)</span>  <span class="c1"># Başlangıca olan uzaklık.
</span>
        <span class="k">for</span> <span class="n">neighbor</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">neighbor</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
                <span class="n">visited</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">neighbor</span><span class="p">)</span>
                <span class="n">queue</span><span class="p">.</span><span class="nf">append</span><span class="p">((</span><span class="n">neighbor</span><span class="p">,</span> <span class="n">distance</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>

    <span class="k">return</span> <span class="n">visited</span>
</code></pre></div></div>

<p>Bir düğüm kuyruğa eklenirken ziyaret edilmiş sayılır. Böylece aynı düğümün farklı komşular tarafından kuyruğa defalarca eklenmesi önlenir.</p>

<h2 id="dfs-ve-bfs-karşılaştırması">DFS ve BFS karşılaştırması</h2>

<table>
  <thead>
    <tr>
      <th>Özellik</th>
      <th>DFS</th>
      <th>BFS</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Temel veri yapısı</td>
      <td>Yığın</td>
      <td>Kuyruk</td>
    </tr>
    <tr>
      <td>İlerleme biçimi</td>
      <td>Dal boyunca derine</td>
      <td>Seviye seviye</td>
    </tr>
    <tr>
      <td>Ağırlıksız en kısa yol</td>
      <td>Garanti etmez</td>
      <td>Garanti eder</td>
    </tr>
    <tr>
      <td>Bellek davranışı</td>
      <td>Derinliğe bağlıdır</td>
      <td>Geniş seviyelerde büyür</td>
    </tr>
    <tr>
      <td>Tipik kullanım</td>
      <td>Çevrim, bileşen, topolojik sıralama</td>
      <td>En kısa yol, seviye analizi</td>
    </tr>
    <tr>
      <td>Tamlık</td>
      <td>Sonsuz derinlikte sorun yaşayabilir</td>
      <td>Sonlu dallanmada çözümü bulur</td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td>Komşuluk listesi kullanılan bir grafta her düğüm ve kenar en fazla sabit sayıda işlendiği için iki algoritmanın zaman karmaşıklığı da $O(</td>
      <td>V</td>
      <td>+</td>
      <td>E</td>
      <td>)$ olur. Bellek karmaşıklığı genel durumda $O(</td>
      <td>V</td>
      <td>)$ düzeyindedir.</td>
    </tr>
  </tbody>
</table>

<p>Arama ağacının dallanma katsayısı $b$, çözüm derinliği $d$ ise BFS yaklaşık $1+b+b^2+…+b^d$ düğüm üretir; baskın terim nedeniyle bu miktar $O(b^d)$ kabul edilir. DFS ise aynı anda çoğunlukla tek yol ile alternatifleri tuttuğundan bellekte daha tutumlu olabilir.</p>

<h2 id="hangisini-seçmeliyiz">Hangisini seçmeliyiz?</h2>

<p>Hedefin başlangıca yakın olduğu veya ağırlıksız en kısa yolun istendiği durumlarda BFS güçlü seçimdir. Arama alanı çok genişse, çözümün derinde olduğu düşünülüyorsa ya da bütün yapısal ilişkiler araştırılacaksa DFS daha uygun olabilir. Ancak kenarlar farklı ağırlıklara sahipse BFS yerine Dijkstra gibi algoritmalar gerekir.</p>

<p>Özetle DFS meraklı bir mağara kâşifi gibi tek tünele dalar; BFS ise düzenli bir arama ekibi gibi bütün katmanı tarar. Doğru algoritma, yalnızca grafın biçimine değil, aradığınız cevabın türüne ve kullanabileceğiniz belleğe de bağlıdır.</p>]]></content><author><name>Sonsuz Us</name></author><category term="Bilgi" /><category term="DFS" /><category term="BFS" /><category term="graf algoritmaları" /><summary type="html"><![CDATA[Bir labirentte çıkış aradığınızı düşünün: Bir yolu sonuna kadar takip edip çıkmazda geri mi dönersiniz, yoksa önce size bir adım uzaklıktaki bütün yolları mı incelersiniz? İlk yaklaşım derinlik öncelikli arama (DFS), ikincisi ise genişlik öncelikli arama (BFS) mantığıdır. Bu iki kapsamlı arama tekniği; ağaçların, grafların ve birçok yapay zekâ probleminin temel araçlarıdır.]]></summary></entry></feed>