2  Algoritmalar ve Hesaplama

Problem vs algoritma, tümevarımla doğruluk, asimptotik verimlilik ve word RAM modeli

NotBölüm bilgisi

2.1 Bu Derste Ne Var?

6.006’nın ilk dersi, kursun neyi öğrettiğini tanımlar: hesaplamalı problemleri çözmeyi, çözümün doğru olduğunu ispatlamayı ve verimli olduğunu savunmayı. Jason Ku, kodlamadan çok düşünmeye ve iletişime odaklandığımızı vurgular — bu derste yazacağınız ispat, çoğu zaman yazacağınız koddan fazladır.

Üç temel kavram bu derste yan yana gelir:

  1. Problem vs Algoritma — bir problem girdi-çıktı ilişkisidir; bir algoritma o ilişkiyi hesaplayan bir prosedürdür.
  2. Tümevarımla doğruluk — bir algoritmanın her girdi için doğru çalıştığını sonlu bir argümanla ispatlama.
  3. Asimptotik verimlilik + hesaplama modeli — zamanı saniyeyle değil, temel işlem sayısıyla ölçme; word RAM modeli.

“Really what the course is about is teaching you to solve computational problems.” — Ku, 1:00

flowchart TD
    A["Ders 1: Algoritmalar ve Hesaplama"] --> B["Problem vs Algoritma"]
    A --> C["Doğruluk → Tümevarım"]
    A --> D["Verimlilik → Asimptotik"]
    B --> B1["Problem = ikili ilişki<br/>(girdi → çıktı)"]
    B --> B2["Algoritma = prosedür<br/>(fonksiyon, sabit boyutlu)"]
    C --> C1["Hipotez P(K)"]
    C --> C2["Temel durum K = 0<br/>(boş-doğru)"]
    C --> C3["Tümevarım adımı<br/>P(K′) → P(K′+1)"]
    D --> D1["O · Θ · Ω sınırları"]
    D --> D2["1 ≪ log n ≪ n ≪ n log n ≪ n² ≪ 2ⁿ"]
    D --> D3["word RAM modeli<br/>(sabit-zaman erişim, word)"]

    classDef root fill:#fef3c7,stroke:#b45309,stroke-width:3px,color:#1f2937
    classDef branch fill:#f3f4f6,stroke:#374151,stroke-width:2px,color:#1f2937
    classDef leaf fill:#ffffff,stroke:#9ca3af,stroke-width:1px,color:#1f2937
    class A root
    class B,C,D branch
    class B1,B2,C1,C2,C3,D1,D2,D3 leaf
Şekil 2.1: Ders 1’in kavram haritası: problem vs algoritma ikili ilişkisinden, tümevarımla doğruluğa, oradan asimptotik verimlilik + word RAM hesaplama modeline.
İpucuBuilder Notu — ML ve OMSCS Köprüleri

Bu ders ML değil, mühendislik temeli kurar. Phase 1 ve ileri hedeflerle köprüler:

  • Geriye → Python (Phase 1): Ku, Python’ın list, set, dict yapılarının “bu modelde olmadığını” söyler (42:28). Python’da bedava görünen işlemlerin gerçek bir zaman maliyeti var — bu kurs o maliyeti görünür kılar.
  • Geriye → Calculus (Phase 1): asimptotik mertebeler (\(\log\), lineer, polinom, üstel büyüme) — fonksiyonların büyüme hızını karşılaştırma.
  • İleriye → OMSCS CS 6515 (Graduate Algorithms): bu dersin formal devamı; aynı dil (\(O/\Theta/\Omega\), DP, çizge).
  • İleriye → kompetitif programlama ve sistem tasarımı: “büyük N’de hangi karmaşıklık geçer” sezgisi; routing, cache, scheduler tasarımı.

Tek cümle: Bu ders, “hızlı kod” ile “hızlı algoritma” arasındaki farkı ve bunu nasıl ispatlayacağını öğretir.

2.2 Hesaplamalı Problem Nedir?

Ku derse bir soruyla başlar: “What is a problem? What is an algorithm?” — ve cevabı söylemek yerine sınıfı oraya taşır (Sokratik üslup). Kursun dört hedefi vardır:

  1. Hesaplamalı problemi çöz.
  2. Çözümün doğru olduğunu ispatla.
  3. Çözümün verimli olduğunu savun.
  4. Bunların hepsini başkalarına iletişimle aktar.

Ku’ya göre bu sınıfı diğer kodlama derslerinden ayıran şey budur: sadece bilgisayara değil, insanlara doğruluğu kanıtlamak. Bu yüzden derste bol bol yazı yazılır.

“we really concentrate on being able to prove that the things you’re doing are correct and better than other things, and being able to communicate those ideas to others, and not just to a computer” — Ku, 2:40

2.3 Problem = Girdi-Çıktı İlişkisi

Soyut tanım: bir problem, girdiler kümesi ile çıktılar kümesi arasında bir ikili ilişkidir (binary relation). Her girdi için hangi çıktıların doğru olduğunu belirtir — bu, girdiler ile çıktılar arasında iki-parçalı (bipartite) bir çizge gibidir (Şekil 2.2). Bir girdinin birden çok doğru çıktısı olabilir: örneğin “dizide 5 değerini içeren bir indeks ver” probleminde birden çok 5 varsa, o indekslerin herhangi biri doğrudur.

Pratikte bir problemi tek tek girdi-çıktı çiftleriyle değil, bir yüklem (predicate) ile tanımlarız: bir girdi-çıktı çifti verildiğinde, çıktının doğru olup olmadığını kontrol edebiliriz. Bu, sonsuz sayıda girdiyi sonlu bir kuralla ifade etmenin yoludur.

“what a problem is is a binary relation between these inputs and outputs.” — Ku, 3:48

Çalışılan Örnek — Doğum Günü Problemi (kurulum): Bir sınıftaki öğrenciler arasında, herhangi iki öğrencinin aynı doğum anına (gün + yıl + saat) sahip olup olmadığını soralım. Ku, algoritmanın yalnızca bu sınıf için değil, keyfi boyutta herhangi bir girdi için çalışmasını ister — 300 öğrenci de olabilir, bir milyar öğrenci de. İşte bu yüzden “sabit boyutlu” bir makine ile “keyfi boyutlu” girdiyi işlemek zorundayız.

Şekil 2.2: Problem bir ikili ilişkidir: sol sütun girdiler (örnek diziler), sağ sütun olası çıktılar (5’in indeksi). Kenarlar geçerli (girdi → çıktı) çiftleridir. [7, 5, 2] için tek doğru çıktı (indeks 1), [3, 8, 4] için “5 yok”; ama [5, 9, 5, 1] için iki geçerli çıktı vardır (indeks 0 ve indeks 2, amber vurgulu) — bir girdinin birden çok doğru cevabı olabilir, yani problem fonksiyon olmak zorunda değildir.

2.4 Algoritma Nedir?

Bir algoritma, problemin tersine, çıktıları önceden bilmez — o bir prosedürdür: sabit boyutlu bir makine ya da tarif ki, kendisine bir girdi verildiğinde bir çıktı üretir. Matematiksel olarak algoritma bir fonksiyondur: her girdiyi tek bir çıktıya eşler ve o çıktı, problemin tanımına göre doğru olmalıdır.

Kritik gerilim şudur: girdi keyfi büyüklükte olabilir (bir milyar öğrenci), ama algoritmamız sabit boyutludur. Sabit boyutlu bir kod, keyfi büyük girdiyi işleyebilmek için döngü kurmalı veya özyineleme (recursion) yapmalıdır — aynı kod satırlarını tekrar tekrar çalıştırarak. Bu gözlem, neden bir sonraki adımda tümevarıma ihtiyaç duyacağımızın da habercisidir.

“An algorithm is some kind of function that takes these inputs, maps it to a single output, and that output better be correct based on our problem.” — Ku, 10:19

“So it’s just a procedure. You can think of it as like a recipe.” — Ku, 14:56

2.5 Doğum Günü Algoritması

Ku, bir öğrenciden algoritma önerisi alır ve onu formalize eder. Algoritma dört adımdan oluşur (somut bir izi için Şekil 2.3):

  1. Bir kayıt (record) tut.
  2. Öğrencileri bir sırayla görüşmeye al (interview).
  3. Her görüşmede: doğum günü kayıtta var mı kontrol et → varsa bir çift döndür; yoksa öğrenciyi kayda ekle.
  4. Herkes bittiğinde eşleşme bulunamadıysa “eşleşme yok” döndür.

“Maintain a record. Interview students in some order… check if birthday in record… return pair… Otherwise, add a new student to record.” — Ku, 12:11

Bu, problem setlerinde beklenen tarif seviyesinde açıklamadır: bir bilgisayara değil, sınıftaki bir arkadaşına anlatabileceğin netlikte sözel bir tanım.

“if you said this algorithm to any of your friends in this class… they would at least understand what it is that you’re doing.” — Ku, 13:46

Şimdi elimizde bir algoritma var. Ama bu yeterli değil: Ku’nun dört hedefinden ikisi henüz eksik — bu algoritmanın doğru ve verimli olduğunu göstermek. Sonraki iki bölüm tam olarak bunu yapar.

Şekil 2.3: Doğum günü algoritmasının adım-adım izi. Öğrenciler sırayla görüşülür; her satır bir görüşme adımıdır ve o ana kadar record sözlüğünde biriken doğum günlerini kutucuklarla gösterir (kutuda gün + ilk gören öğrenci). Eşleşme bulunana kadar her adımda kayıt bir hücre büyür (slate kutular, kesik çizgili ‘+ kayıt’ = yeni eklenen). Adım 5’te Can’ın doğum günü (03-14) daha önce Ali’de görüldüğü için çarpışma olur: çakışan önceki kayıt amber çerçeveyle, aktif sorgu dolu amber kutuyla vurgulanır (‘EŞLEŞME!’) ve algoritma orada durur — bu yüzden iz yalnızca 5 adım. record sabit-zamanlı sözlük araması sayesinde toplam maliyet \(O(n)\).
İpucuBuilder Notu — Çarpışma Tespiti ve Hash Fonksiyonları

Doğum günü algoritmasının “kayıt”ı aslında bir hash kümesidir (Python dict/set). “Doğum günü kayıtta var mı?” kontrolünün \(O(1)\) olması, hash fonksiyonunun anahtarı sabit zamanda bir kovaya eşlemesinden gelir — bu, Ders 4’ün (Hashing) konusu.

  • Geriye → çarpışma matematiği: Doğum günü paradoksu (sürpriz biçimde az sayıda öğrencide eşleşme olasılığının yükselmesi), hash tablolarındaki çarpışma analizinin (yük faktörü, beklenen çarpışma sayısı) ta kendisidir. Phase 1 Stat 110 olasılık dersiyle doğrudan köprü.
  • İleriye → feature hashing (ML): Yüksek-kardinaliteli kategorik öznitelikleri sabit boyutlu bir vektöre indirgeyen “hashing trick”, çarpışmaları bilerek tolere eder — kabul edilebilir çarpışma oranını yine bu doğum günü matematiği sınırlar.
  • İleriye → embedding lookup: Bir token → indeks → embedding satırı erişimi, bu kayıt sözlüğünün ölçeklenmiş hâlidir: anahtar→değer \(O(1)\) erişim. Karpathy’nin makemore serisinde embedding tablosu tam olarak budur.

2.6 Tümevarımla Doğruluk İspatı

Dört öğrenci için algoritmanın doğruluğunu elle deneyerek gösterebilirdik. Ama 300 (ya da bir milyar) öğrenci için tek tek denemek imkânsız. Sabit boyutlu bir kodun keyfi büyük bir girdide doğru çalıştığını göstermenin yolu tümevarımdır (induction) — bu yüzden 6.006 öncesi bir ispat/ayrık matematik dersi şarttır.

“Induction, right? … we write a constant sized piece of code that can take on any arbitrarily large size input.” — Ku, 16:23

Çalışılan Örnek — Doğum Günü Algoritmasının Doğruluğu

Tümevarımı kurmak için üç parça gerekir: hipotez, temel durum, tümevarım adımı. Bu üç parça Şekil 2.4 içinde bir arada görülür.

1) Tümevarım hipotezi (P(K)): İlk K öğrenci bir eşleşme içeriyorsa, algoritma K+1’inci öğrenciyi görüşmeden önce bir çift döndürmüştür.

“If first K students contain a match, algorithm returns a match before interviewing student K plus 1.” — Ku, 19:29

2) Temel durum (K = 0): Hiç öğrenci görüşülmeden önce hiçbir iş yapılmamıştır. İlk 0 öğrenci bir eşleşme içeremez, dolayısıyla hipotez boş-doğru (vacuously true) olarak sağlanır. Ku, en kolay temel durumun 2 veya 1 değil, 0 olduğunu vurgular.

“After interviewing 0 students, I haven’t done any work… the first 0 can’t have a match.” — Ku, 21:21

3) Tümevarım adımı: P(K′) doğru varsayılır, P(K′+1) gösterilir. İki durum vardır:

  • Durum A: İlk K′ öğrenci zaten bir eşleşme içeriyorsa → tümevarım hipotezi gereği algoritma çoktan doğru çıktıyı döndürmüştür.
  • Durum B: İlk K′ öğrenci eşleşme içermiyorsa ve K′+1’inci öğrenci görüşülür → eğer ilk K′+1 öğrenci içinde bir eşleşme varsa, bu eşleşme zorunlu olarak K′+1’inci öğrenciyi içerir (aksi halde daha önce eşleşme olurdu). Algoritma yeni öğrencinin doğum gününü kayıtta arar; varsa çifti döndürür, yoksa öğrenciyi kayda ekler ve hipotez K′+1 için yeniden kurulur.

Sonuç: K = n alındığında, n öğrencinin tümü görüşüldükten sonra bir eşleşme varsa döndürülmüş olur; yoksa algoritma “eşleşme yok” döndürür. İkisi de doğrudur.

“OK, so that’s how we prove correctness.” — Ku, 24:52

Ku, bu seviyede bir formalliğin her zaman gerekmediğini, ama kalibrasyon için yeterli olduğunu belirtir:

“This is a little bit more formal than we would ask you to do… but it’s definitely sufficient.” — Ku, 24:57

Hedef çıta: bir başkası senin algoritmanı okuyup kodlayabilmeli.

“if you communicated to someone else taking this class what your algorithm was, they would be able to code it up and tell a stupid computer how to do that thing.” — Ku, 25:10

Şekil 2.4: Tümevarımla doğruluk ispatının iskeleti. Üst panel: ispat iki parçadan oluşur — temel durum P(0) (K = 0 için ifade boş, dolayısıyla doğru / vacuously true) ve tümevarım adımı P(K′) ⟹ P(K′+1) (hipotez doğruysa bir sonraki de doğru); ikisi birleşince her K ≥ 0 için P(K) doğru → algoritma doğru çalışır. Alt panel: adımın özü — ilk K′ öğrenci için P(K′) doğru kabul edilir (slate hipotez şeridi), sıraya yeni öğrenci K′+1 eklenir (amber vurgu). İlk K′+1 öğrenci içindeki eşleşme iki durumdan biridir: Durum A eşleşme ilk K′ içindedir (P(K′) ile zaten bulunmuştur), Durum B eşleşme K′+1’inci öğrenciyi içerir (o öğrencinin doğum günü kayıtta zaten vardır). Her iki durumda da ilk K′+1 öğrenci için doğru ⟹ P(K′+1) sağlanır.

2.7 Verimlilik: Zamanı Değil, İşlemi Say

Algoritma doğru; şimdi verimli olduğunu savunmalıyız. Verimlilik yalnızca “ne kadar hızlı çalışıyor” değil, “diğer olası yaklaşımlara kıyasla ne kadar hızlı” demektir.

“Efficiency just means not only how fast does this algorithm run, but how fast does it compare to other possible ways of approaching this problem?” — Ku, 26:05

Akla ilk gelen ölçüm — kronometreyle süreyi ölçmek — işe yaramaz, çünkü süre donanıma bağlıdır: bir kol saati hesaplayıcısı ile IBM araştırma bilgisayarı aynı kodu çok farklı sürelerde çalıştırır. Makineyi denklemden çıkarmak için, her temel işlemin sabit zaman aldığını varsayar ve algoritmanın kaç temel işlem yaptığını sayarız.

“Instead, count fundamental operations.” — Ku, 27:57

Önemli nüans — girdi boyutu (n): Performans, girdinin boyutuna göre ölçülür. Ama \(n\) her zaman “eleman sayısı” değildir: \(n \times n\) bir dizi için girdi boyutu \(n^2\)’dir; bir çizge için genellikle düğüm sayısı artı kenar sayısıdır (\(V + E\)). Doğru \(n\)’i seçmek analizin ilk adımıdır.

2.8 Asimptotik Gösterim: O, Ω, Θ

İşlem sayısını bir fonksiyonla ifade ettikten sonra, onu asimptotik gösterimle sadeleştiririz — sabitleri ve düşük dereceli terimleri atıp, girdi büyüdükçe baskın olan büyüme mertebesine bakarız. Üç temel sınır vardır:

  • \(O\) (Big-O)üst sınır: “çalışma süresi en fazla bu mertebede büyür.”
  • \(\Omega\) (Omega)alt sınır: “çalışma süresi en az bu mertebede büyür.”
  • \(\Theta\) (Theta)sıkı sınır: hem üstten hem alttan aynı mertebeyle sınırlı (\(O\) ve \(\Omega\) birlikte).

“We have big O notation, which corresponds to upper bounds. We will have omega, which corresponds to lower bounds. And we have theta, which corresponds to both.” — Ku, 30:54

Ku, asimptotik gösterimin formal tanımının ertesi gün recitation’da (problem oturumu) işleneceğini söyler — bu kurs, ders ile problem oturumunu bilinçli olarak ayırır. Bizim için pratik sezgi şudur: \(\Theta\) “tam olarak bu hızda”, \(O\) “en kötü ihtimalle bu hızda” demektir.

2.9 Yaygın Çalışma Süresi Fonksiyonları

Ku, sık karşılaşılan büyüme mertebelerini tahtada en yavaştan en hızlıya sıralar:

\[1 \ll \log n \ll n \ll n \log n \ll n^2 \ll n^c \ll 2^n\]

Bu sıralamada polinom zamana kadar olan her şey (\(n^c\), \(c\) sabit) bu derste “verimli” sayılır. Üstel (\(2^n\)) ise pratikte kullanılamaz.

“this right here is what we mean by efficient, in this class, usually… generally what we mean is polynomial.” — Ku, 33:45

Ku, fonksiyonları \(n = 1000\)’e kadar çizdiğinde önemli bir sezgi verir: \(\log\) eğrisi o kadar yavaş büyür ki neredeyse sabit gibi görünür; üstel ise neredeyse dik bir çizgi gibi yukarı fırlar.

“So log is almost just as good as constant.” — Ku, 35:52

“So this is crap. This is really good.” — Ku, 36:07

Burada “crap” (berbat) üstel için, “really good” (çok iyi) ise polinom/altı içindir. Builder sezgisi: bir algoritmanın \(2^n\) mi yoksa \(n^c\) mi olduğu, \(n = 50\)’de bile dünyaları ayırır — biri evrenin yaşından uzun sürer, diğeri milisaniyeler.

Şekil 2.5: Asimptotik büyüme hiyerarşisi \(1 \ll \log n \ll n \ll n\log n \ll n^2 \ll 2^n\) iki panelde. Sol (lineer eksen, \(n \le 40\)): \(2^n\) daha \(n\approx 11\)’de görünür tavanı aşıp patlar; polinomlar (\(n\), \(n\log n\), \(n^2\)) tabanda okunur kalır. Sağ (logaritmik y): log ölçek tüm eğrileri birbirinden ayırır ve sıralamayı netleştirir — düz çizgiler farklı eğimlerle dizilir. Amber \(= 2^n\) (üstel, pratikte uygulanamaz); slate→amber tonlaması yavaştan hızlıya iyi büyümeyi gösterir.
İpucuBuilder Notu — Big-O ve Model Karmaşıklığı

Bu büyüme hiyerarşisi (Şekil 2.5) ML’de doğrudan karar verir:

  • İleriye → transformer dikkati (attention): Self-attention, dizinin uzunluğu \(n\)’de \(O(n^2)\)’dir — her token her tokenla eşleşir. \(n^2\) ile \(n \log n\) arasındaki uçurum, uzun-bağlam modellerinin neden alt-karesel dikkat aradığını (linear attention; FlashAttention’ın bellek tasarrufu) açıklar.
  • İleriye → neden gradient descent: Tüm parametre kombinasyonlarını denemek üstel (\(2^n\)) — imkânsız. Gradient descent her adımda yalnızca polinom iş yapar; “polinom = verimli” disiplini, kaba-kuvvet aramayı optimizasyonla değiştirmeyi zorunlu kılar.
  • Geriye → SVD rank kesimi (Phase 1 / 18.065): Bir \(m \times n\) matrisi rank-\(k\)’ya kesmek, çarpımı \(O(mn \cdot \min(m,n))\)’den \(O(mnk)\)’ya indirir. \(k\)’yı seçmek, “karşılayabildiğin büyüme mertebesi” için doğruluğu takas etmektir — bu dersin asimptotik disiplininin lineer cebire taşınmış hâli.

2.10 Hesaplama Modeli: word RAM

Temel işlemleri “saymak” için, bilgisayarın neyi sabit zamanda yapabildiğini tanımlayan bir hesaplama modeli gerekir. 6.006’nın kullandığı model word RAM’dir (word = kelime, RAM = random access memory).

“a machine called a word RAM, which we use for its theoretical brevity.” — Ku, 37:21

İki kavram:

  • Random access memory (RAM): Bellekteki herhangi bir konuma sabit zamanda rastgele erişebiliriz. Bellek, devasa bir bit dizisidir (1’ler ve 0’lar); CPU küçük bir miktar bilgiyi tutup işleyebilir ve bellekten istediği adresi getirip geri yazabilir.

“Random access memory– it means that I can randomly access different places in memory in constant time.” — Ku, 37:44

  • Word (kelime): CPU’nun bellekten bir seferde alıp üzerinde işlem yapabildiği sabit boyutlu bit öbeği. Bu, modelin “sabit zaman” varsayımının temel birimidir — bir sonraki bölümün konusu.

2.11 Bellek ve Word Boyutu

Modern bilgisayarlar belleği byte (8 bitlik öbekler) düzeyinde adresler — her 8 bit için bir adres vardır. CPU bir adres verip o adresteki word’ü (kelimeyi) içeri alır, işler ve geri yazar.

“A word is how big of a chunk that the CPU can take in from memory at a time and operate on.” — Ku, 39:40

Word boyutu neden önemli? Çünkü bir adresi CPU’da saklayabilmek için adresin bir word’e sığması gerekir. Ku kendi gençliğindeki 32-bit word’lerle bugünkü 64-bit word’leri karşılaştırır:

  • 32 bit\(2^{32}\) farklı adres → yaklaşık 4 GB bellek sınırı (eski disklerin neden bölümlere ayrıldığının sebebi).
  • 64 bit\(2^{64}\) adres → yaklaşık 18 exabyte; pratikte sınırsız (Google’ın tüm sunucularındaki veri ≈ 10 exabyte mertebesinde).
Şekil 2.6: Word RAM modeli şeması: bellek, indeksli bir word dizisidir (her kutu w bitlik bir word). CPU, herhangi bir adresteki bir word’ü sabit zamanda (\(O(1)\), word boyutundan bağımsız) okur/yazar — amber okla vurgulanan read(adres 3) işlemi. Sağdaki yan not word boyutunu adreslenebilir bellekle bağlar: 32-bit word \(2^{32} \approx\) 4 GB, 64-bit word \(2^{64} \approx\) 18 EB adres alanı sunar. \(n\) girdinin sığması için word \(w \geq \log_2 n\) bit olmalıdır.
İpucuBuilder Notu — GPU’da O(1) Erişim ve Bellek Bandwidth

word RAM’in “her adrese sabit zamanda eriş” varsayımı, gerçek donanımda kırılır — ve bu kırılma ML performansının kalbidir:

  • İleriye → cache ve coalesced erişim: Önbellek hiyerarşisi yüzünden sıralı (bitişik) erişim, rastgele erişimden kat kat hızlıdır. GPU’da iş parçacıkları bitişik adresleri okuduğunda erişim “coalesced” olur ve tam bant genişliği kullanılır; saçılmış (scatter/gather) erişim bant-genişliği darboğazına takılır.
  • İleriye → bellek-bağlı çekirdekler: matmul ve attention gibi birçok ML çekirdeği işlem değil bellek-bandwidth bağlıdır (roofline modeli, aritmetik yoğunluk). “İşlem say” modeli bu trafiği eksik sayar — gerçek hız çoğu zaman taşınan byte miktarına bağlıdır.
  • Geriye → word = pointer boyutu: 64-bit word, dev tensörleri adresleyebilmenin nedenidir; darboğaz adresleme değil, o adreslere veriyi taşıma hızıdır.

2.12 Temel İşlemler

word RAM modelinde CPU’nun sabit zamanda yapabildiği işlemler şunlardır:

  • İki word’ü karşılaştırma.
  • Tam sayı aritmetiği, mantıksal işlemler, bit düzeyi işlemler (sonuncusunu bu derste pek kullanmayız).
  • Bir bellek adresinden bir word okuma ve yazma.

“I can either do integer arithmetic, logical operations, bitwise operations… And I can read and write from an address in memory, a word in constant time.” — Ku, 41:53

Kritik kısıt: CPU her seferinde yalnızca sabit miktarda bilgi (genelde iki word) üzerinde işlem yapar. Lineer miktarda veriyi (\(n\) öğe) işlemek istiyorsak — örneğin hepsini okumak — bu lineer zaman \(O(n)\) alır, çünkü her parçayı ayrı ayrı okumak zorundayız. Sabit zaman ile lineer zaman arasındaki bu ayrım, tüm algoritma analizinin tohumudur.

2.13 Veri Yapıları: İlk Bakış

Dersin ilk yarısı (ilk sekiz ders) veri yapıları üzerinedir: CPU gibi sabit miktarda değil, büyük miktarda veriyi saklayıp üzerinde verimli işlemler desteklemek. İlk örnek statik dizidir (static array).

“Python has a lot of really interesting data structures, like a list, and a set, and a dictionary… that are actually not in this model.” — Ku, 44:07

Bu cümle bu kursun en önemli uyarısıdır. Python’da list, set, dict bedava ve sihirli görünür — ama word RAM modelinde yokturlar. Aralarında çok sayıda kod katmanı vardır ve o arayüzün ne kadar zaman aldığı her zaman belli değildir.

İpucuBuilder Notu — Gizli Maliyet ve Sistem Tasarımı
  • Geriye → Python (Phase 1): lst.insert(0, x) bir satır görünür ama \(O(n)\)’dir (her elemanı kaydırır); dict[key] ortalama \(O(1)\)’dir ama bunun arkasında hashing vardır (Ders 4). Bu kurs o gizli maliyeti açar.
  • Ampirik doğrulama: time.perf_counter ile farklı \(n\) değerlerinde süre ölçüp asimptotik tahmini test edebilirsin — teori ile ölçümü buluşturmak builder disiplinidir.
  • İleriye → sistem tasarımı: “hangi işlem sabit, hangisi lineer” ayrımı, bir veri yapısını (hash map mı, dizi mi, ağaç mı) seçerken verdiğin her kararın temelidir.
Şekil 2.7: Python list gizli maliyeti: insert(0, x) baştan ekleme, mevcut tüm \(n\) elemanı bir konum sağa kaydırır (amber oklar) → \(O(n)\); append yalnız sona tek hücre yazar → \(O(1)\). Alttaki teorik işlem-sayısı eğrisi DETERMİNİSTİKtir (zaman ölçümü değil): \(n\) kez baştan ekleme toplam \(\approx n^2/2\) kaydırma yaparken, \(n\) kez append toplam \(\approx n\) işlemde kalır. \(n=200\)’de uçurum ≈ 19.900’e karşı 200.

Egzersiz 4’teki ölçüm deneyi bu uçurumu ampirik olarak doğrular — aşağıdaki Egzersizler bölümünde kodu var.

2.14 Bu Dersin Özeti

  1. Problem, girdiler ile çıktılar arasında bir ikili ilişkidir; algoritma, o ilişkiyi hesaplayan bir fonksiyon/prosedürdür.
  2. Bir algoritmanın doğruluğu, keyfi büyük girdiler için tümevarımla ispatlanır (hipotez + temel durum + tümevarım adımı).
  3. Verimlilik, saniyeyle değil, temel işlem sayısıyla ölçülür — donanımdan bağımsız olmak için.
  4. Asimptotik gösterim: \(O\) (üst sınır), \(\Omega\) (alt sınır), \(\Theta\) (sıkı sınır).
  5. Büyüme hiyerarşisi: \(1 \ll \log n \ll n \ll n \log n \ll n^2 \ll 2^n\). Polinom ve altı “verimli”, üstel kullanılamaz.
  6. word RAM modeli: belleğe sabit-zaman rastgele erişim; bir word, CPU’nun bir kerede işlediği bit öbeği.
  7. Python’ın list/dict/set yapıları word RAM modelinde doğrudan yoktur — bedava görünen işlemlerin gizli zaman maliyeti vardır.
ÖnemliTek Bir Cümle

Bir algoritmayı anlamak, onun ne yaptığını değil; neden doğru ve ne kadar hızlı olduğunu — ve bunu başkasına nasıl kanıtlayacağını bilmektir.

2.15 Kontrol Soruları

Cevap: Bir problem, girdiler ile çıktılar arasında bir ikili ilişkidir — ne hesaplanacağını söyler (hangi girdiye hangi çıktı doğru). Bir algoritma ise o ilişkiyi gerçekleştiren bir prosedürdür — nasıl hesaplanacağını söyler. Problem çıktıları bilir (ilişkiyi tanımlar); algoritma çıktıları üretir. Matematiksel olarak algoritma, her girdiyi tek bir (doğru) çıktıya eşleyen bir fonksiyondur.

Cevap: \(O(n)\) tercih edilir. \(n = 10^6\) için \(O(n) \approx 10^6\) işlem (milisaniyeler), \(O(n^2) \approx 10^{12}\) işlem (mertebe olarak dakikalar–saatler) demektir. Asimptotik fark, \(n\) büyüdükçe pratik bir uçuruma dönüşür. Ku’nun deyişiyle: polinom “verimli”, ama düşük dereceli polinom her zaman daha iyidir.

Cevap: \(K = 0\), ispatın en kolay ve en sağlam tabanıdır: hiç öğrenci görüşülmediğinde hiçbir iş yapılmamıştır ve ilk 0 öğrenci bir eşleşme içeremez, dolayısıyla hipotez boş-doğru (vacuously true) olarak sağlanır. \(K = 1\) veya \(K = 2\) de işe yarar ama gereksiz yere durum kontrolü gerektirir. Daha zayıf (daha kolay) temel durum, daha temiz bir ispattır.

Cevap: lst.insert(0, x), listenin başına ekleme yapar; bunun için mevcut tüm \(n\) elemanı bir konum sağa kaydırmak gerekir → \(O(n)\). Tek satırlık “bedava” görünen bu işlem, word RAM modelinde lineer sayıda word okuma/yazma anlamına gelir. Ku’nun uyarısı tam buna işaret eder: Python yapıları modelde doğrudan yoktur; gerçek maliyeti görmek için arayüzün altına bakmak gerekir.

2.16 Egzersizler

Egzersiz 1. Şu problemi girdi-çıktı ilişkisi olarak tanımla: “bir tam sayı dizisinde en büyük elemanı bul.” Girdi kümesi, çıktı kümesi ve doğruluğu kontrol eden yüklemi (predicate) yaz.

Egzersiz 2. Doğum günü algoritmasının çalışma süresini analiz et. “Kayıtta var mı?” kontrolü her seferinde tüm kaydı lineer taramayla yapılırsa toplam süre ne olur? Bu kontrol \(O(1)\) olsaydı (örneğin hash ile) toplam süre ne olurdu?

Egzersiz 3. Tümevarım adımındaki “Durum B”yi kendi cümlelerinle yeniden ifade et: ilk K′+1 öğrenci içinde bir eşleşme varsa, bu eşleşme neden zorunlu olarak K′+1’inci öğrenciyi içerir?

Egzersiz 4. Python’da aşağıdaki deneyi çalıştır; baştan ekleme (insert(0, x)) ile sona ekleme (append) sürelerini farklı \(n\) değerlerinde karşılaştır ve asimptotik farkı gözlemle:

import time

def measure(n):
    a, b = [], []
    t0 = time.perf_counter()
    for i in range(n):
        a.insert(0, i)      # bas: her ekleme O(n) -> toplam O(n^2)
    t1 = time.perf_counter()
    for i in range(n):
        b.append(i)         # son: amortize O(1) -> toplam O(n)
    t2 = time.perf_counter()
    print(n, "insert(0):", t1 - t0, "append:", t2 - t1)

for n in (1000, 2000, 4000, 8000):
    measure(n)

Egzersiz 5. Statik bir dizide “i’inci elemana eriş” işlemi neden \(O(1)\)’dir (word RAM modelinde)? Bu, bir bağlı listede (linked list) neden \(O(1)\) değildir? Ders 2’nin dinamik dizi konusuna bir köprü kur.

2.17 Sonraki Ders İçin Hazırlık

Ders 2 (L2): Veri Yapıları ve Dinamik Diziler

Erik Demaine ile dersin ilk veri yapısı bölümüne giriyoruz. Statik dizinin sınırını (sabit boyut) aşan dinamik dizi (Python list’in altında yatan yapı) ve bağlı liste ele alınır; sona eklemenin neden amortize \(O(1)\) olduğunu göreceğiz — bu dersteki “gizli maliyet” sorusunun ilk cevabı.

UyarıDers 2 Öncesi Yapılacak
  • Bu dersin egzersizlerini, özellikle Egzersiz 4’ü (Python ölçümü) çöz.
  • dizi (sequence) arayüzünü düşün: hangi işlemler \(O(1)\), hangileri \(O(n)\) olmalı?
  • Ana cümleyi tekrar oku: “Bir algoritmayı anlamak, onun ne yaptığını değil; neden doğru ve ne kadar hızlı olduğunu bilmektir.”

2.18 Anahtar Kavramlar (Cheat Sheet)

Kavram Tanım Sayfada
Problem Girdi-çıktı ikili ilişkisi (binary relation) Böl. 2
Algoritma İlişkiyi hesaplayan prosedür/fonksiyon Böl. 3
Tümevarımla doğruluk Hipotez + temel durum (\(K=0\)) + tümevarım adımı Böl. 5
Verimlilik Temel işlem sayısı (saniye değil) Böl. 6
\(O\) / \(\Omega\) / \(\Theta\) Üst / alt / sıkı asimptotik sınır Böl. 7
Büyüme hiyerarşisi \(1 \ll \log n \ll n \ll n \log n \ll n^2 \ll 2^n\) Böl. 8
Polinom = verimli \(n^c\) sınırı; üstel (\(2^n\)) kullanılamaz Böl. 8
word RAM Sabit-zaman rastgele erişimli hesaplama modeli Böl. 9
Word CPU’nun bir kerede işlediği bit öbeği Böl. 10
Temel işlemler Aritmetik, mantık, oku/yaz; her biri \(O(1)\) Böl. 11
Statik dizi İlk veri yapısı; Python list/dict modelde yok Böl. 12

2.19 Builder ve OMSCS Bağlantıları

İpucu6 köprü

Bu ders, ileri algoritma ve sistem mühendisliğinin temel dilini kurar — köprülerin özeti:

  1. Asimptotik sezgi → kompetitif programlama: “\(n = 10^5\) için \(O(n^2)\) geçer mi?” sorusunun cevabı bu derste oturur.
  2. word RAM modeli → sistem tasarımı: hangi işlemin gerçekten sabit zaman olduğunu bilmek, veri yapısı seçiminin temelidir (hash map, dizi, ağaç).
  3. Tümevarımla doğruluk → OMSCS CS 6515: graduate algoritma derslerinde her algoritma ispatla sunulur; bu dersin disiplini oraya taşınır.
  4. İndirgeme (reduction) sezgisi → “bilinen bir probleme indirge” yaklaşımı, hem 6.006’nın hem de ileri derslerin çekirdek stratejisidir.
  5. Python gizli maliyet → üretim kodu: list.insert(0, x) veya x in list gibi “masum” çağrıların asimptotik bedelini görmek, ölçeklenen sistemlerde performans hatalarını önler.
  6. time.perf_counter ile ampirik doğrulama → teori ile ölçümü buluşturmak; bir builder, asimptotik tahmini deneyle test eder.

ÖnemliTek bir şey alıp gideceksen

Bir algoritma yazmak yarısıdır; diğer yarısı onun doğru ve verimli olduğunu — başkasını ikna edecek netlikte — kanıtlamaktır. 6.006 boyunca her ders bu iki yarıyı birlikte öğretir.