İçindekiler
ALGORİTMA ANALİZİ (TEORY OF ALGORITHMS). 6
SORU-1: Branch ve Bounding (Dallanma ve Sınırlandırma Yakl.) hakkında bilgi veriniz. 7
SORU-2: Smith Waterman Dizgi Yaslama (String Alignment) Algoritması hakkında bilgi veriniz. 11
SORU-3: Permütasyon Sıralaması (Permutation Sort) hakkında bilgi veriniz. 16
SORU-4: Gnome Sıralaması (Gnome Sort) hakkında bilgi veriniz. 19
SORU-5: Tarak Sıralaması (Comb Sort) hakkında bilgi veriniz. 23
SORU-6: Flash Sort (Şimşek Sıralaması, Bora Sıralaması) hakkında bilgi veriniz. 26
SORU-7: Maximal Munch (Azami Lokma) Yöntemi 28
SORU-8: Horspool Algoritması hakkında bilgi veriniz. 32
SORU-9: Yeknesan (Invariant , Değişmez) hakkında bilgi veriniz. 36
SORU-10: MU Bulmacası (MU Puzzle) hakkında bilgi veriniz. 37
SORU-11: Örtüşen Alt Problem (Overlapping Subproblem) hakkında bilgi veriniz. 38
SORU-12: Bin Packing (Kutulama Problemi) hakkında bilgi veriniz. 39
SORU-14: Macar Algoritması (Hungarian Algorithm) hakkında bilgi veriniz. 48
SORU-15: Çemberi bölen doğrular problemi hakkında bilgi veriniz. 53
SORU-16: Algoritma (Algorithm) hakkında bilgi veriniz. 57
SORU-17: Post Karşılık Problemi (Post Correspondence Problem) hakkında bilgi veriniz. 58
SORU-18: Merkezi poligon sayıları (Centeral Polygon Numbers) hakkında bilgi veriniz. 59
SORU-19: Levenshtein Mesafe Algoritması (Levenshtein Distance) hakkında bilgi veriniz. 61
SORU-20: Zaman Çizelgeleme (TimeTabling) hakkında bilgi veriniz. 66
SORU-21: Doğrusal Programlama Örnekleri vererek açıklayınız. 66
SORU-22: Permutasyon Algoritması hakkında bir soru. 69
SORU-23: EQP (Exact Quantum Polynomial) hakkında bilgi veriniz. 75
SORU-24: Çakışma Problemi (Collision Problem) hakkında bilgi veriniz. 75
SORU-25: Algoritma Analizi (Analysis of Algorithms) hakkında bilgi veriniz. 76
SORU-26: Bellman Ford Algoritması hakkında bilgi veriniz. 79
SORU-27: NL (Non-deterministic Logarithmic) hakkında bilgi veriniz. 89
SORU-28: Bool Tatmin Problemi hakkında bilgi veriniz. 90
SORU-30: Karmaşıklık Sınıfları (Complexity Classes) hakkında bilgi veriniz. 92
SORU-31: Bellman Ford Algoritması hakkında bilgi veriniz. 96
SORU-32: Edmonds Karp Algoritması hakkında bilgi veriniz. 100
SORU-33: Dijkstra Algoritması hakkında bilgi veriniz. 103
SORU-34: Cardinality (Sayısallık) hakkında bilgi veriniz. 108
SORU-35: Kırmızı-Siyah Ağaçları (Red Black Trees). 115
SORU-36: Serseri sıralaması (Stooge Sort). 122
SORU-37: İkili Arama Algoritması (Binary Search Algorithm). 125
SORU-38: Güvercin Yuvası Kaidesi (Pigeonhole Principle). 132
SORU-39: Arılar Algoritması (Bees Algorithm). 133
SORU-40: Internal Path Reduction Trees ( İç Yol İndirgeme Ağaçları). 137
SORU-41: Sürahi Problemi (Water Jug Problem). 142
SORU-42: Sınırlı Derin Öncelikli Arama (Depth-Limited Search). 144
SORU-43: Tepe Tırmanma Algoritması (Hill Climbing Algorithm). 146
SORU-44: DFA Metin Arama Algoritması (DFA Text Search). 149
SORU-45: Kaba Kuvvet Metin Arama Algoritması (Bruteforce Text Search Algorithm). 153
SORU-46: Arama Algoritmaları (Search Algorithms). 156
SORU-47: Şanslı Sıralama (Lucky Sort). 157
SORU-48: Bogo Sıralama (Bogosort). 157
SORU-49: Push Down Automata. 158
SORU-50: Program doğruluğu ( Program correctness). 164
SORU-51: Rastgele Sayılar (Random Numbers). 165
SORU-52: Matematiksel Tümevarım Teoremi (Mathematical Induction Principle). 167
SORU-53: Özyineli Diller (Recursive Languages). 168
SORU-54: Karar Problemi (Decision Problem). 169
SORU-55: Turing Makinesi (Turing Machine). 169
SORU-56: Özyineli sayılabilir küme (Recursively Enumerable Sets). 177
SORU-57: Hesaplanabilir Fonksiyon (Computable Function). 178
SORU-58: Özyineli Küme (Recursive Set). 179
SORU-59: Hesaplanabilirlik Teorisi (Computability Theory). 181
SORU-60: Sıfır Bilgi İspatı (Zero-Knowledge proof). 181
SORU-61: Laplas Matrisi (Laplacian Matrix). 182
SORU-62: Denkşekillilik (Isomorphism). 184
SORU-63: Öyler Yolu (Eulerian Path). 186
SORU-64: Markof Modeli (Markov Model). 190
SORU-65: Hamilton Yolu (Hamiltonian Path,hamiltonian circuit). 193
SORU-66: Augmented Transition Network (ATN, Uzatılmış Geçiş Ağı). 195
SORU-67: Özyinelilerde Ana Teorem (Master Theorem). 197
SORU-68: Floyd-Warshall Algoritması 201
SORU-69: Boyer Moore Dizgi Arama Algoritması (Boyer-Moore String Search). 205
SORU-70: Binom Ağaçları (Binom Trees, Çift Termili Ağaçlar). 207
SORU-71: Eşleme (Matching). 209
SORU-72: Petri Ağları (Petri Nets). 214
SORU-73: İki Parçalı Graflar (Bipartite Graphs). 216
SORU-74: Minimax Ağaçları (Minimax Tree). 220
SORU-75: Brent Algoritması (Brent's Algorithm). 225
SORU-76: Tavşan Kaplumbağa Algoritması (Hare and Tortoise Algorithm). 227
SORU-77: Meşguliyet (Utilization, Kullanım). 229
SORU-78: Knuth Morris Prat Algoritması (KMP Algorithm). 230
SORU-79: Aks-i Müfret (Palindrome). 231
SORU-80: Atomluluk (Atomicity). 232
SORU-82: Düzenli İfadelerde Pompalama Önsavı (Pumping Lemma for Regular Expressions). 235
SORU-83: Pompalama Önsavı (Pumping Lemma). 237
SORU-84: İçerikten Bağımsız Gramer (context free grammer, CFG). 238
SORU-85: İçerikten bağımsız dil (Context Free Language, CFL). 239
SORU-86: Dolaylı sıralama (Indirect Sort, Gayrimüstakim sıralama). 240
SORU-87: Harici Sıralama (External Sort). 240
SORU-88: A Yıldız Arama Algoritması (A Star Search Algorithm, A*). 241
SORU-89: Sezgi Üstü Algoritmalar (Üstsezgisel Algoritmalar, Meta Heuristic Algorithms). 245
SORU-90: En kötü durum analizi (Worst Case Analysis). 247
SORU-91: Sezgisel Algoritmalar (Buluşsal Algoritmalar, Heuristic Algorithms). 250
SORU-92: Kabuk Sıralama (Shell Sort). 250
SORU-93: Sallayıcı Sıralaması (Shaker Sort). 252
SORU-94: Linear Programming (Doğrusal Programlama). 253
SORU-95: Self Organizing Maps (Özdüzenleyici Haritalar). 254
SORU-96: Sokma Sıralaması (Ekleme Sıralaması, Insertion Sorting). 255
SORU-97: Backus Normal Form (BNF). 257
SORU-98: Buket Sıralaması (Bucket Sort). 258
SORU-99: Kıtlık (Starvation). 258
SORU-100: İlk Gelen Çalışır (First Come First Serve, FCFS, FIFO). 258
SORU-101: Sığ Öncelikli Arama (Breadth First Search , BFS). 259
SORU-102: Durma Problemi (Halting Problem). 260
SORU-103: Tersine Koyarak İspat (antitez, Contraposition). 262
SORU-104: Doğrudan İspat (Direct Proofing). 262
SORU-105: Taban Sıralaması (Radix Sort). 264
SORU-106: Doğrusal Arama (Linear Search). 265
SORU-107: Yerleştirme Algoritmaları (Fitting Algorithms). 265
SORU-109: Brent Yöntemi (Brent's Method). 266
SORU-110: Ara Değer Araması (Interpolation Search). 267
SORU-111: Dış Yol Uzunluğu (External Path Length). 269
SORU-112: İç Yol Uzunluğu (Internal Path Length). 269
SORU-113: İç düğüm (Internal Nodes). 270
SORU-114: Akış Diyagramı (Flow Chart). 270
SORU-115: Seyyar Tüccar Problemi (Traveling Salesman Problem). 272
SORU-116: Devamsal Geçiş Tarzı (Continuation-passing style, CPS). 273
SORU-117: Kuyruk Özyinelemesi (Tail Recursion, Birikimsel Tarz, Accumulation Style). 274
SORU-118: Sıralama Algoritmaları (Sorting Algorithms). 276
SORU-119: Seçerek Sıralama (Selection Sort). 276
SORU-120: Hızlı Sıralama Algoritması (Quick Sort Algorithm). 278
SORU-121: Birleştirme Sıralaması (Merge Sort). 283
SORU-122: Yığınlama Sıralaması (Heap Sort). 288
SORU-123: Yığın Ağacı (Heap). 291
SORU-124: Dizi üzerinde ağaç kodlaması 292
SORU-125: Sayarak Sıralama (Counting Sort). 293
SORU-126: Kabarcık Sıralaması (Baloncuk sıralaması, Bubble Sort). 295
SORU-127: Matris Mod (Masfuf Hali,Matrix Mod). 299
SORU-128: Hamming Mesafesi (Hamming Distance). 299
SORU-129: Fibonacci Arama Algoritması (Fibonacci Search Algorithm). 300
SORU-130: Özyineli Fonksiyonlar (Recursive Functions). 300
SORU-131: Bağımsız düğümler (Anti Clique, Independent Set). 306
SORU-133: İstikra ile ispat (Tüme varım, Proof by Induction). 307
SORU-134: burhan-ı mütenakıs (proof by contradiction, olmayana ergi). 308
SORU-135: Binaen Burhan (İnşâa ile İspat , Proof by Construction, Binaenaleyh). 309
SORU-136: k-düzenli graf ( k-regular graph). 310
SORU-137: Sembol (Harf, İşaret, Symbol). 310
SORU-138: Güçlü Bağlı Graf (Strongly Connected Graph). 311
SORU-139: Basit Döngü (Simple Cycle). 311
SORU-140: Bağlı graf (conected graph). 311
SORU-142: Altgraf (Subgraph). 311
SORU-144: Yönlü Graflar (Directed Graphs). 312
SORU-145: Özetleme Fonksiyonları (Hash Function). 312
SORU-146: Ağaçlarda Dengeleme (Rotation, Balancing). 313
SORU-147: Trie (Metin Ağacı). 316
SORU-149: Dairesel Grup (Cyclic Group). 318
SORU-150: OWL Time (OWL Zaman, Web Varlıkbilim Dili Zaman). 318
SORU-151: TTML (Time Tabling Markup Language, Zaman Çizelgeleme İşaretleme Dili). 324
SORU-152: Arc Constraint (Kiriş Şartı). 324
SORU-153: 4 vezir problemi (4 queen problem). 324
SORU-154: Belirsiz Çokterimli Tam (NP-Complete, Nondeterministic Polynomial Complete). 325
SORU-155: Açgözlü Yaklaşımı (Greedy Approach). 326
SORU-156: Torba Problemi (knapsack problem). 326
SORU-157: Sonlu Durum Makinası (Finite State Machine, Finite State Automaton). 328
SORU-158: Kruskal Asgari Tarama Ağacı Algoritması 329
SORU-159: Prim asgari tarama ağacı Algoritması 337
SORU-160: asgari tarama ağacı (en kısa örten ağaç, minimum spanning tree). 345
SORU-161: alt program (subprogram, subroutine). 345
SORU-162: fonksiyon göstericileri (function pointer). 346
SORU-163: En uzun Ortak Küme (longest common subsequence, Lcs). 348
SORU-164: Dinamik Programlama (Dynamic programming). 349
SORU-165: parçala fethet yöntemi (divide and conquer). 350
ALGORİTMA ANALİZİ (TEORY OF ALGORITHMS)
SORU-1: Branch ve Bounding (Dallanma ve Sınırlandırma Yakl.) hakkında bilgi veriniz.
Algoritma, basitçe verilen bir fonksiyon için en iyi (optimum) çözümü bulmayı amaçlar. Bu amaca sahip çok sayıdaki yaklaşımdan farkı ise bu amaca ulaşmak için iki çok önemli aracı kullanıyor olmasıdır:
- Parçalama (Splitting) veya Dallandırmda (Branching)
- Sınırlandırmda (Bounding)
Bu iki yaklaşım sırasıyla probleme uygulanır. Parçalama aşamasında, problem alt parçalarına ayrılır (Burası parçala fethet (divide and conquere) yaklaşımına benzemektedir) ardından bu parçalar için alt ve üst sınırlar (upper and lower bounds) belirlenir.
Algoritma özellikle oyun programlamada sıkça kullanılmaktadır. Yapay zeka yaklaşımının kodlandığı ve karar ağacı oluşturulduğu durumlarda, ağacın dallarına bölünmesi ve her dal için limitlerin belirlenmesi şeklinde çalışır.
Ayrıca algoritmanın iyileştirilmesi için alfa-beta budaması (alpha beta prunning) benzeri yöntemler kullanılabilir.
Algoritmanın en önemli özelliklerinden birisi NP-Hard problem grubu için kullanışlı olmasıdır.
Algoritmanın kullanıldığı bazı problem tipleri aşağıda sıralanmıştır:
- Azami Tatmin Problemi (Maximum Satisfaction Problem)
- Torba Problemi (Knapsack Problem)
- Seyyar Satıcı Problemi (Travelling Salesman Problem)
Azami Tatmin Problemi (Maximum Satisfaction Problem)
Litartürde, Boolean Satisfiability Problem olarak geçen ve boole cebirindeki denklemlerin doğruluğunun sağlanması olarak özetlenebilecek olan problemdir. Ayrıca çeşitli kaynaklarda bu probleme, problemin İngilizce tanımında geçen Satisfiability kelimesinin kısaltması olan SAT kelimesi olarak da rastlanabilir.
Bu problemi, bilgisayar bilimleri açısından ilginç kılan yanı ise, problemin yapısal olarak bir NP-Tam (NP-Complete) sınıfı problem olmasıdır.
Boole Cebiri, aslında mantıksal bir gösterim ifade etmektedir. Dolayısıyla mantıktaki karşılığı olarak probleme önermeler mantığı (propositional logic, kaziye mantığı) tatmin problemi ismi de verilebilir.
Boole cebirinden alışıldığı üzere mantıksal kurgu için tanımlı olan ve bilgisayar bilimlerinde değişken (variable) olarak isimlendirilebilecek belirticiler (literal, lafziler) bulunmaktadır. Bunlar basit birer sembol ile gösterilir.
Örneğin x ile gösterilen bir sembol çoğu zaman gerçek hayattaki bir kaziyeyi (önerme, proposition) ifade etmektedir.
Bu lafzi gösterimlerin (literal, belirticiler) üzerinde tanımlı ve yine boole cebiri içerisinde kabul edilebilecek işlemler (operators) tanımlanabilir. En sık kullanılan bool cebiri işlemleri ve(and), veya (or), tersi (not) şeklinde sayılabilir. Bu işlemler kullanılarak kurulan bir dizilimin sonucunun yine bir lafzi gösterim (literal) cinsinden elde edilmesi ve ya doğru (true) ya da yanlış (false) olması beklenir.
Bu tanımın üzerine örneğin sadece 3 adet lafzi gösterim (literal)’in ve (and) bağlacı normal şeklinde (conjunction normal form) olması durumuna 3SAT veya 3CNFSAT isimleri verilir. Bu problem NP-TAM (NP-Complete) bir problemdir.
Benzer tanım sadece 2 adet lafzi gösterim’in (literal) ve bağlacı normal şeklinde (conjunction normal form) olması durumuna ise 2SAT veya 2CNFSAT ismi verilir ve bu problem de NL-Tam (NL-Complete) bir problemdir.
Boole tatmin probleminin diğer önemli bir yanı da tarihsel olarak, 3SAT problemlerin, ilk NP-Complete problem olmasıdır. Bu konuda çalışan Stephen Cooke 1971 yılında bu problem grubunun NP-Complete olduğunu göstermiştir.
Ayrıca boole tatmin probleminin birinci derece mantık (First order predicate calculus) içerisindeki çözümünün aranması da ayrıca bir problemdir.
Bilindiği üzere birinci derece mantığın, üzerinde tanımlı olduğu lafzi gösterimler (literals) için bir takın etki alanı tanımlamaları (domain) mümkündür. Bu tanımlamalar ile verilen bir boole cebiri, diziliminin çözümlenmesi bir seviye daha karmaşık hale gelmektedir.
Örneğin aşağıdaki problemin çözümünü bu anlamda değerlendirebiliriz:
∃a (Şadi (a) ∧ Mühendis(a)) → Öyle bazı a’lar vardır ki, bu a’nın ismi Şadi’dir ve bu a mühendistir.
Bu tip birinci derece mantık dizilimlerinin çözüm karmaşıklığı Co-NP problem olarak geçmektedir.
Torba Problemi (Knapsack Problem)
Şekil-1.1: Torba problemi
Seyyar Satıcı Problemi (Travelling Salesman Problem)
Bu problemin amacı pekçok farklı yer gezen bir tüccarın en az yol katederek bütün gezeceği yerleri nasıl tamamlayacağının hesaplanmasıdır. (gezgin satıcı problemi) Örneğin aşağıda verilen türkiye haritasında farklı iller ve bu illerin coğrafi konumları işaretlenmiştir. Kolaylık olsun diye iller arasındaki mesafeleri kuş uçuşu gitmek istersek bu durumda bütün illeri dolaşan en kısa yol aşağıdaki şekilde olur.
Örnek olarak 0-1 Torba problemini (0-1 Knapsack problem) çözmeye çalışalım. Öncelikle problemi hızlıca hatırlayalım. Amacımız elimizdeki torbayı mümkün olan en değerli şekilde doldurmaktır. 0-1 torba problemi ise klasik torba probleminin özel bir halidir ve bu özel halinde bir eşya, ya tamamen alınır ya da tamamen bırakılır, bir eşyanın bir kısmını almak mümkün değildir.
Şekil-1.2: Seyyar Satıcı Problemi
Dikkat edilirse bu haritada bütün noktaları dolaşan en kısa yol bulunmuştur. Bu işlemi yaparken kullanılabilecek bir iki yol aşağıda açıklanmıştır:
Açgözülü yaklaşımı (greedy approach): Bu yaklaşıma göre başlangıç olarak seçilen herhangi bir şehire en yakın mesafedeki diğer şehir seçilir ve gezi listesine eklenir. Bu şekilde bütün şehirler gezi listesine eklenene kadar hep en son eklenen şehre en yakın şehir seçilir.
Bu yaklaşım klasik açgözlü yaklaşımının hata yapma ihtimalini barındırmakla birlikte uygulanması en kolay yöntemlerden birisidir.
Enküçük artış yöntemi (Smallest increase): By yöntemde ise toplam gezilecek mesafe her seferinde yeniden hesaplanmakta ve alternatiflerden hangisi eklenirse toplam gezi mesafesi en az olur diye sorglunarak yeni şehirler eklenmektedir.
Örneğin başlangıçtan alınan 3 şehir gezi listesinde bulunurken 4. şehri seçecek olalım. Bu durumda bütün ihtimaller teker teker listeye eklenerek en az mesafe hangisinde bulunuyorsa bu şehir seçilir.
Örneğin aşağıdaki tabloda verilenleri ele alalım:
|
Eşya |
Ağırlığı (w) |
Değeri (v) |
Değer/Ağırlık |
|
1 |
4kg |
40 TL |
10 |
|
2 |
7kg |
42 TL |
6 |
|
3 |
5kg |
25 TL |
5 |
|
4 |
3kg |
12 TL |
4 |
Tablo-1.1: Torba problemi tablosu
Elimizde sadece 10kg alabilecek bir torbamız olduğunu düşünelim. Bu durumda en yüksek doldurma değerimiz, yani en pahalı şekilde bu torbayı nasıl doldurabiliriz?
Yukarıda verilen değerler çerçevesinde bir karar ağacı oluşturacağız. Önce problemin çözümünü verip ardından açıklamak istiyorum:
Şekil-1.3: Karar ağacı
Şekildeki kök düğümünden başlayarak diğer düğümleri dolaşmak istiyor olalım. Dallanma ve sınırlandırma problemi bizim başlangıç düğümünden başlayarak problemi alt paralara bölmemizi söyler (branching). Buna göre kök düğümünden başlandıktan sonra olası 2 ayrı yolu 2 alt problem olarak görebiliriz ve elbette bu alt problemlerin de alt problemleri bulunmaktadır. Yani problemin çözümüne sol veya sağ düğümünden devam edilmesi alt problemlerdir. Burada problemi parçalara bölerek bir dallanma elde ediyoruz. Genelde problemin bu aşamasında özyineli (recursive) bir yaklaşım izlenir.
Hemen bu noktada önemli bir durumu belirtmek istiyorum. Dallanma ve sınırlandırma (Branch and bound) algoritmalarının çok benzediği bir yaklaşım da geri izleme (bactracking) algoritmalarıdır. Aynen geri izleme algoritmalarında olduğu gibi bir yoldan başlanır başarıya ulaşılamazsa farklı bir yola girilir ve arama işleminin en son başarılı olduğu yerden herşey devam eder. Ancak dallanma ve sınırlandırma algoritmaları, geri izleme algoritmalarında olduğu gibi bize belirli bir yolu dikte etmez. Yani sığ öncelikli arama (breadth first search) veya derin öncelikli arama (depth first search) gibi sistematik olarak nasıl gidileceğini belirten bir yanı yoktur. Bunlardan herhangi birisi kullanılabileceği gibi tamamen başka bir yöntem de seçilebilir.
Gelelim problemin sınırlandırılmasına (bounding). Sınırlandırma sırasında bulunan değer elimizdeki en iyi değerle karşılaştırılır. Şayet daha kötü bir sonuçsa çözüm olma ihtimali yoktur (non-promising) şayet elimizdeki sınırdan iyi bir durumdaysa o zaman çözüm ihtimali olabilir (promising) ve bu şekilde tutulur.
Yukarıda sınırlar için kullanılan ub (upper bound, üst sınır) değerinin hesaplanması aşağıdaki şekildedir:
Şekil-1.4: UB değerinin hesaplanması
Buradaki hesaplama işlemi sırasında W, toplam torbanın kapasitesini, w o ana kadar torbadaki eşyaların ağırılğını, vi+1, o anda alınmasına karar verilmeye çalışılan eşyanın değerini ve wi+1 ise o anda alınmasına karar verilmeye çalışılan eşyanın ağırlığını ifade etmektedir.
Diğer bir deyişle elimizdeki torbada, o ana kadar aldığımız eşyaların değerine, yeni eklenen eşyanın kg başına değerini ekliyoruz. Bu yeni eklenen değeri de torbada kalan boş yer ile çarpıyoruz. Bu sayede şayet torbadaki boş yerden daha fazla yer kaplıyorsa eksi değer olarak sisteme etki ediyor. Şayet torbaya sığıyorsa, alınabilecek en değerli eşyanın en fazla yeri kaplamasını istiyoruz.
Örneğin başlangıç durumunu hesaplayalım:
ub = + ( 10 – 0 ) (10TL)= 100 TL
Yani başlangıçta torbamızda elimizde 0 TL değerinde eşya, 0 kg ağırlığında eşya ve en fazla kilogramı 10TL gelebilecek eşya bulunuyor (tablodan görülebilir). Dolayısıyla üst limitimiz 100TL. İddiamız şu an itibariyle 100TL’den daha değerli bir torba doldurma alternatifi olmadığı (üst limit, upper bound) ki bu durum tamamen doğrudur.
Diyelim ki ilk ürünü aldık ve ikinci ürüne karar vermek istiyoruz. Bu durumda formülümüz aşağıdaki şekilde olacak:
40 TL + (10-4) * 6 = 76
Yukarıdaki hesapta, 40 TL ilk ürünün alınmış değeridir. Bu değerin üzerine, ikinci ürün için torbamızda kalan yer ile ikinci ürünün değerinin çarpımı eklenerek 76 sonucu bulunmuştur. Demek ki iki ürünü alırsak torbanın değeri 76 olacak. Peki şimdi de ilk ürünün alınmaması durumunu görelim:
0 + (10 – 0 ) * 6 = 60
Burada ilk ürün alınmayacaksa, zaten elimizdeki torbadaki ürünlerin değeri 0 olacağına göre 10 kg boş yerimizi elimizdeki alınabilecek en yüksek değer ile çarpıyoruz ( bir önceki adımdan farklı olarak ilk ürünü artık alamayacağımız için en yüksek değer 6 olmuştur).
Yukarıdaki hesaplamalar, üst limitleri belirtiyor. Bu üst limitlerin yanında ağacımızda o ana kadar alının ürünlerin toplam değeri ve ağırlığıda tutulmaktadır. Bazı durumlarda toplam ağırlık kapasitemizi aştığı için ve problem 0-1 torba problemi olduğu için mümkün olmayan durumlarla karşılaşılmıştır.
Netice olarak 1 ve 3 numaralı eşyaları alıp 2 ve 4 numaralı eşyaları almazsak en yüksek torba değerine ulaşılmış olunur.
Ağacın oluşturulması sırasında, önce sol tarafı oluşturmaya çalıştım. Bu sırada 8 numaralı düğüme (node) kadar ulaşmış oldum. Bu değere ulaştıktan sonra kökten ayrılan sağ tarafın hesaplanması, daha fazla hesaba gerek kalmadan budama (prunning) yapılmasını sağlıyor çünkü elimizdeki limitten daha kötü bir üst limit elde ediyoruz. Yani 8 numaralı düğümde torba değerimizi 65 olarak hesapladık. 2 numaralı düğümde ise üst limiti (alınabilecek en yüksek değeri) 60 olarak bulduk. Demek ki bu düğümün altında, 60′dan daha büyük bir değer olamaz. O halde bu düğümün altındaki hiçbir düğümün hesaplanmasının anlamı yoktur çünkü bütün ihtimaller daha kötü olacaktır.
İşte yukarıdaki son paragraf dallanma ve sınırlandırma (Branch and bounding) yaklaşımının özünü oluşturur.
SORU-2: Smith Waterman Dizgi Yaslama (String Alignment) Algoritması hakkında bilgi veriniz.
Dizgi yaslama işlemi (string alignment), basitçe iki dizgiyi alıp bu iki dizgideki ortak alanları bulmayı amaçlar. Bu tip algoritmalar, özellikle gelişen biyobilişim (bioinformatics) çalışmaları ile, genler arasında benzerlik bulunmasının önem kazanmasıyla günümüzde hatırı sayılır bir öneme haizdir.
Algoritma Hakkında
Smith-Waterman algoritması, Needleman-Wunsch algoritmasına oldukça benzemektedir ve bir çeşidi olarak görülebilir. Algoritma 1981 yılında, algoritmaya ismini veren iki bilimadamı tarafından ortaya atılmıştır. Algoritmanın en önemli özelliği, dinamik programlama (dynamic programming) kullanan yapısıdır. Bu tip dinamik programlama kullanan algoritmaların ortak özelliği, ikame masfufu (substitution matrix, yer değiştirme matrisi) kullanmaları ve boşluk değerleri için bir boşluk fonksiyonu kullanmalarıdır (gap scoring function).
Needleman-Wunsch algoritmasından tek farkı, eksi değerli skorlar yerine 0 kullanıyor olmasıdır.
Algoritmanın Çalışması
Algoritma, bir masfuf (matrix) inşa ederek çalışmaya başları. Bu masfuf aşağıdaki özelliklerdedir:
H(i,0) = 0 , 0 <= i <= m
H(0,j) = 0 , 0 <= j <= n
if( ai == bj )
w(ai,bj) = w (uyar)
else
w(ai,bj) = w (uymaz)
Şekil-1.5: Algoritmanın Çalışması
Yukarıdaki matris tanımından anlaşılacağı üzere yaslama işlemi (alignment) yapılan iki dizgi a ve b olarak gösterilmiştir. Ayrıca m, a’nın boyutu ve n de b’nin boyutunu gösteren değerlerdir. i ve j değişkenleri ise, bu matristeki ve dolayısıyla dizgilerdeki kaçıncı elemanın işlendiğini tutan birer değişken sayıdır. H(i,j) değeri, matris üzerindeki bir sayıya işaret etmekte olup bu değer, a[1...i] ve b[1...j] şeklinde gösterilebilecek, a’nin i. ve b’nin j. elemanına kadar olan en yüksek benzerlik skorunu tutmaktadır.
w(x,y) şeklinde gösterilen fonksiyon ise, parametre olarak aldığı iki değer için boşluk skorunu (gap score) tutmaktadır.
Örnek: Örnek olarak, aşağıdaki iki dizgiyi (string) ele alalım:
a= G CACGCTG
b= GA CGCGCG
Bu iki dizgi için n = 8 ve m = 8 olarak hesaplanabilir. Şimdi matrisin oluşturulmasına geçelim:
Bütün ceza durumlarını -1 olarak verelim. Bu ceza durumları şunlardır:
- w(boşluk ) = -1
- w(a,-) = -1
- w(-,b) = -1
- w(uymaz) -1
Bütün iyi durumları da +2 olarak alalım ki tek durum uyma durumudur:
- w(uyar) = 2
Şimdi matrisimizi oluşturabiliriz:
Şekil-1.6: Matris (adım 1)
Öncelikle, yukarıda görüldüğü üzere, boş bir tablonun satır ve kolon başlıklarına birer harf gelecek şekilde iki dizgi de (string) yerleştiriliyor.
Şekil-1.7: Matris (adım 2)
Ardından, w(a,-) ve w(-,b) şartlarının -1 olmasından dolayı bu durumdaki satır ve sütuna 0 değeri yerleştiriliyor. Bu yerleştirmenin sebebi, değerin eksi değere düşmesi halinde 0 ile gösteriliyor olmasıdır.
Şekil-1.8: Matris (adım 3)
Yukarıdaki ilk satır ile işe başlayalım. Burada seçtiğimiz satırın başlığı, G olarak atanmış. Bu durumda kolon başlığında da G olması halinde +2, farklı bir değer olması halinde ise -1 ekleyerek devam edeceğiz. İlk başlangıç değerimiz satırın ilk kolonunda yazan 0 değeri. O halde ikinci kolonda G-G uyması olduğu için +2 değer konuluyor. Ardından gelen 3. kolon başlığı C olduğu için ve uymadığı için -1 ekleniyor, yani bir önceki değer olan 2′den 1 çıkarılarak 1 bulunuyor. Ardından gelen A değeri yine G ile uymadığı için -1 eklenerek 0 bulunuyor. Sonra yine G-G uyumu bulunarak +2 ekleniyor ve 2 değeri yazılıyor ve işlem satır sonuna kadar bu şekilde devam ediyor.
Şekil-1.9: Matris (adım 4)
Gelelim bir sonraki satıra. Bu satır, A ile başladığı için, kolon başlıklarını A ile karşılaştıracağız. A-G karşılaşması bir uyuşmama durumudur. Dolayısıyla -1 kuralını işletiriz. Bu hücrenin bir üst satırındaki hücrede 2 bir sol hücrede ise 0 olduğunu görüyoruz. Bu değerlerden en büyüğünü alıyoruz (ki bu 2′dir) ve -1 ekleyerek yazıyoruz, şu halde 1 olmuş oluyor.
Ardından A-C uyuşmazlığı gelir ve hem bir üst satırda hem bir sol kolonda 1 olduğu için -1 kuralıyla 0 bulunur. Ardından A-A uyuşması ile +2 işletilir ki hem satır hem sütundan 0 geldiği için hücre değeri 2 olmuş olur. Bu işlem satır sonuna kadar bu şekilde devam eder. Yani uyuşma durumunda +2 ve uyuşmama durumunda -1 eklenerek işlemeye devam edilir. Ekleme işlemi ise bir üst hücre veya bir sol hücredeki en büyük değer seçilerek devam eder.
Şekil-1.10: Matris (adım 5)
Yukarıda, bu tablonun tamamının doldurulmuş hali gösterilmektedir. Buna göre tablodaki her hücre için, ilgili satır ve sütun başlıkları kontrol edilir, şayet eşitlerse bir üst hücre veya bir soldaki hücreden en büyük değerde olanı alınır ve +2 eklenir, şayet eşit değilse bu üst ve sol hücrelerin en büyüğünden 1 çıkarılır.
Sonuç olarak herhangi bir satır ve sütun için o ana kadarki en çok benzerlik durumunu veren bir ölçü geliştirilmiş olur.
Örneğin, GCACG ile GACG dizgilerinin (string) mesafesi tablodan 4 olarak okunabilir. Bunun anlamı iki dizgi arasındaki mesafeye en büyük değerlerin seçimi ile ulaşılması halinde, iki dizginin birbirine yaslanması mümkündür.
Tablodaki en yüksek değerler ile sol üst köşeye kadar dönecek olursak:
Şekil 1.11: Matris (adım 6)
Yukarıdaki şekilde, iki dizginin birleştirildiği noktalarda tartışmasız tek seçenek olan durumlar kırmızı, aynı yere ulaşmayı sağlayan birden fazla alternatifin olduğu durumlar ise mavi olarak gösterilmiştir. Bu durumda iki dizginin alacağı değerlerin en yüksekleri seçilerek sol üst köşeye ulaşacak birden fazla alternatif bulunmuştur. Bu yolların hepsi aynı sonuca varmaktadır ancak örnek olarak aşağıdaki seçimi yapacak olursak (konunun anlaşılması için bir tanesini seçtiğimizi düşünelim).
Şekil-1.12: Matris (adım 7)
Şekilde görüldüğü üzere, iki dizgi arasındaki bağlantı aşağıdaki şekilde kurulabilir:
GCACG
G-ACG
bağlantısı kurulabilir. Bu bağlantıda iki dizgi arasında kopan tek nokta bulunmuş olur.
SORU-3: Permütasyon Sıralaması (Permutation Sort) hakkında bilgi veriniz.
Algoritma asılnda oldukça basit bir yapıya sahiptir. Basitçe bir sayı dizisinin bütün permütasyonları sırasıyla denenir ve bunlardan birisinin sıralı olarak bulunması halinde algoritma sona erer.
Algoritmayı basitçe aşağıdaki adımlar şeklinde yazmak mümkündür:
Dizi sıralı olana kadar,
Dizinin permütasyonunu al
Bu durumu aşağıdaki kod ile gerçeklemek mümkündür. Örneğin, sıralamak istediğimiz dizi aşağıdaki şekilde verilmiş olsun:
2 6 8 1
Bu dizinin permütasyonları alınarak sıralanmış olana kadar permütasyon işlemi devam ettirilir, bu sayı n! ile hesaplandığına göre 4 elemanlı dizi için 4! = 24 ihtimal bulunmaktadır. Bu ihtimaller sırasıyla işlenir:
2 6 8 1
2 8 6 1
2 6 8 1
8 6 2 1
2 6 8 1
2 6 1 8
2 1 6 8
1 2 6 8
Yukarıdaki permütasyonlardan sonuncusu sıralanmış halidir dolayısıyla çalışma durdurulur.
Yukarıdaki algoritmanın C dilinde kodlanmış hali aşağıdaki şekildedir:
Şekil-1.13: C kodu
Yukarıdaki kodda, siralimi fonksiyonu, basitçe bir dizide bulunan elemanları baştan sona kontrol etmektedir. Bu kontrol sırasında dizideki herhangi bir eleman, sağındaki elemandan büyükse, olumsuz, şayet bütün elemanlar küçükse olumlu sonuç döndürmektedir.
İkinci fonksiyonumuz olan permsirala fonksiyonu ise, dizinin bütün alternatif permütasyonlarını oluşturmaktadır. Bu sırada diziyi ve boyutunu parametre aldığı gibi, özyineli (recursive) olarak çalışıtğı için en son kaldığı konumu da üçüncü bir parametre olarak almaktadır.
Ayrıca her permütasyon ihtimalini denedikten sonra dizinin sıralı olup olmadığını da kontrol etmektedir.
SORU-4: Gnome Sıralaması (Gnome Sort) hakkında bilgi veriniz.
Algoritma basitçe aşağıdaki şekilde anlatılabilir:
Yanyana duran ve sıralama kriterini bozan bir ikili bulana kadar dizide hareket edilir.
Bu ikili bulununca düzeltilir ve dengeye ulaşana kadar gerekirse dizinin başına kadar geri dönülür.
Ardından işlem kaldığı yerden devam eder.
Şimdi bu adımların örnek bir dizi üzerinde nasıl uygulandığını görelim.
Örneğin sıralamak istediğimiz dizi aşağıdaki şekilde olsun:
2 6 8 1 3 7 4 9 0 5
Sol baştan başlayarak yanlış duran, yanyana iki sayı arıyoruz.
2 6 8 1 3 7 4 9 0 5
2,6 doğru
2 6 8 1 3 7 4 9 0 5
6,8 doğru
2 6 8 1 3 7 4 9 0 5
8,1 hatalı (büyük sayı sağda olmalı) Yer değitşirilir ve gelinen en son nokta olan 3. eleman akılda tutulur:
3 <-> 2
2 6 1 8 3 7 4 9 0 5
kararlı olan kadar tekrar kontrol edilir.
2 6 1 8 3 7 4 9 0 5
6,1 hatalı değiştirilir:
2 <-> 1
2 1 6 8 3 7 4 9 0 5
2,1 hatalı değiştirilir:
1 <-> 0
1 2 6 8 3 7 4 9 0 5
dizide kalıdığımız yerden devam ediyoruz. En son 3. elemana kadar ilerlemiştik buradan devam edelim:
8,3 hatalı değiştirilir ve 4. elemana kadar gelindiği akılda tutulur.:
4 <-> 3
1 2 6 8 3 7 4 9 0 5
6,3 hatalı değiştirilir
1 2 6 3 8 7 4 9 0 5
3 <-> 2
1 2 3 6 8 7 4 9 0 5
Kalınan yerden devam edilir:
1 2 3 6 8 7 4 9 0 5
8,7 hatalı değiştirilir ve 5. eleman kalınan yer olarak saklanır:
5 <-> 4
1 2 3 6 7 8 4 9 0 5
Hatasız kalınan yerden devam edilir:
1 2 3 6 7 8 4 9 0 5
8,4 hatalı değiştirilir:
6 <-> 5
1 2 3 6 7 4 8 9 0 5
7,4 hatalı değiştirilir:
5 <-> 4
1 2 3 6 4 7 8 9 0 5
6,4 hatalı, değiştirilir:
4 <-> 3
1 2 3 4 6 7 8 9 0 5
Kalınan yerden devam edilir:
1 2 3 4 6 7 8 9 0 5
Sorunsuz devam edilir:
1 2 3 4 6 7 8 9 0 5
9,0 hatalı değiştirilir:
8 <-> 7
1 2 3 4 6 7 8 0 9 5
7 <-> 6
1 2 3 4 6 7 0 8 9 5
6 <-> 5
1 2 3 4 6 0 7 8 9 5
5 <-> 4
1 2 3 4 0 6 7 8 9 5
4 <-> 3
1 2 3 0 4 6 7 8 9 5
3 <-> 2
1 2 0 3 4 6 7 8 9 5
2 <-> 1
1 0 2 3 4 6 7 8 9 5
1 <-> 0
0 1 2 3 4 6 7 8 9 5
Kalınan yerden devam edilir:
0 1 2 3 4 6 7 8 9 5
9,5 Hatalı yer değiştirilir:
9 <-> 8
0 1 2 3 4 6 7 8 5 9
8 <-> 7
0 1 2 3 4 6 7 5 8 9
7 <-> 6
0 1 2 3 4 6 5 7 8 9
6 <-> 5
0 1 2 3 4 5 6 7 8 9
Görüldüğü üzere dizinin son elemanına kadar ulaşılmış ve dizi sıralanmıştır. Bu algoritmanın kodu aşağıdaki şekilde yazılabilir:
Şekil-1.14: C kodu
SORU-5: Tarak Sıralaması (Comb Sort) hakkında bilgi veriniz.
Algoritma, çıkışı itibariyle kabarcık sıralaması (bubble sort) ve hızlı sıralama (quick sort) karışımı olarak düşünülebilir.
Tarak sıralaması aslında anlaşılması oldukça kolay bir algoritmadır. Ancak anlamak için kabarcık sıralamasının (bubble sort) anlaşılması gerekir.
Kabarcık sıralamasında, sayılar sürekli olarak bir yanındaki sayı ile karşılaştırılır ve istenen sıraya göre yer değiştirme işlemi (swapping) yapılır. Tarak sıralamasında da benzer bir yaklaşım vardır ancak karşılaştırma ve yer değiştirme işlemleri hemen yan yana olan sayılar arasında değil de daha uzaktaki sayılar arasında yapılır. Yani kabarcık sıralaması bu anlamda aslında tarak sıralamasının özel bir örneği (mesafenin 1 olduğu ) olarak düşünülebilir. Tarak sıralamasında mesafe herhangi bir sayı olabilir.
Örnek olarak aşağıdaki şekilde verilmiş bir sayı dizisini ele alalım:
2,6,8,1,3,7,4,9,0,5
Yukarıdaki dizide 10 eleman bulunmakta ve ilk başta arama boşluğumuz (gap) 10/1,24 = 8,06 yani 8 olacak.
2 6 8 1 3 7 4 9 0 5
Bu durumda 0. elemandan başlayan algoritmamız, 0. eleman ile boşluk miktarı ilerisindeki elemanı karşılaştıracak ve 8. eleman ilk elemandan küçük olduğu için (9. sırada 0 var) yer değiştirme (swapping) işlemi yapılacaktır.
0 <-> 8
0 6 8 1 3 7 4 9 2 5
Ardından sıradaki elemana bakıyoruz yani 1 ile 8 fazlası olan 9 karşılaştırılıyor ve yine 9. sıradaki sayı, 1. sıradaki sayıdan küçük olduğu için, yer değiştirme (swapping) işlemi yapılıyor.
1 <-> 9
0 5 8 1 3 7 4 9 2 6
Ardından dizinin sonuna ulaşıldığı için bu defa yeni boşluk (gap) hesaplanıyor : 8/1,24 = 6 olarak bulunuyor.
A[0] = 0 ve A[6] = 7 olduğu için değişme olmuyor,
A[1] = 5 ve A[7] = 9 olduğu için yine değişme olmuyor,
A[2] = 8 ve A[8] = 2 olduğu için küçük olanı öne alıyoruz ve değiştirme işlemi yapılıyor:
2 <-> 8
0 5 2 1 3 7 4 9 8 6
Dizinin sonuna kadar olan sayılarda bir değişme olmuyor ve yeni boşluk değeri hesaplanıyor, 6/1,24 = 4 olur:
A[0] = 0 ve A[4] = 3 değişme olmaz,
A[1] = 5 ve A[5] = 7 değişme olmaz
A[2] = 2 ve A[6] = 4 değişme olmaz
A[3] = 1 ve A[7] = 9 değişme olmaz
A[4] = 3 ve A[8] = 8 değişme olmaz
A[5] = 7 ve A[9] = 6 olduğu için yer değiştirirlirler:
5 <-> 9
0 5 2 1 3 6 4 9 8 7
Dizinin sonuna ulaştığımız için yeni atlama değeri (gap) hesaplanır: 4/1,24 = 3
A[0] = 0 , A[3] = 1 değişme olmaz,
A[1] = 5 , A[4] = 3 olduğu için yer değiştirirler:
1 <-> 4
0 3 2 1 5 6 4 9 8 7
A[2] = 2 ve A[5] = 6 değişme olmaz
A[3] = 1 ve A[6] = 4 değişme olmaz
A[4] = 5 ve A[7] = 9 değişme olmaz
A[5] = 6 ve A[8] = 8 değişme olmaz
A[6] = 4 ve A[9] = 7 değişme olmaz
Dizinin sonunda yeni boşluk değeri hesaplanır (gap) : 3/1,24 = 2
A[0] = 0 ve A[2] = 2 değişme olmaz
A[1] = 3 ve A[3] = 1 yer değiştirilir:
1<->3
0 1 2 3 5 6 4 9 8 7
A[2] = 2 ve A[4] = 5 değişme olmaz
A[3] = 1 ve A[5] = 6 değişme olmaz
A[4] = 5 ve A[6] = 4 yer değiştirilir
4 <-> 6
0 1 2 3 4 6 5 9 8 7
A[5] = 6 ve A[7] = 9 değişme olmaz
A[6] = 5 ve A[8] = 8 değişme olmaz
A[7] = 9 ve A[9] = 7 yer değiştirilir:
7 <-> 9
0 1 2 3 4 6 5 7 8 9
Yeni boşluk değeri 2/1,24 = 1 olarak bulunur:
Bu durumda yan yana duran sayılardan sadece 5. ve 6. elemanlar düzeltilerek sıralama tamamlanır:
5 <-> 6
0 1 2 3 4 5 6 7 8 9
Görüldüğü üzere sıralama işlemi sırasında en kötü durum olan 1 aralıklı, komşuların kontrol edildiği durum en son olarak işlenir. Yani sırasıyla aşağıdaki atlama miktarlarında kontrol edilmiştir:
8, 6 , 4 , 3 , 2 , 1
Yukarıdaki atlama miktarlarından sadece sonuncusu 1 kabarcık sıralaması, diğerleri ise hızlı sıralama (quick sort) gibi dizinin bir seçim değerine göre (pivot) ikiye bölündüğü durumu olarak algılanabilir.
Yukarıdaki işlemleri yapan kod aşağıdaki şekilde yazılabilir:
Şekil-1.15: C kodu
SORU-6: Flash Sort (Şimşek Sıralaması, Bora Sıralaması) hakkında bilgi veriniz.
Bu sıralama algoritması yapısal olarak aslında araya ekleme sıralamasının (insertion sort) özel bir hali olarak kabul edilebilir. Sıralama algoritmaları arasında parçalı sıralama özelliği olan diğer sıralama algoritmaları ile aynı sınıfta kabul edilebilir.
Bora sıralamasında amaç, bir veri kümesinin bilinen bir dağılıma uygun olması durumunda bu özelliğini sıralama algoritmasında kullanmaktır. Yani şayet elimizde olan ve sıralamak istediğimiz sayılar gauss dağılımına (guassian distribution) uygunsa bu sıralama algoritması tarafından bir avantaj olarak kullanılabilir.
Parçalı sıralama algoritmaları (partial sort algorithm) dağılımdan gelen bu özelliği kullanarak sıralamadan önce bir aşamada sayıları mümkün olduğunca sonuçta olacakları yere yaklaştırmayı hedefler. Ardından herhangi bir sıralama algoritması ile işlem tamamlanır.
Örneğin elimizde tekdüze dağılıma sahip (uniform distribution) bir veri kümesi varsa, bu durumda her sayının gideceği adresi aşağıdaki şekilde hesaplayabiliriz:
Şekil-1.16: Adres hesaplama
Diğer bir deyişle sıralanmış halindeki konumu olan S(Ai), verilen aralıktaki ( A azami ve asgari aralığı) bulunduğu konum içerisinde asgari değere olan uzaklığı ile ölçülebilir. Sayı kümemizin 0′dan başladığını kabul edersek 1 ilave edilebilir. Ayrıca kaç sayımız olduğu, yukarıdaki m sembolü ile ifade edilmiştir.
Bu durumu bir örnek üzerinden anlatalım. Örneğin elimizde 1 ile 10 arasında 5 adet sayı olsun ve bu sayılar tekdüze dağılım (uniform distribution) özelliği gösteriyor olsun. Diyelim ki sayılarımız aşağıdaki şekilde verilmiş olsun (kolaylık olması için sadece tek sayıları alacağım):
3,5,1,7,9
Şimdi her sayıyı doğru konuma koymak için aşağıdaki şekilde bora sıralamasını kullanalım.
İlk sayımız 3, sayı aralığımızı aşağıdaki şekilde hesaplayabiliriz:
Şekil 1.17: Sayı aralığı hesaplama
Neticede 3 sayısının 2. sırada olacağını bulduk. Benzer şekilde diğer sayıları hesaplayalım:
Şekil-1.18: Sayı aralığı hesaplama
Sonuç olarak dizimizdeki elemanları aşağıdaki şekilde bulduğumuz sıralı yerlere yerleştiriyoruz.
|
Sayı |
1 |
3 |
Boş |
5 ve 7 |
Boş |
9 |
|
Konum |
1 |
2 |
3 |
4 |
5 |
6 |
Tablo-1.2: Sayıları tabloya yerleştirme
Yukarıda görüldüğü üzere, sonuçta bulunacakları adresler, bazı sayılar için kesin olarak bulunmuş oldu, bazı sayılarda ise çakışmalar bulunuyor. Bu çakışmaları önlemek için dizi yeniden işlenebilir veya sayının bulunduğu anda müdahale edilebilir. Örneğin yukarıdaki hesaplamalar sonucunda, sonuç dizisine yazmak yerine bulduğumuz adreslerdeki sayıları verilen değerlere göre sıraladığımızı düşünelim:
1,3,7,5,9
Yukarıdaki sıralamada 7 ve 5 aynı adrese geldikleri için (collision) kötü ihtimal olan sırasız halini ele aldım. Bu durumda örneğin kabarcık sıralaması (bubble sort) kullanmamız halinde bile algoritmamız çok daha hızıl sıralama işlemini yapıp sonuca ulaşacaktır.
Elbette sayılar yukarıdaki örnekte olduğu gibi her zaman mükemmel olmayabilir, tam bir dağılım özelliği göstermeyebilir. Bu durumda son halinin üzerinden geçilerek bir sıralama işlemi yapılması gerektiği kesindir. Bu aşamada istenen bir sıralama tercih edilebilir ancak sıralanmış diziyi en az işleyen algoritmalar tercih sebebidir.
SORU-7: Maximal Munch (Azami Lokma) Yöntemi
Algoritma, bilgisayar programlama veya bilgisayar bilimlerinde genelde derleyici tasarımı (compiler design) gibi dizgi (string) işlemenin yoğun olduğu alanlarda büyük lokma (maximal munch) veya en uzun eşleşme (longest match) olarak geçmektedir. Buradaki amaç, anlık olarak en büyük lokmayı oluşturmak ve yutmak veya aranan koşulları sağlayan en uzun yapıyı koparmak olarak düşünülebilir. Tam olarak aynı olmasa da aç gözlü yaklaşımına (greedy approach) yakın bir yapıdır.
Örneğin sözcük analizi sırasında (lexical analysis), programlama dilinde gelen yazıların anlamlı kelimelere bölünmesi gerekmektedir. Diyelim ki aşağıdaki şekilde verilmiş bir satır olsun:
int a=b+c;
Bu satırı insan olarak biz okuduğumuz zaman, bu satırın içeriğinin a isimli bir değişkene b ve c değişkenlerinin toplamını koymak amacında olduğunu anlıyoruz. Ancak bilgisayar açısından henüz işlenmemiş olan bu dizgi (string) aslında bir boşlukla ayrılmış ve birisi 3 diğeri 6 karakter uzunluğunda iki kelimeden oluşan bir satırdır. Buradaki sembollerin taşıyabileceği çok sayıda anlam bulunduğundan derleyici (compiler) bu kelimeleri sözcük analiziden geçirmek zorundadır.
İlk harfi ele alalım ve soldan sağa işlemeye çalışalım. İlk kelimenin ilk harfi i harfi ve bu harf C dilinde farklı anlamdaki komutların başlangıcına işaret ediyor olabilir. Örneğin if, int gibi komutlar olabileceği gibi bir değişken veya fonksiyon ismi de olabilir. Demek ki ilk harften sonra bir anlam çıkarmaya çalışıyoruz ancak ihtimalleri azaltarak sonuna kadar gitmek zorundayız. Ardından gelen n harfi ihtimalleri azaltıyor nihayetinde gelen t harfi ile kelime tamam oluyor. Böylelikle bir kelimeyi analiz etmiş oluyoruz. Benzer durum ikinci kelime için de geçerli ancak ikinci gelime 3 değişken ve 3 sembolden oluşmakta ve kelime bunlara teker teker bölünecek.
Algoritmanın çalışmasını daha iyi anlayabilmek için, sözcük analizinde (lexical analysis) algoritmanın nasıl kullanıldığını anlatalım.
Örneğin bize sözcük analizinde kullanılmak üzere düzenli ifadeler(regular expressions) verilmiş olsun. Bunları kullanarak sözcükleri analiz etmemiz istniyor ki, LEX (flex) gibi diller için de durum tam olarak bundan ibarettir.
Yaklaşımımız öncelikle düzenli ifadeleri (regular expressions), birer sonlu durum otomatı (finite state automaton) şekline çevirmek ve ardından gelen girdi cümlesini bu otomatlarda en fazla yolu gidecek şekilde parçalamak olacaktır.
Örneğin kullandığımı 3 adet düzenli ifade aşağıdaki şekilde verilsin:
T_Do do
T_Double double
T_Mystery [A-Za-z]
Buna göre bir kelime, do, double veya tek harfli herhanbi başka bir kelime olma ihtimaline sahiptir.
Şekil-1.19: Algoritma
Yukarıdaki şekilde, 3 farklı ihtimali de içeren ayrı birer sonlu durum makinesi çizilmiştir. Bu makineleri kullanarak aşağıdaki örnek cümleyi analiz edelim:
“DOUBDOUBLE”
Bu kelimeyi yukarıdaki kurallara göre parçalamak istiyoruz:
Şekil-1.20: Algoritma
Öncelikle, bütün makinelerimizde başlangıç durumuna gidiyoruz ve ardından gelen ilk harfe göre makinelerimiz çalışmaya başlıyor:
Şekil-1.21: Algoritma
Yukarıda görüldüğü üzere, ilk harf olan d harfi bütün makineler için kabul edilir bir harftir. Bu durumda bütün makinelerdeki ilk geçiş, geçilmiş ve başlangıç durumundan, bir durum ileri gidilmiştir. Burada dikkat edilecek diğer bir nokta, makinelerimizden birisinin kabul durumuna (final state) gelmiş olmasıdır. Bu durumda son makinemiz, bize bu harfi yani “d” harfini kabul ettiğini söylüyor. Öyleyse biz d harfine bir gösterici koyarak bu harfi kabul ettiğimizi söyleyebiliriz ve bu gösterici bir sonraki güncellemeye kadar bekliyor. Şimdilik lokmamızda sadece “d” harfi var.
Şekil-1.22: Algoritma
İkinci harf olan “o” harfi işlendiğinde ise sadece ilk iki makinede ilerleme sağlanmıştır. Son sırada yer alan ve 1 harf uzunluğundaki kelimeleri parçalamaya yarayan makine, kelime uzunluğu 1′den fazla olduğu için ilerleyemeden kalır. Tam bu noktada büyük lokma (maximal munch) algoritmasının anlamı ortaya çıkar. Buna göre artık son makineden bir sonuç çıkma ihtimali yoktur çünkü ilk iki makine her durumda, son makineye göre daha büyük bir lokma işleyecektir.
Ayrıca ilk makinemiz son durumuna (final state) ulaştığı için bir önceki adımda, lokmamızda olan “d” harfini güncelliyoruz, çünkü iki harfli daha uzun bir lokmayı kabul eden bir durum ile karşılaştık ve yeni lokmamız “do” oluyor.
Şekil-1.23: Algoritma
Ardından gelen u harfi için işleme devam etmektedir. Tek alternatif olan ikinci makinemizi çalıştırıyoruz. Şu ana kadar bitiş durumuna eriştiğimiz en uzun lokmamız “do” kelimesi.
Şekil-1.24: Algoritma
Girdi olarak aldığımız kelimeden bir harf daha ilerleyerek b harfine kadar işlemiş oluyoruz. Girdi kelimemizin “DOUBDOUBLE” olduğunu hatırlarsak, şu anda 4. harfe kadar gelmişiz demektir. Ayrıca en büyük lokmamız henüz “do” kelimesi.
Bu aşamadan sonra ne yazık ki hiçbir makinemiz ilerleyemeyecek. Bunun sebebi şu ana kadar işlediğimiz kelimenin “DOUBD” olması ve bu kelimeyi kabul eden makinemizin bulunmaması.
Elimizde şimdiye kadar işlediğimiz en büyük lokma olan “do” kelimesini, ilk çıktı olarak basıyoruz. Ardından bu kelimenin devamı olan girdimizdeki 3. harfe yani u harfine geri dönerek işlemeye devam ediyoruz.
Şekil-1.25: Algoritma
Kelimenin yeni başlangıç değeri için U kabul edilecek ve bu harf sadece 3. makineye uygun bulunacak. Bu durumda ikinci kelimemiz U olmuş oluyor. Benzer durum B için de geçerli ve üçüncü kelime olarak B harfini çıkarıyoruz:
DO+U+B
şeklinde şimdiye kadar parçalama işlemini tamamladık. Girdimiz “DOUBDOUBLE” kelimesiydi, bu kelimenin ilk 4 harfi böylece işlenmiş oldu gelelim diğer harflere.
Diğer harfler yeniden makineye girecek ve en büyük lokma oluşana kadar işlenecek.
Şekil-1.26: Algoritma
Kısacası, geriye kalan harflerin 3 makine için işlenmesi sonucunda ikinci makine tarafından kabul edilmesi söz konusudur. Bu durumda parçalama işlemi aşğaıdaki şekilde sonuçlanacaktır:
DO+U+B+DOUBLE
görüldüğü üzere en büyük lokma (maximal munch) algoritmasını basit bir girdi ve 3 makine üzerinde çalıştırdık ve bize, bu makineler tarafından üretilebilecek en uzun alternatifleri döndürdü.
SORU-8: Horspool Algoritması hakkında bilgi veriniz.
Algoritmanın gayesi, bir metin içerisinde verilen bir dizginin (string) aranmasıdır. Literatürde arama yapılan metin için T (ingilizcedeki Text (metin) kelimesinden gelmektedir) ve aranan kelime için P (ingilizcedeki Pattern (örüntü) kelimesinden gelmektedir) kullanılmaktadır.
Şekil-1.27: Örüntü
Klasik bir arama, yukarıdaki temsili resimde gösterilmiştir.
Algoritmanın diğer metin arama algoritmalarından en büyük farkı, aranan dizgi (pattern) yerine aranılan metin (text) üzerinden işlem yapmasıdır.
Şekil-1.28: İşlem yapma
Yukarıdaki şekilde gösterildiği gibi, üzerinde arama işleminin yapıldığı metinde bulunan herhangi bir x harfi için, aranılan metin üzerindeki (pattern) en solda olan harf bulunmaya çalışılır.
Şekil-1.29: Uyuşma
arama işlemine başlanan α değerinin ardından gelen harfler eşleştiği sürece, aranan kelimenin (pattern) sonuna kadar eşleştikçe arama işlemi devam eder. Şayet bu aşama uyuşmayan bir harf olursa, zaten aranan kelimenin olmadığı sonucuna varılabilir.
Şekil-1.30: Kelime arama
Ardından uyuşma olup olmadığına bakılmaksızın aranan kelimeyi ß değerine denk gelecek şekilde kaydırma işlemi yapılır. Bu kaydırmanın olması için kelimenin (pattern) kalan kısmında ikinci bir değerinin bulunmaması gerekir.
Örnek
Algoritmanın çalışmasını bir örnek üzerinden göstermeye çalışalım.
Şekil-1.31: Algoritma
Yukarıdaki şekilde görüldüğü üzere, aranan kelime için (P) sağdan sola doğru bir numaralandırma yapılmakta ve bu numaralandırma üzerinden aşağıda gösterilen HpBc dizisi çıkarılır. Bu dizi üzerinden metin üzerinde arama yapılır.
Sırasıyla önce, A harfi aranır. Ardından C ve G harfleri aranır. Dikkat edilirse, kelimede üç kere geçen A harfi, dizide bir kere kullanılmaktadır.
Uyuşma oldukça aranan kelime kaydırılmaktadır.
Müsvette Kod (Pseudo Code)
Algoritmanın müsvette kodu aşağıdaki şekilde yazılabilir:
Horspool (P = p1p2…pm,T = t1t2…tn)
Önişleme Aşaması
For c Î ∑ Do d[c] ← m
For j Î 1…m-1 Do d[pj] ← m – j
Arama Aşaması
pos←0
While pos ≤ n-m Do
j ←m
While j > 0 And tpos+j = pj Do j ← j-1
If j = 0 bulundu: pos+1
pos ← pos +d[tpos+m]
End of while
Yukarıdaki algoritmada geçen değerlerin, örnek üzerindeki gösterimlerini adım adım anlatmaya çalışalım:
Şekil-1.32: Algoritma adımları
Yukarıdaki ilk adımda aranan ilk harf olan C harfi için öncelikle kümede olup olmadığı kontrolü yapılır. Bunun anlamı aranan kelimenin harflerinden birisi olmaması durumunda vakit kaybedilmemesidir.
İkinci adımda ise daha önce çıkardığımız HpBc[a] dizisine göre her harfin değerleri atanır.
Arama işleminin devamı aşağıdaki şekildedir:
GCATCGCAGAGAGTATACAGTACG
GCAGAGAG
pos ← 0 + d[t0+7] , pos ← 0 + d[A], pos ← 1
Yukarıda görüldüğü üzere, kelimenin ilk kontrolü için 1. pozisyonda bulunan A değeri aranmıştır. Ardından arama işlemi sonraki harf olan G için aşağıdaki şekilde devam eder:
GCATCGCAGAGAGTATACAGTACG
GCAGAGAG
pos ← 1 + d[t1+7] , pos ← 1 + d[G], pos ← 3
G harfi ile C harfinin tutuşmamasından dolayı aranan kelime (P) kaydırılmıştır.
GCATCGCAGAGAGTATACAGTACG
GCAGAGAG
pos ← 3 + d[t3+7] , pos ← 3 + d[G], pos ← 5
Benzer şekilde C ve G uyuşmamasından dolayı bir kere daha kaydırıyoruz.
GCATCGCAGAGAGTATACAGTACG
GCAGAGAG
While j > 0 And tpos+j = pj Do j ← j-1
If j = 0 pos+1′de bulundu
pos ← 5 + d[t5+7] , pos ← 5 + d[G], pos ← 7
Görüldüğü üzere aranan kelime ile uyuşma oldu. Algoritmamızın ilgili satırlarını çalıştırıyor ve bulunduğunu rapor ediyoruz.
GCATCGCAGAGAGTATACAGTACG
GCAGAGAG
pos ← 7 + d[t7+7] , pos ← 7 + d[A], pos ← 8
İkinci kere A harfini arıyoruz.
GCATCGCAGAGAGTATACAGTACG
GCAGAGAG
pos ← 8 + d[t8+7] , pos ← 8 + d[T], pos ← 16
Bulunan A harfi G harfi ile uyuşmadığı için atlıyoruz:
GCATCGCAGAGAGTATACAGTACG
GCAGAGAG
pos ← 16 + d[t16+7] , pos ← 16 + d[G], pos ← 18
pos > n-m // pos >23-7
while döngüsünden çık.
Son olarak kelimenin sonuna gelindiği halde aranan yazı bulunmadığı için algoritma sonlanıyor.
Algoritmanın Karmaşıklığı
Algoritmanın zaman karmaşıklığı aranan kelimenin boyu n ve aranılan kelimenin boyu m olmak üzere O(mn)’dir denilebilir. Çünkü en kötü durumda her aranılan kelime harfi için üzerinde arama yaptığımız her harfi karşılaştırmamız gerekebilir. Bu da her n harf için m adet karşılaştırmaya eşittir.
SORU-9: Yeknesan (Invariant , Değişmez) hakkında bilgi veriniz.
Bilgisayar bilimlerinde, bir programın incelenmesi sırasında, herhangi bir kaziyenin (predicate, haber, önerme), çeşitli işlemler uygulanmasına karşılık yeknesan olması halidir.
Diğer bir deyişle, program çalışır ve çeşitli işlemlerden geçer, ancak bazı şeyler değişmeden kalıyor ve ne kadar işlem yapılırsa yapılsın değişmiyorsa buna yeknesan (değişmez, invariant) ismi verilir.
En basit örneği, bir değişkenin (variable) içine değer atanması ama hiç değiştirilememesidir.
int a = 10;// işlemlerprintf("%d",a);
Yukarıdkai kodda, a değerini ilgilendiren işlemler yapılmıyorsa, a için yeknesan olmuş denilebilir. Farklı bir örnek de döngülerde görülür:
for(int i = 0;i<10;i++){ printf("%d",i);}
Yukarıdaki döngüde, i<10 şartında bulunan 10′dan küçük olma şartı yeknesan olmuştur. Yani döngünün dönmesi sırasında değişmeden kalmaktadır. Hemen bu noktada önemli bir farkı belirtmek gerekir. Yeknesean kavramı, sabit kavramı ile (constant) karıştırılabilmektedir. Örneğin yukarıdaki döngüde bulunan 10 sayısı bir sabittir ve yeknesan değildir. 10′dan küçük olma durumu ise değişmeden kalan bir yeknesan olarak düşünülmelidir. Yukarıdaki tip yeknesan örneklerine, özel olarak daire yeknesanı (loop invariant, döngü değişmezi) ismi de verilebilir.
Bu yaklaşım, ayrıca program analizi veya problem analizi sırasında da kullanılabilir. Örneğin MU Probleminin çözümünde, yeknesan bir değer yakalamaya çalışmak gerekir. Benzer şekilde Hoare Mantığı (Hoare Logic), programların belirli bölümlerinde, ön ve son koşullar belirleyerek bunların yeknesan olmasını sağlamaktadır.
Bu yöntem, bilgisayar bilimleri dışında da kullanılır. Örneğin, kanun yapım sürecinde, bütün kanunlarda değişmeyen kötü hasletler vardır. Bu yüzden, hırsızlık veya cinayet gibi suçlar, kanunlar değişmesine karşılık, bütün kanunlarda yerini almaktadır. Bu anlamda, değişen kanun sistemleri için, hırsızlık, yeknesan bir suçtur denilebilir.
SORU-10: MU Bulmacası (MU Puzzle) hakkında bilgi veriniz.
Bilgisayar bilimlerinde de kullanılan ve normal gösterim elde etmeyi hedefleyen, özellikle de sondan normal şekil gösterimi için oynanan bir oyundur.
Oyunun aşağıda tanımlı olan kuralları ile MU kelimesinin elde edilip edilemeyeceği sorulur:
- Oyunda kullanılabilecek harfler M, I ve U’dur. Bu harfler dışında harf kullanılamaz.
- Oyuna MI kelimesi ile başlanır.
- Oyundaki kelimenin sonuna, U harfi eklenebilir. (Örneğin MI kelimesi MIU yapılabilir)
- Oundaki M harfinden sonra gelen harfler ikiye katlanabilir (Örneğin MIU kelimesi, MIUIU şeklinde, M harfinden sonra gelen IU kelimesini iki kere yazarak elde edilmiştir)
- Ardışık olarak tekrar eden I harfleri U harfine dönüşebilir. Örneğin MUIIIU kelimesi MUUU kelimesine dönüştürülebilir.
- Çift U harfi silinebilir. Örneğin MUUU kelimesi, MU kelimesine dönüşebilir.
Sorumuz, başlangıçta bulunan MI kelimesinin, yukarıdaki kurallar kapsamında MU olarak yazılıp yazılamayacağıdır.
Sorunun cevabı, bu şekilde bir geçişin imkansız olduğudur.
Bu tip bir problemin çözümünde bir “yeksan” (invariant, değişmez) aramak gerekir. Yani problemdeki değişen durumlara karşı yeksan olan (değişmeyen) bir kararlı hal yakalanabiliyor mu diye bakılması gerekir.
Çözümün ispatlanması sırasında I harfi içeren kelimelere bakmamız gerekir. Bu harfin (I) sayısını değiştiren kurallar, yukarıdaki listede, 4. ve 5. Kurallardır. 4. kural, sondaki harfleri iki misline katlamaktadır. Dolayısıyla sonda bulunan I harfi kaç tane ise bunun sayısı iki misline katlanacaktır. 5. kuralda ise I harfleri U harfi ile değişebilmektedir.
Bu iki kural dışında I harfinin sayısını değiştiren bir kural yoktur.
Ayrıca, başlangıçta 1 adet I harfi ile başlanmaktadır.
Şimdi kurallarımızı tekrar gözden geçirdiğimizde, I harflerinin sayısının 2 misline çıkabildiğini veya 3 I harfinin U harfine döndüğünü görürüz. Ayrıca oyunun bitmesi için hiç I harfi bulunmamalıdır.
Bu şartlarda oyun bitmez çünkü:
- Başlangıçtaki I sayısı (ki 1ile başlar) 3′e tam bölünmez.
- 3′e tam bölünemeyen bir sayının iki misli de üçe bölünmez.
- 3′e bölünemeyen bir sayıdan 3 çıkarmak ta sayıyı 3′e bölünür hale getirmez.
Dolayısıyla hiç I içermeyen MU kelimesine ulaşılması imkansızdır.
SORU-11: Örtüşen Alt Problem (Overlapping Subproblem) hakkında bilgi veriniz.
Bilgisayar bilimlerinde, özellikle özyineli (recursive) problemlerde, problemin bir kısmının tekrar edilmesi durumudur. Örneğin, klasik bir problem olan fibonacci sayıları örneğinde, örtüşen altproblem bulunmaktadır.
Fibonacci serisinin 4. terimini hesaplamak isteyelim ve bunun için aşağıdaki fonksiyonu yazmış olalım:
Matematiksel olarak : fib(0) = 1, fib(1) = 1 ve fib(n) = fib(n-1)+fib(n-2)
Programlama dillerinde:
fib(int n){
if(n==0||n==1)
return 1;
return fib(n-1)+fib(n-2);
}
Yukarıdaki bu fonksiyonun çalışması sırasında çağırdığı alt fonksiyonları bir ağaç şeklinde çizecek olursak:
Şekil x
Yukarıdaki ağaçta, görüldüğü üzere, bazı terimler birden fazla kere hesaplanmaktadır. Örneğin fib(2) fonksiyonu, hem fib(4) hesaplanırken, hem de fib(3) hesaplanırken hesaplanmaktadır. Bu anlamda, problemin bir alt grubunun (subproblem) örtüştüğünden (overlap) bahsedebiliriz. Genelde bu tip örtüşmelerde, hız kazandırmak ve problemin sadece bir kere çözülmesini saplamak için dinamik programlama (dynamic programming) yöntemi kullanılır.
SORU-12: Bin Packing (Kutulama Problemi) hakkında bilgi veriniz.
İyileştirme problemleri açısından klasik bir örnektir (optimisation problems). Problem basitçe bir kutunun içerisine en az boş alan bırakarak, eşyaların en iyi şekilde nasıl yerleştireceği olarak düşünülebilir.
Aslında problemi boyutlara göre incelersek aşağıdaki şekilde bir liste yapılabilir:
Tek boyutlu kutulama (1D bin packing): Bu problemde amaç bir çizgi veya hat gibi görülebilecek yapının içerisine farklı boyutlardaki çizgileri yerleştirmek olarak düşünülebilir.
Örneğin zaman çizelgelemesinde, bir kişinin yapacağı işleri, zaman çizgisinin üzerine yerleştirmesi (ve her işin farklı miktarda zaman gerektirdiğini ve kişinin çalışma saatlerinin sınırlı olduğunu düşünürsek en fazla işi en az zamanda (örneğin 8 saatlik mesailer içinde) yapması ) bir problemdir. Buradaki hem kişinin yapacağı işler hem de bu işlerin yerleştirileceği zaman çizgisi tek boyutludur.
Daha basit olması açısından örneğin 100m uzunluğundaki bir ipi 7 ve 9m uzunluğundaki parçalara en az fire ile bölmek istiyoruz, en verimli bölme işleminde kaç adet 7 ve kaç adet 9 uzunluğunda ipimiz olur gibi soruları düşünebiliriz. Bu tip sorular tek boyutlu kutulama problemleridir.
İki boyutlu kutulama problemleri (2D bin packing optimization): Bu grupta bir tablodan ve iki boyuttan bahsedilebilir. Örneğin kot pantolon üreten bir tekstil firmasında farklı boyutlardaki Pantolon kalıplarının en az fire ile 5x5m büyüklüğündeki bir kare kumaştan kesilmesi isteniyor olsun. Bu problem iki boyutlu (x ve y boyutları) bir kutulama problemidir. Benzer bir problem, bir gazetedeki seri ilanların, en az fire ile sayfaya yerleştirilmesi olarak da düşünülebilir.
3 boyutlu kutulama (3D bin packing) problemin en zor şekli olarak tanımlanır ve 3 boyutlu bir kutunun içerisine konulan her şeklin farklı x,y ve z boyutlarında şekiller olması olarak düşünülebilir. Problemin genel tanımını yaptığımız için belirteyim, örneğin ev taşıma sırasında çıkan eşyaların kutulanması olarak düşünebilirsiniz. Bu durumu daha da karmaşık yapmaktadır çünkü kutular farklı boyut ve şekillerdedir (örneğin silindir bir varil veya küp veya dikdörtgenler prizması gibi kutuların içerisine yerleştirme yapılmakta) ve kutulanan şekillerde farklıdır ve hatta girintilidir (concave , non-convex) (örneğin avize, koltuk, sandalye gibi birbirinin içine girebilen nesneleri düşününüz). 2 ve 3 boyutlu paketleme, paketlenen nesnelerin girintili olup olmamasına göre ikiye ayrılmaktadır. Dış bükey nesnelerin paketlenmesi nispeten daha basit bir problemdir. Ancak nesnelerin iç bükey olması halinde problem biraz daha karmaşıklaşır.
Hatta literatürdeki kısıtlı aramalarım sonucunda ulaşabildiğim kadarıyla tam olarak iç bükey nesnelerin paketlenebildiği bir sonuç ne yazık ki bulamadım. Örneğin iki vidanın en verimlim paketlenmesi sırasında vidaların girinti çıkıntılarının üstüste gelmesi gerektiğini tecrübi olarak biliyoruz. N adet vida için bu durum birbirini tekrar eden bir hal alır. Gerçekten farklı boylarda ve adım sıklığında ve çaplarda vidalar verilse bu durumda en verimli paketlemeyi yapabilen bir algoritma henüz görmedim.
Paketlenen nesnelere göre problemin sınıflandırılması:
Paketlenen nesne çeşitlerinin sabit olması ve ön tanımlı olması halinde problem homojen olarak tanımlanır. Örneğin tek boyutlu kutulama probleminin tanımı sırasında verilen ve “100m uzunluğundaki bir ipi 7 ve 9m uzunluğundaki parçalara en az fire ile bölmek” şeklinde geçen örnek bu tip homojen (homogenous) bir yapıdadır. Buna karşılık heterojen bir problemde, paketlenecek nesnelerin tipleri ya tamamen birbirlerinden farklıdır ya da aynı tipte çok az tekrar vardır. Yine tek boyutlu problem örneğinde verilen zaman çizgisi üzerinde farklı uzunluklardaki randevuların yerleştirilmesi bu tiptendir.
Bu anlamda aşağıdaki problemler, kutulama probleminin birer özel hali olarak düşünülebilir:
- kamyon yükleme problemi (truck loading),
- konteyner yükleme problemi (Container loading problem, CLP)
- şerit paketleme problemi (Strip Packing problem, SPP)
Yukarıda, problemin tanımını yaptıktan sonra çözümlere bir göz atalım:
Homojen tek boyutlu problem çözümü
Şayet problem tek boyutlu ise ve homojen nesnelerin paketlenmesi olarak problemin çözülmesi isteniyorsa problem oldukça basit demektir ve basit matematiksel hesaplamalar ile problemi çözebiliriz.
Örneğin tek nesne ve tek paket varsa işlem basitçe paketin nesneye bölümü olarak bulunur (zaten burada zor Bir şey de yok):
Örneğin 100m uzunluğundaki bir ipten kaç tane 5m uzunluğunda ip kesilebilir:
100 / 5 = 20
biraz daha zorlaştırıp ip sayısını 2 çeşide veya 3 çeşide çıkarırsak problem np-tam (np-complete) bir hal alır. Örneğin aşağıdaki kodu inceleyelim:
Şekil x
Kodda görüldüğü üzere bütün ihtimaller denenmektedir. Basitçe herhangi bir k değeri için, k-a ve k-b değerlerini denemekte ve denenen duruma göre a veya b değerini bir arttırmaktadır. Aslında kodumuz basit bir ikili ağaç (binary tree) oluşturmaktadır:
Önce 9 veya 7 ile başlanması ihtimalleri:
Şekil x
Sonra bu ihtimallerin de 7 veya 9 azalma ihtimalleri:
Şekil x
Yukarıda gösterildiği gibi her düğümden yine ikişer ihtimal indirerek bir alt seviyeye geçilebilir. Neticede 0 olana kadar yapılan bir aramadır ve 0 sonucuna birden farklı yoldan ulaşılabilir.
Kodumuz çalıştırıldığında çözüm olarak aşağıdaki sonuçları üretmektedir:
cozum : 7*4 + 9*8
cozum : 7*13 + 9*1
Gerçekten de problemin iki farklı çözümü bulunmaktadır.
Yukarıdaki kod basit bir hesaplama ile, 2′nin üstlerinin toplamı kadar adım hesaplamaktadır.
Bu değer yukarıdaki ağaçtan çıkarılabilir:
ilk düğüm için tek ihtimal 2 0
ikinci seviye için iki ihtimal: 2 1
üçüncü seviye için dört ihtimal 2 2
şeklinde gitmektedir ve örneğin problemimiz üçüncü seviyede çözülseydi (sonuç 0 olsaydı) o zaman karmaşıklığımız bu değerlerin toplamı olacaktı ve 2 0 + 2 1 + 2 2 şeklinde hesaplanacaktı.
Bu değer, ikili ağaçlardan bilindiği üzere 2 n-1 şeklinde hesaplanabilir.
Görüldüğü üzere yukarıdaki algoritma O(2 n) değerinde bir karmaşıklığa sahiptir ve bu değer bir çok terimli (polynom) değildir yani algoritmanın karmaşıklık sınıfı np-tam (NP-Complete) olarak belirtilebilir. Ayrıca yukarıdaki k değeri için bir çözüm bulunmuştur ancak çözüm bulunamasaydı bu değer k terim için denenecekti. Yani 100 için çözüm yoksa bir yaklaşığı olan 99 için ardından iki yaklaşığı 98 için … Bu işlem hiç çözüm bulunamaması halinde k terim için denenecekti.
Yukarıdaki problem, dinamik programlama (dynamic programming) kullanılarak iyileştirilebilir. Bunun sebebi arama işlemi sırasında bazı sonuçların tekrar etmesidir. Örneğin yukarıdaki ikili ağacı aşağıdaki şekilde çizebiliriz:
Şekil x
Farka dikkat ederseniz, 93 – 9 = 84 ve aynı zamanda 91-7 = 84 olduğu görülür. Bu durumda aslında 84 değeri bir önceki kodda iki farklı durum için aranmaktayken şimdi tek bir durum için aransın istiyoruz. Elbette 84 sadece bir örnektir ve buna bağlı olarak çok sayıda tekrar eden değer bulunmaktadır.
Hesaplanan bu değerleri bir dizi (array) içerisinde tutup tekrar hesaplanmasını engellemek istiyoruz:
Şekil x
Yukarıdaki yeni kodda, bir dizi içerisinde tek döngü ile sonucu hesaplattık. Buna göre algoritmamız iki elemanlı bir diziyi kullanmakta, dizinin 0. elemanları 7lerin sayısını ve 1. elemanları ile 9ların sayısını saymaktadır.
Kodumuz ilk başta 0 için 0 tane 7 ve 0 tane 9 gerektiği gerçeği ile çalışmaya başlıyor. 14-23. satırlar arasındaki döngü basitçe i. terim için i-a ve i-b değerlerine bakıyor. Şayet i. terim için i-a veya i-b değerinde bir çözüm varsa (nereden geldiğini önemsemeksizin) bu çözüme bulduğu koşulu ilave ederek mevcut i değeri için çözümü kaydediyor. Şayet bu iki terim de bulunmuyorsa o zaman bir sonraki i değerine geçiyor.
Ekran çıktısı aşağıdaki şekildedir:
Şekil x
Son 20 satır görülmekle birlikte daha önceki satılar alıntılanmamıştır. Ayrıca son 20 satırda görüldüğü üzere, tamamına ait bir çözüm bulunmaktadır. Örneğin 88 sayısı için 7*10 + 9*2 = 88 sonucuna ulaşılmıştır. En son satırda ise 100 için 7*13 + 9*1 sonucu görülmektedir.
Görüldüğü üzere birden fazla sonuç olsa bile tek bir sonucu görmekteyiz bunun sebebi veri yapısının bir sonuç üretmek üzere tasarlanmış olmasıdır. Elbette daha farklı veri yapıları kullanılarak diğer çözümleri de gösteren sonuçlar elde edilebilir.
Yukarıdaki algoritmanın karmaşıklığı ise bir öncekine göre oldukça iyi sayılabilecek O(n) olarak bulunur. Bunun sebebi dizideki her elemanın üzerinden tek bir kere geçiyor olması ve dolayısıyla tek bir döngünün (koddaki 14-21 satırlar arası) çalışıyor olmasıdır.
SORU-13: Amortized Algorithm Analysis (İtfa Tahlili, amotize algoritma analizi) hakkında bilgi veriniz.
Bilgisayar bilimlerinde algoritma performansının değerlendirilmesinde kullanılan yöntemlerden birisidir. Kısaca, bir algoritmanın en kötü durumunu araştırırken (worst case analysis) en kötü durumun olma ihtimallerinin de beraberinde incelendiği tahlil yöntemidir.
Klasik bir en kötü durum analizi (worst case analysis) yöntemi algoritmanın çalışacağı en kötü durumu ortaya atar ve buradaki performansı ölçer. Örneğin kabarcık sıralaması (bubble sort) ele alalım. Bu algoritma için en kötü durum sıralanacak olan grubun tersten sıralı olarak verilmesidir (küçükten büyüğe doğru sıralamak istediğimiz bir örneğin, tam ters sıralı olarak büyükten küçüğe sıralı olması durumudur).
Ancak itfa tahlil (amortized analysis) yöntemi ile bu en kötü durumun olma ihtimali de göz önünde bulundurulur. Bu durumu daha basit bir örnek üzerinden anlatmaya çalışalım.
Örnek Değişken boyutlu dizi (Dynamic Array)
Bir değişken dizi uygulaması sırasında (dynamic array) hafızada dizinin ne kadar yer kaplayacağı önceden bilinemez. Amacımız hafızada eklenen eleman sayısı arttıkça kapladığı yeri artan bir dizi kodlamaktır. Örneğin tek eleman eklenmesi halinde hafızada tek eleman, beş eleman eklenmesi halinde ise hafızada beş elemanlık yer kaplayan bir dizi kodlamak istiyoruz.
Bu dizi kodlamasının en klasik uygulaması, dizi için hafızada ayrılan yerin dolması halinde, hafızadaki kaplanan yerin iki misline çıkarılmasıdır. Ayrıca iki misline çıkınca önceki dizide bulunan verilerin, yeni diziye kopyalanması da gerekmektedir. Örneğin hafızada iki elemanlı yer ayırarak başladık, 2 eleman dolduğunda hafızadaki yeri 4 elemana çıkarıyoruz, ve eski dizideki iki elemanı, yeni diziye kopyalıyoruz, 4 eleman dolduğunda 8 elemana çıkarıyoruz ve eski dizideki 4 elemanı yeni diziye kopyalıyoruz v.b.
Şimdi eleman ekleme işleminin hafızadaki karmaşıklığına bakalım. En kötü durum analizi bize, analiz sırasında karşılaşılabilecek en kötü durumu hesaplamamız gerektiğini söyler. Buna göre en kötü durum hafızanın dolması ve yeni yer açılmasıdır. Dizimizde n eleman olduğunu kabul edersek dizi dolduğu anda n elemanlık bir yer daha açılacaktır. Bu durumda algoritma karmaşıklığımızın O(n) olduğunu söyleyebiliriz. En kötü durum analizine göre doğru olan bu yaklaşım, itfa tahlilinde (amortize algoritma analizi) geçerli değildir.
Algoritmamızın çalışması sırasında n. elemanın eklenmesi için yeni açılan diziye n eleman kopyalanacaktır. Bu kopyalama işleminden sonra dizimize n adet eleman daha alabileceğiz. Dolayısıyla 2n elemanı eklemek için n adet kopyalama işlemine ihtiyaç duyuyoruz. O halde basit bir hesaplama ile 2n elemanın eklenmesi için 2n işlem + n adet kopyalama = 3n işlem yapmaktadır. Karmaşıklık ise basitçe O(3n/2n) = O(3/2) = O(1) olarak bulunur (sabit değerlerin big-oh değerinin 1 olduğunu hatırlayınız örneğin O(20) = O(1) )
Yukarıdaki işlem sırasında akla gelebilecek bir soru, n adet elemanın eklenmesi işleminin altında da kopyalama işlemleri yapıldığıdır. Örneğin 16 eleman eklenmesi için kaç adım gerektiğini hesaplayalım:
2 eleman eklenir
2 eleman daha eklemek için 2 kopyalanır 2 eklenir: toplam 4 eklenmiş 2 kopyalanmıştır
4 eleman daha eklemek için 4 kopyalanır 4 eklenir: toplam 8 eklenmiş 6 kopyalanmıştır
8 eleman daha eklemek için 8 kopyalanır 8 eklenir: toplam 16 eklenmiş 14 kopyalanmıştır
Yukarıdaki gidişten anlaşılacağı üzere n ekleme için yaklaşık olarak n kopyalama yapılmaktadır. Yani limit alındığında ve eklenen elemanların sayısı sonsuza götürüldüğünde kopyalanan elemanların sayısı neredeyse eklenen elemanların sayısına eşit çıkacaktır. O halde her yeni eleman eklenmesinin maliyeti 1′e daha da yaklaşacaktır (n / n = 1 olduğu için)
Örnek : Binom Sırası (Binomial Queue)
Farklı bir örnek olarak Binom Sırasına bakalım (binomial queue).
Öncelikle sıranın nasıl ilerlediğini hatırlayalım :
B0:
Şekil x
B1:
Şekil x
B2:
Şekil x
B3:
Şekil x
B4:
Şekil x
Yukarıda görüldüğü üzere, her binom sırasında, o ana kadar olan sıraların tamamı içerilmektedir. Örneğin B4′ün kolları, sırasıyla B0, B1, B2, B3 ağaçlarını barındırmaktadır.
Bu durumda i. Terim için aşağıdakine benzer bir hesaplama sayısı gerekmektedir:
T(i) = T(i-1) + 2 – C
ve T(0) = 0
Yukarıdaki özyineli formülde (recursive function) i. adım için T(i) adet ağaç gerekmektedir. Elbette bu sayı da T(i-1) ile gösterilen ve i. adım öncesinde bulunan ağaç sayısına 2-C eleman eklenerek bulunmaktadır. Bu denklemdeki C ile gösterilen değer, i. adımın maliyetini (bilgisayardaki hesaplama işlem maliyeti, hafıza maliyeti vs. ) göstermektedir.
Yukarıdaki işlemi biraz daha ilerletirsek :
C + T(i) – T(i-1) = 2
sonucunu bulur ve buradan bütün denklemleri yazıp toplarsak, n adet eleman için işlem maliyetini aşağıdaki şekilde bulabiliriz:
C 1 + T(1) – T(0) = 2
C 2 + T(2) – T(1) = 2
C 3+ T(3) – T(2) = 2
…
C n-1+ T(n-1) – T(n-2) = 2
C n + T(n) – T(n-1) = 2
Yukarıdaki denklemlerin toplamı olarak:
Ayrıca ilk denklem olarak T0 = 0 olduğunu biliyoruz çünkü binom ağacının ilk elemanı 0 çocuk sahibidir.
Dolayısıyla yukarıda bulduğumuz n eleman için 2n maliyeti bize yine itfa tahlili ile O(2n /n) = O(2) = O(1) sonucunu gösterir. Klasik bir en kötü durum analizi ile, bu hesaplamada, O(n) maliyet çıkaracaktık ancak amortize analiz bize burada bir adım ilerisini göstererek en kötü durumun tahlil edilmesinin aslında tek elemana bakılarak yapılamayacağını ve bütün elemanlar incelendiğinde aslında en kötü durumun o kadar da kötü olmadığını göstermektedir.
Ortalama durum ile Amortize algoritma analizinin farkı.
Bu anlamda ortalama durum analizinden (average case analysis) ayrılmaktadır. Yani ortalama durum analizinde bütün ihtimallerden ortalama maliyette bir ihtimal üzerinden analiz yapılır. Bütün ihtimaller hesaplanarak ortalamaları bulunur. Amortisman analiz yönteminde ise hala en kötü durumu analiz etmekteyiz ancak analizi bir adım daha genişleterek en kötü durumun beklenenden daha iyi olduğunu ispatlıyoruz. Yukarıdaki iki örnek için de , yani binom yığıtı (binomial heap) veya değişken boyutlu dizi (dynamic array) için de tek elemanın eklenmesi halinde yapılan en kötü durum analizinden daha iyi sonuçlar bulduk ancak bu sonuçlar hala en kötü durumda bulunacak sonuçlardır. Tek farkı en kötü durum analizinde tek bir elemana bakılmaktayken amortize algoritma analizinde olayın geneline baktık.
İtfa Tahlili Yöntemleri:
İtfa tahlilinde kullanılan 3 klasik yöntem bulunmaktadır.
Münasebet tahlili (aggregate analysis). Bu tahlil yönteminde, n terim için olan maliyet, özyineli bir fonksiyon olarak yazılır, örneğin T(n) olarak isimlendirelim ve bu değer n terime bölünür, örneğin T(n) / n değeri olarak hesaplanır. Yukarıdaki örneklerden binom ağacı (binomial tree) bu yöntemle tahlil edilmiştir.
Muhasebe tahlili (accounting analysis). Bu yöntemde, her ilave işlemin sisteme getirdiği yük anlık olarak hesaplanır. Örneğin n terimden sonra n+1 terim olduğunda ne kadar yükte değişiklik olacağı anlaşılmaya çalışılır. Sabit uzunlukta çalışma örnekleri alınarak sisteme getirdiği maliyete her elemanın etkisi ölçülür ve buradan n elemanlı bir çalışmaya maliyet genellemesi yapılır. Yukarıdaki örneklerden değişken boyutlu dizi (dynamic array) bu tahlile bir örnektir ve örnekte görüldüğü üzere 2,4,8,16 gibi çalışma uzunlukları alınarak bir genelleme yapılmıştır.
Kapasite tahlili (potential analysis). Bu yöntem, muhasebe tahlili ile tamamen aynıdır. Bu tahlil yönteminde de elemanlara bakılmak suretiyle genelleme yapılmaya çalışılır. Muhasebe tahlilinden tek farkı her elemanın sisteme yaptığı etkinin bir ödeme değil bir kapasite azalması olarak görülmesidir. Bu noktayı biraz açmak gerekirse:
Muhasebe tahlili ile kapasite tahlili aslında algoritma analizine muhasebe dünyasından girmiş yöntemlerdir. Muhasebede kullanılan ve bir birikim fonksiyonunu tahlil etmek için (debit function) kullanılan iki yaklaşımdan birisi, birikimi bir ödeme olarak görmek ve her adımda ödeme yapılmak suretiyle birikimi arttırmak iken ikincisi birikimi bir kapasite olarak görmek ve her adımda bu kapasiteyi azaltmak olarak görmektir.
Aslında ikisi de aynı değeri hesaplamaktadır ancak birincisi her adımda değerleri biriktirmekte, ikincisi ise başta birikimi hesaplayıp her adımda bu birikimi eksiltmektedir.
Yukarıdaki örneklerden değişken boyutlu diziyi (dynamic array) hatırlayacak olursak, bu dizideki maliyeti hesaplarken her elemanın sisteme etkisini bulmuştuk. Bazı elemanlar sisteme 1 etkisi yaparken bazı elemanlar n etkisi yapmıştı. Yani 2,4,8 gibi elemanlarda o ana kadar olan eleman kadar etki yapılmıştı. Ancak örneğin 1,3,5,6,7 gibi sayıların sisteme maliyeti 1 idi. Bu yaklaşım, muhasebe yaklaşımı olup, her elemanın sisteme artan miktardaki etkisi biriktirilmektedir.
Bu yaklaşımın tersi bir yorumla, ilk başta 2n maliyet getireceğini kabul edip (2n değerinde borç kredi aldığımızı kabul edin) her adımda bu değeri düşürmek (her adımın maliyetini kredimize geri ödeme olarak düşünüyor ve kredi borcumuzu kapatıyoruz) ise kapasite yaklaşımıdır (potential analysis).
SORU-14: Macar Algoritması (Hungarian Algorithm) hakkında bilgi veriniz.
Algoritma analizi konusunda geçen meşhur problemlerden eşleşme problemini çözmek için (matching problem, bazı kaynaklarda atama problemi (assignment problem) olarak da geçmektedir) macar araştırmacıların etkisi ile gelişen algoritmanın ismidir.
Algoritmanın ulaşmak istediği amaç, azami eşleşmeye ulaşmaktır. Bu adımda azami eşeleşmeyi tanımlayalım. Eşleşme problemleri (matching) iki grup altında incelenebilir:
- ikili eşleşme (binary matching)
- Azami eşleşme (maximum matching)
ilk gruptaki problemlerde iki üyenin eşleşmesi yeterlidir. Örneğin evlilik problemi gibi, en fazla kişinin evlendiği eşleşmeler veya bir arz / talep eşleşmesi gibi. Örneğin nehri geçmek isteyen kişilerin sallara atanması gibi en az kişinin açıkta kaldığı en fazla kişinin eşleştiği problemlerdir.
İkinci grup olan azami eşeleşme (maximum matching) problemleri ise bu yazının da konusu olan macar algoritması tarafından çözülmesi hedeflenen ve klasik eşleşmenin ötesinde, her eşleşmenin değer sahibi olduğu problemlerdir. Örneğin kişilerin iş bulması ama her işin ve kişinin farklı maaşlar alması halinde azami maaş (maximum maaş) değerine ulaşmak gibi. Yani sadece eşlemek yetmiyor bir de her eşlemeye bir skor verip bu değerin en fazla olmasını istiyoruz. Bu problemin tersten okunuşu ise asgari maliyettir. Yani aynı problemi bir şirket yöneticisi ele aldığında örneğin çalışanlarına en az maliyetle iş yaptırmak isteyecektir.
Problem ve macar algoritması (hungarian algorithm) daha iyi anlaşılsın diye bir örnek üzerinden anlatmaya çalışalım. Örneğin elimizde N adet çalışan ve N adet iş olsun ve her kişinin her işe atanması durumunda alacağı maaşları bir masfufta (matrix) göstermeye çalışalım:
|
|
İşler |
|||
|
Çalışanlar |
|
A |
B |
C |
|
S |
5 |
2 |
3 |
|
|
Ş |
6 |
1 |
4 |
|
|
T |
4 |
4 |
6 |
|
Tablo x
Yukarıdaki masfufta görüldüğü üzere 4 adet iş ve bu 4 iş için 4 adet çalışan atanmış, her hücrede ise ilgili çalışanın ilgili iş için alacağı ücret gösterilmiştir. Örneğin Ş çalışanının C işini yapması halinde 4 lira alacağını anlıyoruz.
Artık problem daha rahat anlaşılabilir. Amacımız yukarıdaki ücret değerlerine göre en düşük maaşlı işe atama olacaktır.
Problem masfuf olarak gösterilebileceği gibi bir şekil (graph) olarak da tasviri mümkündür:
Şekil x
Problem daha iyi anlaşılsın diye örnek bir çözümü ele alalım:
Şekil x
Yukarıdaki eşleşmede A-S , B-T ve C-Ş ataması yapılmıştır. Bunun maliyeti 5+4+4 = 13 olarak hesaplanabilir.
Elbette bu çözüm ihtimal havuzundakilerden sadece bir tanesidir. Basit bir olasılık hesabı ile 3×3 boyutundaki bir masfuf için 3! ihtimal olduğu görülebilir. Örneğin A için 3 ihtimal B için kalan 2 ihtimal ve C için tek ihtimal bulunmaktadır. Aynı şekilde nxn boyutundaki bir eşleşme (matching) problemi için de n! ihtimal bulunacaktır.
Klasik bir algoritma kodlanması durumunda n! ihtimalin incelenmesi gerekeceğinden problemi np-zor (np-hard) olarak görebiliriz. Ancak macar algoritması bu problemi çok terimli zamana (polinom, polynomial) indirgeyebilmektedir. Bu anlamda problem np-tam (np-complete) olarak görülebilir.
Gelelim problemin çözümüne. Problem, yukarıdaki şekilden den görüleceği gibi iki parçalı grafiğin (bipartite graph) doğru oluşturulması ile çözülebilmektedir.
Algoritma 3 adımdan oluşmaktadır.
- adımda 0-ağırlıkta kenarlar oluşturuyoruz (0-weighted edges) bunun için işler kısmında bakarak en düşük değerdeki kenarın ağırlığını diğer kenarlardan çıkarıyoruz ve benzer şekilde kişiler tarafından bakıp en düşük değerdeki kenarın ağırlığını diğer kenarlardan çıkarıyoruz.
Bu aşama aslında problemin yapısını değiştirmemektedir. Örnek problemimize bu adımı uygulayarak durumu görelim:
|
|
İşler |
|||
|
Çalışanlar |
|
A |
B |
C |
|
S |
5-4 |
2-1 |
3-3 |
|
|
Ş |
6-4 |
1-1 |
4-3 |
|
|
T |
4-4 |
4-1 |
6-3 |
|
Tablo x
Yukarıdaki masfufta görüldüğü üzere A için en düşük değer olan 4, B için en düşük değer olan 1 ve C için en düşük değer olan 3 bütün değerlerden çıkarılmış ve aşağıdaki masfuf elde edilmiştir:
|
|
İşler |
|||
|
Çalışanlar |
|
A |
B |
C |
|
S |
1 |
1 |
0 |
|
|
Ş |
2 |
0 |
1 |
|
|
T |
0 |
3 |
3 |
|
Tablo x
Şimdi çalışanlar açısından olaya bakıp en düşük değerleri çıkaralım:
|
|
İşler |
|||
|
Çalışanlar |
|
A |
B |
C |
|
S |
1 |
1 |
0 |
|
|
Ş |
2 |
0 |
1 |
|
|
T |
0 |
3 |
3 |
|
Tablo x
Masfufta bir değişiklik olmadı çünkü her çalışan için en düşük değer 0 idi. Ancak bu durum farklı örneklerde değişebilir. Şayet çalışanların ücretlerinde 0′dan büyük değer bulunursa bu değerlerin en küçüğü satır bazlı olarak bütün satırdan çıkarılacaktır.
Daha iyi anlaşılması için yukarıdaki masfufun şekil halini aşağıda verelim:
Şekil x
- Yukarıdaki yeni şekilde 0-ağırlığındaki düğümleri kullanarak eşleme yapıyoruz. Bu adımda azami akış (max-flow) veya dalgalı yol bulma (augmented path) algoritmaları gibi algoritmalardan istifade edebiliriz.
Eşleşme sonucu şayet mükemmel eşleşme ise (perfect matching) problem çözülmüştür. Şayet mükemmel eşleşme sağlanamazsa, sadece 0-ağırlığındaki kenarları kullanarak en düşük toplam değere sahip alt şekli (subgraph) buluyoruz.
- Adımdan, problem çözülene kadar 2. adıma dönüyoruz.
Problemimize dönüp 2. adımı uygulayalım. 0-ağırlığına sahip kenarların değerleri aşağıda verilmiştir:
Şekil x
Yukarıdaki şekilde bulunan en düşük maliyetli atamadır. Bu atama A-T , S-C ve Ş-B şeklindedir ve Orijinal değerleri hatırlanacak olursa, 4+3+1 = 8 olarak hesaplanabilir. İddiamız, bu şekilde daha düşük maliyetli bir atama yapılamayacağıdır.
SORU-15: Çemberi bölen doğrular problemi hakkında bilgi veriniz.
Problemimiz oldukça meşhur olan bir çemberin doğrular tarafından bölünmesidir.
Kabaca, bir çemberi 20 adet doğrunun en fazla kaç alana ayırabileceğini soralım.
Örneğin n=0 için alan sayımız 1′dir:
Şekil x
Bir doğru ile çemberi kestiğimizde iki alan çıkar:
Şekil x
ikinci bir doğru eklendiğinde 3 alan çıkmaktadır:
Şekil x
3 doğru için 7 alan bulunur:
Şekil x
4 doğru için 11 alan bulunur:
Şekil x
Şimdi yukarıdaki bulunan değerleri liste halinde yazalım:
0 1
1 2
2 3
3 7
4 11
Buradaki sayılardan çıkan bağlantı, her adımda, bir önceki değere adım değerinin eklenmesidir. Örneğin 4. adım, bir önceki adımdaki değer olan 7+4 = 11 şeklinde bulunmuştur.
Bu ağlantı aşağıdaki şekilde formülize edilebilir:
f(n) = n + f(n-1)
Görüldüğü üzere bu denklem özyineli bir denklemdir (recursive equation).
Şimdi bu özyineli denklemi çözmeye çalışalım. Bu tip denklemlerin çözümü için iki alternatif bulunur. Aslında ikisi de sonuçta aynı şeyi bulmayı amaçlar.
- Top Down Approach (Yukarıdan Aşağıya Yaklaşım)
- Bottom Up Approach (Aşağıdan Yukarıya Yaklaşım)
Denklemimizi önce Yukarıdan aşağıya yaklaşımla açalım:
f(n) = n + f(n-1)
f(n-1) = n-1 + f(n-2)
f(n-2) = n-2 + f(n-3)
…
f(1) = 2
f(0) = 1
Yukarıdaki ilk üç denklemi birbiri cinsinden yazalım:
f(n) = n + n-1 + n-2 + f(n-3)
Buna göre denklemin tamamı açıldığında aşağıdaki gibi bir bağlantı görülebilir:
f(n) = n + n-1 + n-2 + n-3 … + f(1)
Bu denklemi matematiksel olarak aşağıdaki şekilde gösterebiliriz:
Bu denklem de oldukça meşhur olan 1′den n’e kadar olan sayıların toplamı şeklinde düşünülebilir:
Aynı problemi bu defa aşağıdan yukarıya yaklaşımla çözelim (bottom up approach):
f(0)= 1
f(1) = 2
f(2) = 3
f(4) = 7
f(5) = 11
…
Buradaki denklemleri birbiri cinsinden yazıyoruz:
f(0) = 1
f(1) = 1 + f(0)
f(2) = 2 + f(1)
f(3) = 3 + f(2)
f(4) = 4 + f(3)
f(5) = 5 + f(4)
…
şeklinde yazdıktan sonra, son terimi çekelim:
f(5) = 5 + 4 + 3 + 2 + 1 + 1
Buradan çıkan netice ise bir önceki yöntemde elde ettiğimiz netice ile aynıdır.
f(n) = n + n-1 + n-2 … 5 + 4 + 3 + 2 + 1 + 1
dolayısıyla aynı denklemi yazabiliriz:
ve sonuç olarak aşağıdaki hesap doğrudur:
Yukarıdaki denklemleri elde ettikten sonra, bize ilk başta sorulan acaba 20 adet doğru olsaydı en fazla kaç alana bölünebilirdi sorusuna cevap bulabiliriz.
SORU-16: Algoritma (Algorithm) hakkında bilgi veriniz.
Bazen biz insanlar için çok kullanılan kelimeler, tanımlanması en güç kelimeler haline dönüşebiliyor. Algoritma da sanırım bilgisayar bilimleri için benzer özellikte olan bir kelime.
Sanırım bu kelimeyi tanımlarken “bir dizi matematiksel adım” ifadesini kullanmak yerinde olur.
Bütün algoritmalar, matematiksel olarak ispatlanabilen ve dizilimi kesinlikle önem taşıyan ve bir metot anlatmasıdır.
Aslında bir kelimeyi başka bir kelime ile ifade etmek bir hatadır. Şayet iki kelime aynı şeyi anlatıyorsa, o zaman bir tanesi fazla demektir ama genelde algoritma için bu “metot” veya “sistem” kelimeleri sıkça kullanılıyor. Oysaki algoritma bunlardan farklı olarak bir, matematiksel bir dizilimdir. Evet, bir problemi çözmek için gerek metodu anlatır bu çözümde kullanılan sistemi gösterir ama atlanmaması gereken ispat edilebilirlik, karmaşıklığının ölçülebilirliği aslında bilgisayar bilimlerinin de temelini oluşturur.
Bu durumu bir örnek üzerinden açıklamaya çalışalım.
Örneğin sık kullanılan sıralama problemini (sorting problem) ele alalım. Elimizde bir dizi karıştırılmış sayı var ve biz bunları küçükten büyüğe doğru sıralamak istiyoruz. Bu durumda birisinin çıkıp ben bunları sıraladım demesi bir algoritma olmaz. Çünkü algoritmanın sistematik olarak probleme yaklaşması gerekir.
Ben bunları sıralayan bir sistem yaptım demesi de algoritma olmaz. Çünkü sistemin bütün adımlarını belirli olması ve analiz edilebilir olması gerekir.
Ben bunları sıralayan ve şu adımlardan oluşan bir sistem yaptım demesi de yeterli olmaz çünkü bu algoritmanın her durumda çalışacağının matematiksel olarak ispatlanabilmesi gerekir.
Ancak bu aşamadan sonra bir algoritmadan bahsediyoruz demektir ve tam da bu aşamadan sonra artık algoritmanın performansından bahsetmeye başlar ve algoritmamızın hafıza ve zaman ihtiyaçlarını ölçebilmeyi isteriz.
Yukarıdaki bütün bu tanımların yanında, bir algoritma elde ettikten sonra ayrıca bu algoritmanın uygulanabilir oluşu da tartışmaya açıktır. Örneğin geliştirilen algoritma, günümüz bilgisayarları için uygulanabilir olmayabilir. Bu durum algoritmamızı , algoritma olmakta çıkarmaz ancak uygulanamaz bir hale sokar (örneğin şu anda yeni yeni gelişen kuantum hesaplama algoritmaları buna birer örnek olabilir).
Ayrıca algoritmaların yaklaşımlarına göre sınıflandırılması da mümkündür. Hatta bu sınıflandırmaya uygun olarak algoritma geliştirilme metodolojileri de bulunmaktadır. Örneğin nesne yönelimli programlama (object oriented programming) , yapısal programlama (structured programming) veya fonksiyonel programlama (functional programming) , Emirli programlama (Imperative Programing), özdevinirli programlama (automat based programming) gibi.
SORU-17: Post Karşılık Problemi (Post Correspondence Problem) hakkında bilgi veriniz.
Emil Post tarafından 1946 yılında ortaya atılan ve belirsiz karar problemi olarak sınıflandırılabilecek olan (undecidable decision problem) problemin ismidir. Literatürde kısaca PCP olarak da geçmektedir. Bu problem, yine Emil Post tarafından geliştirilen, Post Makinesi (post machine) olarak bilinen ve Turing makinesinin (Turing Machine) bir benzeri olan makinenin geliştirilmesini sağlamıştır. Problem, durma problemine göre (halting problem) daha basit bir yapıdadır bu yüzden belirsiz karar problemlerinin ispatlanmasında daha çok tercih edilir.
Problem aşağıdaki şekilde tanımlanabilir.
A bir alfabe olmak üzere, bu alfabeden tanımlı olan n elemanlı iki küme ele alalım:
X = {x1,x2, … xn }
Y = {y1,y2, … yn}
Bu iki küme üzerinden üretilen iki kelimenin eşit olması durumunu arıyoruz.
Örneğin aşağıdaki kümeleri ele alalım:
X = { sad , sek , er, i}
Y = { sa , se, ker , di }
Yukarıdaki örnek kümeler için böyle bir eşitlik bulunabilir:
sad + i + sek + er = sadiseker = sa + di + se + ker
Yukarıdaki eşitliğin sol tarafı X kümesinde, sağ tarafı ise Y kümesinden üretilmiştir.
Bazı problemler çözümsüz, bazı problemler ise birden fazla çözüme sahip olabilir. Örneğin aşağıdaki kümeleri ele alalım:
X= {a,ab,bba}
Y= { baa, aa, bb}
Çözüm aşağıdaki şekilde olabilir:
bba + ab + bba + a = bbaabbbaa = bb + aa + bb + baa
Ancak bu tek çözüm değildir. Örneğin aşağıdaki de bir çözüm üretebilir:
bba + ab + bba + a + bba + ab + bba + a = bbaabbbaabbaabbbaa = bb + aa + bb + baa + bb + aa + bb + baa
Yukarıdaki ikinci çözüm, bir önceki çözümün tekrarı niteliğindedir. Yani üretilen eşitlik dizilimi bir önceki çözümün iki kere tekrarlanması ile oluşturulmuştur. Bu işlem istenildiği kadar tekrar edilebilir.
Örneğin yukarıdaki kümelerin ilk elemanları çıkarılarak aşağıdaki kümeler oluşturulsaydı
X= {ab,bba}
Y= { aa, bb}
Bu kümelerden elde edilen bir çözüm bulunamazdı.
SORU-18: Merkezi poligon sayıları (Centeral Polygon Numbers) hakkında bilgi veriniz.
Bir dairenin verilen doğru sayısıyla kaç farklı parçaya bölünebileceğini veren sayı serisidir.
1, 2, 4, 7, 11 şeklindeki sayılara, merkezi poligon sayıları ismi verilir. Bu sayılar, verilen doğru sayısına göre, bir daireyi kaç farklı şekle böldüğünü gösterir.
Örneğin yukarıdaki sayı serisini eksi olmayan tam sayılar ile birebir eşleştirirsek
|
0 |
1 |
2 |
3 |
4 |
5 |
|
1 |
2 |
4 |
7 |
11 |
16 |
Yukarıdaki gibi bir tablo elde ederiz. Bu tablodaki ilk satır kaç doğru kullandığımız, ikinci satır ise kaç parça elde ettiğimizdir. Örneğin ilk sütunda 0 doğru kullanılmış ve daire en fazla tek parçaya ayrılabilmiştir. İkinci sütunda tek bir doğru ile daire iki parçaya ayrılmıştır. 3 doğru kullanarak daire en fazla 7 parçaya ayrılabilir. Bu durumu aşağıdaki şekilde görebilirsiniz:
Şekil x
Yukarıdaki şekilde görüldüğü üzere daire 7 farklı parçaya bölünmüştür.
Bu sayılara aynı zamanda, tembel pizzacı serileri (lazy caterer’s sequence) isimleri verilir. Bu ikinci isimlendirme, bir pizzacının, pizzasını en az bıçak darbesi ile en çok parçaya ayırma hevesinden gelmektedir J
Floyd üçgeninin (Floyd’s Triangle) ilk sütununu oluşturan bu seri, aşağıdaki formül ile hesaplanabilir:
k = (n2 + n + 2) / 2
Bu formülün çıkarılışı ise özyineli bir denklemin (recursive equation) çözümünden gelmektedir.
Dairenin her yeni çizgi ile bölünmesini aşağıdaki şekilde formülleyebiliriz:
T(n) = T(n-1) + n
Bu denkleme göre her yeni çizgi, daha önceki çizgileri keserek o ana kadar olan çizgi sayısının bir fazlası yeni parça oluşturacaktır. Örneğin, yukarıdaki şekle yeni bir çizgi ekleyerek bu durumu görelim:
Şekil x
Yukarıdaki şekilde görüldüğü üzere eklenen yeni doğru, daha önceden bulunan bütün doğrular ile kesişmelidir. Şekilde bu kesişim noktaları işaretlenmiştir. Yeni çizgi, şekle eklendikten sonra şekildeki alan sayısı 11′e yükselir. Daha önceden vâr olan 3 doğru ile kesiştikten sonra, yeni 4 alan çıkmaktadır. Dolayısıyla denklemimiz:
T(4) = T(3) + 4 , yani ilk üç doğru için oluşan alan + 4 yeni alan şeklinde özetlenebilir.
Bu denklemin çözümüne geçecek olursak:
T(n) = T(n-1) + n , denklemi yukarıdan aşağıda doğru açıyoruz (top-down approach)
T(n) = T(n-2) + n-1 + n
T(n) = T(0) + 1 + 2 + … + n , Denklemdeki T(0) değeri 1′dir çünkü daireyi kesmek için hiçbir çizgi kullanılmazsa (çizgi sayısı 0 ise) daire tek parçadır.
T(n) = 1 + 1 + 2 + … + n , Gauss’un 1′den n’e kadar olan sayıların toplamı formülünü yazarsak
T(n) = 1 + n(n+1) / 2 , toplama işlemini yaparsak
T(n) = (n2 + n + 2) / 2
Şeklinde denklemi çözmüş oluruz. Bu denklemin ve bu denklemden üretilen sayı serisinin bir diğer özelliği ise, üçgen sayılarının bir fazlası olmasıdır (triangle numbers).
SORU-19: Levenshtein Mesafe Algoritması (Levenshtein Distance) hakkında bilgi veriniz.
İki dizilim arasındaki benzerliği derecelendirmek için kullanılır. Pratikte arama sonuçlarında kelimeler arasındaki benzerliği derecelendirmek için kullanılmaktadır.
Basitçe, iki dizi, iki kelime, iki cümle gibi varlıklar arasındaki değiştirme ve ekleme işlemlerini tutar.
Örneğin
- Oyun- Ayan kelimeleri arasındaki mesafe 2 ‘dir çünkü ilk ve ikinci o harfleri a harfi ile değişmiştir.
- Arap – araba kelimeleri arasındaki mesafe 2′dir çünkü p harfi b ile değişmiş ve a harfi eklenmiştir.
Algoritma değişim değerini bulmak için iki boyutlu bir dizi (matris) üzerinde kelimelerin farklı olan harfleri için değer arttırımına gider.
Örneğin karşılaştıracağımız dizgiler (string) “Cuma” ve “Pazar” kelimeleri olsun.
|
C |
U |
M |
A |
||
|
0 |
1 |
2 |
3 |
4 |
|
|
P |
1 |
1 |
2 |
3 |
4 |
|
A |
2 |
2 |
2 |
3 |
3 |
|
Z |
3 |
3 |
3 |
3 |
4 |
|
A |
4 |
4 |
4 |
4 |
3 |
|
R |
5 |
5 |
5 |
5 |
4 |
Tablo x
Yukarıdaki tabloda, yeşil renkle gösterilen satır ve sütun, boş bir dizgi ile kelimenin karşılaştırılmasıdır. Örneğin boş bir kelime ile CUMA kelimesi karşılaştırılınca her har için ekleme yapılacak ve neticede CUMA kelimesinin uzunluğu olan 4 değerine ulaşılacaktır (satırın sonu 4 sayısı ile bitmiştir).
Yeşil satır ve sütunun altında ve sağında kalan alan ise teker teker harflerin karşılaştırıldığı bölümdür.
Örneğin C harfi ile başlayan sütundaki değerler, sadece C harfinden oluşan bir kelimenin PAZAR kelimesi ile karşılaştırılmasını adım adım gösterir. Buna göre M harfiyle başlayan sütundaki karşılaştırma CUM kelimesi ile PAZAR kelimesinin her harfinin karşılaştırılmasıdır. Bu sütundaki değerler (tabloda sarı renktedir) aşağıdaki şekilde okunabilir:
P-CUM : 3 , P harfini C ile değiştirip UM eklendiği için toplam 3 işlem gerekir
PA-CUM: 3, P harfi C ile, A harfi U ile değiştikten sonra M harfi eklenmiştir
PAZ-CUM : 3 , P harfi C ile, A harfi U ile ve Z harfi M ile değişmiştir, toplam maliyet 3′tür.
PAZA-CUM: 4, P harfi C ile, A harfi U ile ve Z harfi M ile değişmiştir, ilave olarak A harfi eklenmiştir.
PAZAR – CUM: 5, P harfi C ile, A harfi U ile ve Z harfi M ile değişmiştir ilave olarak A ve R harfi eklenmiştir.
Levenshtein mesafesi hesaplanırken, dizideki hesaplanmakta olan hücrenin üstündeki ve solundaki değerlerden faydalanılır. Herhangi bir hücre için aşağıdaki durum geçerlidir:
|
Harf1 |
|||
|
… |
|||
|
A |
B |
||
|
Harf2 |
… |
C |
D |
D hücresinin değeri, satırdaki ve sütundaki kelimelerin karşılaştırmasını yaparken, Harf1 ve Harf2 harflerini karşılaştırır. Bu karşılaştırmada 3 ihtimal bulunur.
Harf1 ile Harf2 eşittir. Bu durumda D değerine A değeri konulur. Bunun sebebi, A değerinin daha önceki (Harf1 ve Harf2 karşılaştırmasından önceki kelime parçası) mesafeye yeni bir mesafe eklemiyor olmasıdır.
Örneğin PARA ve YARA kelimelerini karşılaştırırken PA ve YA durumunda, Harf1 = Harf2 olur. Bu durumda PA-YA maliyeti 1′dir ve bu maliyet P ile Y nin değişmesinden gelen maliyettir, karşılaştırılan A değerleri için ilave bir maliyet olmaz.
|
P |
A |
||
|
Y |
1 |
||
|
A |
1 |
Yukarıdaki tabloda görüldüğü üzere A-A karşılaştırmasının değeri üst çaprazdan okunmuştur.
Şayet Harf1-Harf2 karşılaştırma sırasında, harfler birbirinden farklıysa, bu durumda yine çaprazdaki değer alınır ( yukarıdaki tabloda, D’nin değeri hesaplanırken A’nın değeri alınır) ve bu değere 1 eklenir.
Kısacası kelime boyları aynı olduğu sürece D değerinin hesabında A değeri kullanılır.
Şayet kelime boyları farklıysa, bu durumda Harf1 karşılığı olarak Harf2 bulunamayacak veya Harf2 karşılığı olarak Harf1 bulunamayacaktır. Her iki durum için de daha önceden bulunan ve kelimelerin son harflerinin karşılaştırmasına 1 eklenir.
Şayet Harf2 yok ve Harf1 varsa, bu durumda D değeri B+1 olarak hesaplanır.
Şayet Harf1 yok ve Harf2 varsa, bu durumda D değeri C+1 olarak hesaplanır.
Örneğin AS kelimesi ile ASİ kelimesini karşılaştırmak isteyelim:
|
A |
S |
||
|
0 |
1 |
2 |
|
|
A |
1 |
0 |
1 |
|
S |
2 |
1 |
0 |
|
İ |
3 |
2 |
1 |
Yukarıdaki kırmızı ile gösterilen hücrenin değeri, 1 olarak bulunmuştur. Bunun sebebi değerin bir harf eklemesi ile 0 maliyetli AS-AS karşılaştırmasına yapılması ve hemen üzerinde bulunan hücreden alınarak 1 arttırılmasıdır.
Daha genel anlamda, bir hücrenin değeri hesaplanırken iki ihtimal bulunur.
Karşılaştırılan harfler eşittir ( bu durumda sol çaprazdaki değer kopyalanır)
Harfler farklıdır ( bu durumda, hücrenin solu, üstü ve sol çaprazındaki değerlere 1 eklenerek en küçük olanı alınır. )
Yukarıdaki bu algoritmanın kodlanmış hali aşağıda verilmiştir.
Şekil x
Kodun 5-12. satırları, basit bir şekilde verilen 3 sayıdan en küçüğünü döndüren minimum fonksiyonudur.
41-46. satırlar arasında, fonksiyonumuz olan LevenshteinMesafesini, 4 parametre ile çağırıyoruz. Bunlar sırasıyla, 1. Kelime, 2. Kelime, 1. Kelimenin boyu, 2. kelimenin boyudur.
Burada boyların 1 fazla verilme sebebi, boşluk karakteri ile de yapılacak olan karşılaştırmadandır.
Kodun 16-19. Satırları arasında, matrisimizin ilk satırına ve ilk sütununa (yazının başındaki ilk tabloda yeşil ile gösterilen satır ve sütun) ardışık sayıları döşüyoruz. Bunun anlamı, kelimelerin boş kelime ile karşılaştırılma değerini tutmaktır.
Ardına 20-34. Satırlar arasında ana döngümüz başlıyor ve bu döngüde, kelimelerin her harfi, diğer kelimenin her harfi ile karşılaştırılıyor. Bu karşılaştırma sırasında iki ihtimal bulunuyor, harfler eşittir veya değildir.
Harfler eşitse çaprazdaki değer kopyalanıyor. (bu işlem şu anda hesaplanan d[i][j] değerine d[i-1][j-1] değeri konularak 25. Satırda yapılıyor).
Harfler farklıysa, yukarıda da anlatıldığı üzere, hücrenin üstündeki (d[i][j-1]) solundaki (d[i-1][j]) veya sol çaprazındaki (d[i-1][j-1]) değerlerinden en küçüğüne 1 eklenerek hücreye yazılıyor (d[i][j]).
Sonuçta, matrisin sağ alt köşesindeki değer, iki kelime arasındaki mesafeyi belirliyor ve bu değeri 39. Satırda döndürüyoruz.
Algoritmanın karmaşıklığı, n ve m uzunluğunda iki kelime için, (n+1)x(m+1) işlem gerektirmesidir (+1 değerleri boş kelime ihtimalinden gelmektedir). Bu durumda algoritmanın hem yer hem de zaman karmaşıklığı O(nxm) olarak bulunur.
SORU-20: Zaman Çizelgeleme (TimeTabling) hakkında bilgi veriniz.
Klasik bir optimizasyon problemidir. Basitçe belirli bir süreye, en verimli şekilde belirli kurallara uyarak olayları yerleştirmeyi hedefler. Örneğin öğrencilerin haftalık programının yapılması, doktor / hemşirelerin nöbet çizelgeleri, televizyon kanallarının yayın akışları gibi.
Daha basit anlaşılacağı için öğrenci programlarını örnek vererek konuyu açıklamaya çalışayım. Bir öğrenci probleminde, sistemin uyması istenen kıstaslar(constraints) şunlar olabilir: Örneğin öğrencilerin haftada 7 adet ders almaları gerekiyor. Bu dersler 3′er saat olsun ve her ders 2+1 şeklinde bölünecek olsun. Yani 3 saat üst üste olmayacak 2 saat farklı bir günde 1 saat farklı bir günde olacak. Dersi veren hocalardan 1. Dersi veren hoca, pazartesileri gelemiyor olsun. 3. Dersi veren hoca aynı zamanda farklı bir sınıfın farklı bir dersini veriyor olsun. …
Yukarıda bu saydıklarımız klasik bir öğrenci programı hazırlanması sırasında yaşanan problemlerden sadece bazılarıdır. Bilgisayar mühendisliği, bu problem için çözüm geliştiren programların yazılması ile ilgilenir ve bu problem, tanımı itibariyle NP-Complete sınıfında kabul edilebilir. Yani problemin çözülmesi polinom olmayan bir zamanda üstsel olarak artan hesaplama gerektirir. Örneğin n adet kıstasın olduğu (constraint) bir sisteme yeni bir kıstas girildiğinde, sistemin daha önceki n kıstas ile bu yeni girilen kıstasın uyumunu kontrol etmesin ve çakıştırmamaya çalışması gerekir. Görüldüğü üzere her yeni kıstas kendinden öncekilerin çarpımı kadar, yani basit bir hesaplama ile n! kontrol gerektirir.
Zaman çizelgeleme problemlerinin çözümü için çeşitli seviyelerde yöntemler geliştirilmiştir. Bunlardan ilki, problemin modellenmesidir. Problem modellemesi için kullanılan yöntemlerden birisi de TTML (time tabling markup language) kullanımıdır.
Problem çözümü için kıstas programlama (constraint programming) yaklaşımları kullanılabilir.
Ayrıca problemin verimli bir çözümü olup olmadığı veya hangi yöntemin kullanılması gerektiği bilinmediği durumlarda, genetik programlama yaklaşımları olumlu sonuçlar verebilmektedir. Bu yüzden literatürde, zaman çizelgeleme problemlerinin genetik algoritmalar ile çözümüne de rastlanmaktadır.
SORU-21: Doğrusal Programlama Örnekleri vererek açıklayınız.
Doğrusal programlama daha önce de bahsedildiği üzere birden fazla doğrusal denklem için en verimli, en iyi noktayı bulmayı hedefler.
Öncelikle bir problemin nasıl doğrusal denklemlerle ifade edildiğini bir örnek üzerinden görelim. Daha sonra farklı örneklerle konuyu anlamaya çalışacağız.
Örneğimizde bir üretici, 2 farklı üründen üretebiliyor olsun.
A1 ürünü için R1 kaynağından 1 birim ve R2 kaynağından 3 birim gerekiyor ve
A2 ürünü için R1 kaynağından 1 birim ve R2 kaynağından 2 birim gerekiyor olsun.
Ayrıca A1 ürününün satış değeri, 6 lira ve A2 ürününün 5 lira olsun.
Örneğin 5 birim R1 ve 12 birim R2 kaynağı bulunan bir üretici için, hangi üründen ne kadar üretmesi en fazla kâr elde edilmesini sağlar?
Bu soruyu çözmek için öncelikle problemi matematiksel olarak modellememiz gerekir.
Aşağıdaki gibi bir tablo yaparsak soruyu daha net görebiliriz:
|
R1 |
R2 |
|
|
A1 |
1 |
3 |
|
A2 |
1 |
2 |
|
Toplam |
5 |
12 |
Yukarıdaki tabloda hangi ürünün, hangi kaynaktan ne kadar gerektirdiği yazılmış ve son satırda, elimizde bulunan kaynak miktarı yazılmıştır.
Bu tabloyu hazırladıktan sonra, aşağıdaki denklemleri üretebiliriz.
X, A1 ürününden üretilen mal sayısı ve Y, A2 ürününden üretilen mal sayısı olmak üzere:
X + Y <= 5 olmalıdır. Çünkü iki üründe birer adet R1 gerektirmekte, dolayısıyla elimizdeki R1′den fazla üretemeyeceğimiz için en fazla 5 birim üretim yapabilmekteyiz.
Aynı şekilde 3X + 2Y <=12 olmalıdır.
Yazabileceğimiz diğer bir denklem ise X+Y>=0 olmasıdır. Bunun sebebi, üreticinin eksi miktarda mal üretememesidir.
Ayrıca kazancımızı, Z ile gösterecek olursak:
Z = 6X + 5Y şeklinde yazılabilir. Çünkü X ürünü 6 lira ve Y ürünü 5 liraya satılmaktadır.
Yukarıdaki denklemleri aşağıdaki şekilde listeleyebiliriz:
- X+Y<=5
- X+Y>=0
- 3X+2Y <= 12
- Z= 6X + 5Y
Görüldüğü üzere yukarıdaki denklemlerin tamamı y=ax + b şeklinde yazılabilecek doğrusal denklemlerdir. Konunun ismi olan doğrusal programlamanın da (linear programming) bir probleme uygulanabilmesi için bu yapıda bir problemle karşılaşılması gerekir.
Burada biraz terminolojiden bahsedecek olursak. Klasik bir OR (operations research, yöneylem) probleminde, ilk adım, problemin matematiksel olarak modellenmesidir. Bu modelleme sonucunda çıkarılacak iki önemli denklem grubu bulunur:
- Problemin kısıtları (Restriction)
- Problemin hedef fonksiyonu (objective function)
Yukarıdaki denklemlerden ilk 3 tanesi, problemin kısıtlarıdır. Yani X ve Y değerlerinin alabilecekleri limitleri belirlemektedir. Örneğin X>=0 veya Y>=0 olması gibi.
Son denklem ise hedef fonksiyonumuzdur. Bu fonksiyon bizim sonuçta azami değeri almasını istediğimiz hedefimizdir.
Yukarıdaki modellemeden görüldüğü üzere bir problemi doğrusal denklemlerle ifade edebiliyoruz. Şimdi farklı bir örnek üzerinden doğrusal programlamanın nasıl çalıştığını anlayalım.
Bir firmanın önümüzdeki 4 aylık satış beklentileri sırasıyla 1000, 800, 1200, 900 olarak beklenmektedir.
Firmanın normal üretim kapasitesi 800 birim iken aşırı üretim kapasitesi 200 birimdir. Normal üretimindeki birim maliyet 20 lira iken aşırı üretime girmesi durumunda, her aşırı üretim ürünü için 25 birim ödenmektedir.
Ayrıca her ürünün bir aylık depolama maliyeti 3 liradır.
Yukarıdaki problemi doğrusal olarak modellemeye çalışalım.
Öncelikle hedef fonksiyonumuzu (objective functions) yazarak başlıyalım:
X: normal üretim miktarı
Y: aşırı üretim miktarı
I: depolanan ürün miktarı olmak şartıyla
20X + 25Y + 3I değeri bize aylık olarak maliyetleri vermektedir. Dolayısıyla bizim beklentimiz, ön görülen üretimi karşılarken bir yandan bu maliyetleri asgari düzeye indirmektir. Dolayısıyla hedef fonksiyonumuz 20X+25Y+3I değerini minimize etmektir.
Bu problemdeki kısıtları yazacak olursak:
- Ay için
X+Y = 1000 olmalıdır (ilk ay depolanan ürün olmadığını kabul ediyoruz)
Ayrıca diğer aylar için olan stok miktarları aşağıdaki şekilde formüllenebilir:
I1 = (X1 + Y1) – 1000
I2 = (X2 + Y2 + I1) – 800
I3 = (X3 + Y3 + I2) – 1200
I4 = ( X4 + Y4 + I3) – 900
Yukarıdaki denklemleri hedef fonksiyonumuz ile birleştirerek bir maliyet fonksiyonunu aşağıdaki şekilde yazabiliriz:
Görüldüğü üzere denklemimizde ilk fonksiyonumuzda olduğu üzere 3 değişken ve bu değişkenlerin aylık toplamları bulunmaktadır. Amacımız bu değerleri minimize etmektir.
Ayrıca aşağıdaki koşullarda yazılabilir:
0 ≤ X ≤ 800
0 ≤ Y ≤ 200
Bu koşullar da her ay için üretilen normal ve aşırı üretim miktarlarını belirlemektedir.
Şimdi yukarıdaki denklemleri açarak doğrusal programlamada uğraşılan denklemlerin yapılarını tanımaya çalışalım. Yapacağımız işlem, her denklemde, bir önceki denklemde bulunan değeri yazmak. Örneğin
I2 = (X2 + Y2 + I1) – 800
Denklemindeki I1 değeri daha önceden belirlenmiş bir değerdir. Bu değeri yerine yazarak başlayabiliriz:
I2 = (X2 + Y2 + (X1 + Y1) – 1000 ) – 800
I3 = (X3 + Y3 + (X2 + Y2 + (X1 + Y1) – 1000 ) – 800 ) – 1200
I4 = ( X4 + Y4 + (X3 + Y3 + (X2 + Y2 + (X1 + Y1) – 1000 ) – 800 ) – 1200 ) – 900
Her adımdaki denklemde bulunan I değeri, bir önceki denklemde bulunan değer ile değiştirilmiştir.
Dolayısıyla aslında minimize etmek istediğimiz değeri aşağıdaki şekilde yazabiliriz:
SORU-22: Permutasyon Algoritması hakkında bir soru.
Soru: Hocam merhabalar bir soru sormak icin rahatsız etmiştim sizi,Bir fonksiyonumuz olsun ve parametre olarak zarSayisi alsin ve bu fonksiyon zar sayısına gore olası tum sonucları(zarların permutasyonunu ekrana bassın).ornegin zar sayısı 2 ise asagıdaki gibi sonuc elde etsin.
Cıktı : 11 12 13 14 15 16 21 22 23 24 25 26 31 32 33 34 35 36 41 42 43 44 45 46 51 52 53 54 55 56 61 62 63 64 65 66.
zarSayisi 3 ise aşagıdaki gibi
Cıktı: 111 112 113 114 115 116 121 122 123 124 125 126 211 212… seklinde
Hocam bu düz mantık ile kac zar sayısı varsa okadar ic ice dongu kurarak cozulebilir diye dusunmustum
mesela 3 zar var ise
for(i=1;i<=6;i++)
for(j=1;j<=6;j++)
for(k=1;k<=6;k++)
printf(“%d%d%d”,i,j,k);
Boyle dogru buluyor ancak 5,6,7 hatta 8 adet zar olursa butun durumlar icin ayrı dongu kurmakta sacma olacak.Isin icinden cıkamadıgım icin size sormak istedim hocam.Bunun bir matamatiksel denklemi varmı acaba veya optimum bir sonucu varmıdır ? Umarım anlatabilmişimdir derdimi hocam yardımınızı bekliyorum
Bu ti sorunlar, program yazarken sıklıkla karşımıza çıkabilir. Bu soru klasik bir permütasyon sorusudur, soruda bütün olasılıkların sırayla basılması istenir ( randomly permutate ).
Öncelikle bilgisayar bilimlerinde her şeyin bir hafıza X zaman tercihi (time X memory trade off) olduğunu bilmek gerekir. Yani ya zaman kazanır ya da hafıza kazanırsınız. İkisini birden kazanıyorsanız diğer herşeyi boşverebilirsiniz.
Yukarıdaki problemde görüldüğü üzere aslında 6′nın n’inci kuvveti kadar (6 n ) işlem gerekmektedir. Bu anlamda bahsettiğiniz problemler, karmaşıklık sınıfı olarak NP sınıfıdır. Yani polinom zamanda çözülemeyen ancak çözümü polinom zamanda kontrol edilebilen (çözümünün doğruluğu) problem sınıfıdır. Malum n üzeri n bir polinom değildir.
Gelelim bu tip soruları nasıl çözebileceğimize. Öncelikle sizin probleminiz, performans kaygısından daha çok, zar sayısına göre bir algoritma geliştirmede çıkıyor. Yani, permütasyon problemleri için 6n performansına razı olsak bile , n tane zar için yukarıdaki problemin çözümünu algoritma olarak ifade etmemiz gerekiyor. Bunun için çeşitli yöntemler bulunmasına karşılık ben sizin probleminiz için sayılabilirlik teoreminin (countability theorem) iyi anlaşılması gerektiğini düşünüyorum.
Acaba yukarıdaki bu problemi bir sayma problemi haline indirgeye bilir miyiz? Cevabımız evet. Aslında bütün permütasyon problemlerini bu şekilde görmek mümkündür.
Bakın klasik sayı sistemi 10 farklı rakamdan oluşur (0,1,2,3,4,5,6,7,8,9) ve ben size 1′den 1000e kadar sayıları alt alta yazın desem, aslında sizin yaptığınız 3 haneli olarak bu 10 farklı rakamın permütasyonunu almaktır. Diğer bir deyişle bu sayılar arka arkaya arttırılırken aynı zamanda bütün permütasyonlarını belirli bir sırayla dolaşmış olursunuz.
Bizim yukarıdaki problemimizde de bir permütasyon alınması istenmiş ve bu permütasyonun normal sayılara göre iki farklı özel durumu belirtilmiş.
- Birinci farkımız sayılar 1-6 arasında.
- İkinci farkımız ise sayıların sayısı limitli. Yani zar sayısı ile ekrana basılacak sayıların sayısı arasında bir bağlantı bulunmalı.
Bu bilgiler ışığında iki şeyi görmemiz gerekiyor. Şayet ben size 6 sayı tabanında 0′dan başlayarak sayın dersem aşağıdaki şekilde bir sayma olacaktır:
0,1,2,3,4,5,10,11,12,13,14,15,20,21,22,…
Gördüğünüz üzere klasik olarak onluk sayı tabanındaki permütasyon işlemini 5lik sayı tabanına uygulamış oluyoruz. Yukarıdaki sayıların her hanesini 1 arttırırsak problem çözülmüş olur:
11,12,13,14,15,16,21,22,23,24,25,26,31,…
Elbette burada kaç zar olduğu problemi devam etmektedir. Örneğin 3 zar için başlangıç değerini 111 olarak kabul edip, 5 tabanındaki sayma sayılarımızı 111 arttırmalıyız. Yukarıdaki sayma sayılaırının ilk halini arttırdığımızda:
111,112,113,114,115,116,121,122,123,124,125,126,131,..
Şeklinde permütasyonu elde ederiz.
Gelelim bu sayıları arttırmaya ne kadar devam edeceğimize. Bu sorunun çözümü de oldukça basit. Olasılık teorisinden hatırlanacağı üzere n tane zar için 6 üzer n ihtimal bulunur.
Yukarıda bu söylediklerimizi C dilinde ifade edecek ve koda dökecek olursak:
Şekil x
Yukarıdaki kod, problemimizi çözmektedir. Bu kodda, zar sayısını n değişkeninde tutuyrouz. 29-32. satırlar arasında, yukarıda anlattığım zar sayısı kadar 1′den oluşan bir katar ekleniyor. Yani klasik sayma sayılarımız 0′dan başladığına göre örneğin zar sayısı = 5 için 11111 + 0 , 11111+ 1, … şeklinde her zar ihtimaline bir başlangıç değeri ekleniyor.
Kodun 36-39. satırları arasında sorunun çözümü yapılmakta ve görüldüğü üzere 6 üzeri n kadar dönmektedir.
Kodun 28. Satırında görüldüğü üzere basma işleminden hemen önce sayı, 6′lık tabana çevrilmektedir.
Kodun çıktısı aşağıdaki şekildedir:
Şekil x
Kodumuzu analiz ettiğimizde bu haliyle kodun herhangi bir performans artışı sağlamadığını söyleyebiliriz.
Sırasız zar ihtimallerini basan algoritma
Yukarıdaki yazının yayınlanmasından sonra Sayın Cem Çankaya tarafından aşağıda bulunan ve acaba bu zarların sırasız olması halini nasıl basarız sorunu cevaplamaya çalışalım.
Problemi anlamak adına örneğin 2 adet zar atıldığında, 12 ile 21 ihtimalleri aynı oluyor. Yani zarların sırasının bir önemi kalmıyor.
Bu durumda yukarıda yayınladığımız algoritma işe yaramaz. Çözüm olarak problemimizi, yukarıda anlattığımız üzere sayma problemi haline getirmeye çalışalım. Sanırım problemi şu şekilde modellersek hata yapmamış oluruz.
n= 1 için
1,2,3,4,5,6
n=2 için
11,12,13,14,15,16,22,23,24,25,26,33,…
n=3 için
111,112,113,114,115,116,122,123,124,125,126,133,..222,223,224,..
Yukarıda vurgulamak istenen, her durumda birler hanesi 1-6 arasında ilk başta arttırılıyor. Ardından onlar hanesi 2 olduğunda birler hanesi geri 1den başlayıp artmıyor bunun yerine 2den devam ediyor. Yani örneğin 21 şeklinde bir değer hiç olmuyor çünkü bu zaten 12 olarak yazılmış.
Burası önemli çünkü şayet iki zar arasındaki bağlantıyı anlayabilirsek diğerlerini kolayca çözüyoruz.
Dizilimimizde, sağda olan bir zarın değeri hiç bir zaman solundakinden küçük olamaz dersek problem sayma problemi olarak (counting problem) çözülmüş oluyor.
Yani 3 zar için ilk iki zarın değerleri 13 oldutan sonra hiçbir şekilde 131 veya 132 olamaz. Problemin doğru çözülebilmesi için artık birler hanesi (en sağdaki hane) 3′ten başlamalıdır çünkü kendisinden önce bir adet 3 bulunmuş.
Aslında burada kendinden önce 3 bulunmuş ifadesi ile anlatılmak istenen kendinen önce 3ten küçük sayıların olduğu ihtimaller kapsanmış demektir.
Problemi bu şekilde modelledikten sonra performans artışı için bir de olaya dinamik programlama ekleyelim (dynamic programming).
Problem normalde zar sayısı n olmak üzere, 6 üzeri n kadar (6n) dönüp bu döngüler sırasında uygun olmayan zarları elemeli. Oysaki bu döngülerde istenmeyen zarlar için de döngümüz gereksiz yere dönecek. Acaba bunu engelleyip sadece istediğimiz zarlar kadar döngüyü döndürmenin bir yolu var mıdır?
Bu soruya cevap olarak yukarıda, ilk kodu yazarken yaptığım yorumu hatırlayalım. Bilgisayar bilimlerinde herşey zaman X hafıza tercihidir (time X memory trade off). Dolayısıyla dinamik programlama bize zaman kazandırmak adına, hafızada belirli durumları tutmayı ve bu durumların yeniden hesaplanmamasını sağlamayı hedefler.
Geçen örneğimizde olduğu gibi, bütün zar ihtimallerini tek bir int değişkende tutmak yerine bu sefer, dinamik programlama uyguluyor ve zarın her hanesini ayrı bir dizi değişkeninde tutuyoruz. Örneğin n adet zar için n adet elemanı olan bir dizi oluşturuyoruz.
Problemimiz bu sefer sayma işleminin bu diziye nasıl uygulanacağına dönüşüyor. Ben çözüm olarak aşağıdaki şekilde bir kod yazdım:
Şekil x
Yukarıdaki kodda, amacımız k isminde n elemanlı bir diziyi alıp son hanesini (yani n-1. haneyi) 1 arttırmaktır. Ancak son hane 1 artınca 6 yüzü olan zar için bu değer 2,3,4 gibi sayılar olduğunda problem olmazken 6′dan 7′ye çıkınca problem olmaktadır. Sayma problemimiz icabı, buradaki sayı 7 olunca bir soldaki hanenin arttırılması gerekir.
Örneğin dizinin içindeki değerler 11666 olsun. son hane 1 artınca 11667 olacaktır. Oysaki böyle bir zar ihtimali olamaz. Çözüm olarak bir soldaki hane artacak ve bu haneden 6 çıkarılacaktır. 11671 tekrar düzeltilecek 11711 tekrar düzelecek 12111 olacaktır.
Problemin buraya kadar olan kısmını 7-13. satırlar arasındaki döngü çözmektedir.
Bu çözüm yine yeterli değildir çünkü daha önce anlattığım üzere sağdaki bir değer, solundaki bir değerden küçük olamaz. Yani yukarıdaki gibi 12111 şeklinde bir değer bulunduysa, bu değerin 12222 olarak düzeltilmesi gerekir.
Problemin bu kısmının çözümü için de 14-16. satırlar arasındaki döngü çalışmakta ve sağdaki bir değerin solunda küçük olması halindde bu değeri eşitlemektedir.
Gelelim bir diğer probleme. Bizim kodumuz n adet zar için kaç tane değer hesaplayacak?
Bu sorunun cevabı belki olasılık teorisine göre formüllenip hesaplanabilir ancak bilgisayarların nümerik güncünü kullanarak bu problemi de kolayca çözebiliriz. Sayma teorimiz gereği, sayma işlemimiz, 6 yüzlü bir zar için 1′den başlayıp 6′da biter. Öyleyse dizimizin ilk hanesi (en soldaki hane) 6 olunca problem bitmiştir.
Bu kontrolde aşağıdaki main kodunda görüldüğü gibi kolayca yapılabilir:
Şekil x
Görüldüğü üzere kodun 35. satırında, dizinin ilk elemanının 6′dan küçük veya eşit olması istenmiştir.
Kodun örnek çalışmaları aşağıda verilmiştir:
Şekil x
Yukarıda, 5e kadar olan durumları test ettim ve bir problem göremedim. Umarım sorunuza cevap olmuştur.
SORU-23: EQP (Exact Quantum Polynomial) hakkında bilgi veriniz.
Bilgisayar bilimlerinde, kuantum hesaplama konusunda kullanılan bir karmaşıklık sınıfıdır. Literatürde tam kuantum polinom zaman (exact qunatum polynomial time) olarak geçmektedir.
Özellikle olasılıksal problemler için %100 başarı ile (yani bütün ihtimalleri eleyerek) sonuç üretme süresinin polinom zamanda olduğunu belirtir.
Bilindiği üzere karmaşıklık problem sınıfları tanımlanırken, klasik problemlerin (burada klasik kelimesi, kuantum hesaplaması öncesinde kullanılan Turing makinelerini ifade için kullanılmıştır) karmaşıklıkları P veya NP olarak ikiye ayrılabilir. P sınıfı, polinom zamanda çözüm üretilebilen problemleri gösterirken, NP sınıfı, doğruluğunun polinom zamanda ispatlanabildiği kümeyi ifade eder.
EQP sınıfı ise, kuantum bilgisayarları üzerinde çalışan programlar için (yani kuantum algoritmaları için), klasik algoritmalardaki P sınıfı gibi düşünülebilir. EQP sınıfının hedefi ise içinde ihtimaller bulunan problemleri çözmektir.
SORU-24: Çakışma Problemi (Collision Problem) hakkında bilgi veriniz.
Bilgisayar bilimlerinde, karmaşıklık teoremi (complexity theory) ve kuantum işleme (quantum computing) gibi konularda sıkça geçen bir problemdir.
Problem basitçe, bir fonksiyonun 1′e 1 veya n’e 1 olup olmadığını sorgular.
Örneğin f: {1 … n } à { 1 … n } bir fonksiyon olsun. Yani n sayıdan n sayıya tanımlı bir fonksiyon. Bu fonksiyonun örneğin 2′ye 1 olduğunu anlayabilmek için, en kötü ihtimalle, n/2 +1 elemanın sınanması gerekir. Şayet fonksiyon 2′ye bir fonksiyon değilse n/2+1 denemenin sonunda hep farklı sonuçlar çıkacaktır. Şayet fonksiyon 2′ye 1 fonksiyonsa ve şanslıysak, 2. denemede iki farklı sayı için aynı sonucu bulur ve fonksiyonun 1′e 1 olmadığını söyleyebiliriz, ancak şanssızsak n/2+1 deneme yapmamız gerekir.
Benzer problem, 3′e 1 olduğunun sınanması için n/3 +1 deneme gerektirir ve r’ye 1 için n/r +1 deneme gerekir.
SORU-25: Algoritma Analizi (Analysis of Algorithms) hakkında bilgi veriniz.
Bilgisayar bilimlerinde temel olarak iki önemli kaynak bulunur:
- Yer ( Hafıza )
- Zaman ( Hız )
Buna göre bir bilgisayar programı, ne kadar fazla yer kaplıyor ve ne kadar yavaş çalışıyorsa o kadar kötü demektir. Tersine, hızlı çalışan ve az yer kaplayan program ise daha iyi olarak düşünülebilir.
Elbette bu kıyaslama benzer amaçlı programlar için yapılmalıdır. Örneğin üniversite sınavına giren yaklaşık bir milyon öğrencinin alfabetik olarak sıralanması istensin. Bu sıralama işlemini yapan 10 farklı yöntem varsa, bu yöntemlerden hangisini tercih etmemiz gerektiğini bulurken hız ve hafıza kullanımlarına bakmamız gerekir. Örneğin bir milyon kişiyi iki ayda sıralayan bir algoritma ile iki dakikada sıralayan bir algoritmadan hızlı olanını tercih etmek çok daha makul olacaktır.
Elbette, zaman kısıtlaması dışında kısıtlar da bulunur. Örneğin 2 dakikada bir milyon kişiyi sıralayacak algoritma, şu anda teknolojik olarak mümkün olmayan bir hafızaya (RAM) ihtiyaç duyarsa, bu algoritmayı kullanamayız.
Öyleyse algoritmayı tercih ederken elimizdeki hafıza ve zaman kısıtlarına göre en verimli seçimi yapmak zorundayız.
Burada önemli bir soru, bir algoritmanın ne kadar zamanda çalışıp biteceğinin nasıl bulunacağıdır.
Kısaca, bir algoritmanın çalışma süresini bulmanın en kolay yolu, algoritmayı çalıştırmaktır. Örneğimize dönecek olursak, bir milyon kişiyi sıralayan 10 farklı yol varsa, bunların hepsini deneyip sonra seçim yapmak çok doğru bir yol olmaz. Zaten ilk denemeden sonra sıralanmış olacağından diğer denemeler anlamsız olur.
Öyleyse tek bir sıralama işlemi yapacağız ama elimizdeki yollardan en iyisini kullanarak. Tam bu noktada devreye algoritma analizi girer.
Algoritma analizi, bilgisayar mühendislerine, bir algoritmayı çalıştırmadan, ne kadar sürede çalışacağını, veya en azından diğer alternatiflerine göre nasıl davranacağını (daha hızlı, daha yavaş, yakın hızda, daha az yer kaplayacak şekilde vb.) söyler.
Bu açıdan, algoritma analizi, bilgisayar mühendisliğinin (bilimlerinin) en önemli matematiksel dayanağını oluşturur. Matematik bilimindeki karmaşıklık teoreminin (complexity theory) bir uygulama alanı olan algoritma analizi, matematiksel yöntemlerle, bir algoritmanın performansı hakkında bilgi verir.
Günümüzde, özellikle Türkiye’de, yazılım sektöründe çalışan bilgisayar mühendislerinin, algoritma analizi konusunda donanımlı olmaları gerekmektedir. Bunun sebebi, bir programı yazmanın onlarca yolu olmasına karşılık en iyi yolun kullanılmaması durumunda, programın çalışırken ödenen büyük maliyetlerdir.
Örneğin, günümüzde pek çok firmada veri tabanı işlemleri yapılmaktadır. Bir veri tabanına bağlanılarak, verinin alınması için 100ms harcanması yerine 200ms harcanması aslında sadece 100 mili saniye gecikme olarak görülebilir. Ancak gün içinde milyonlarca kere çalışan bu kod, sonuçta daha fazla donanım ihtiyacı (ve dolayısıyla donanım maliyeti) ve takılan ekranlar, yavaş programlar gibi sonuçlar doğurabilir.
Algoritma Analizi ve Karmaşıklık Teoremi
Algoritma analizi çalışmalarının dayandığı karmaşıklık teoremi genel olarak bir bilgisayar programının veya algoritmasının asimptotik değerini bulmayı hedefler. Buradaki amaç, algoritmanın ulaşabileceği en kötü veya en iyi durumu matematiksel olarak modellemektir.
Bu sınıfların detayı için karmaşıklık sınıfları (complexity classes) isimli yazıyı okuyabilirsiniz.
Bu bölümün amacı bir örnek üzerinden, bir algoritmanın karmaşıklığının nasıl hesaplandığı hakkında fikir vermektir.
Örneğin ekrana çarpım tablosunu bastıran basit bir kod üzerinden ilerleyelim:
A: int n=10;
B: for (int i = 1;i<n;i++){
C: for(int j = 1;j<n;j++)
D: printf(“%d “,i*j);
E: printf(“n”);
F: }
Yukarıdaki kodda, iç içe iki döngü (nested loop) kullanılarak çarpım tablosu, n = 10 , yani ilk 10 sayı için ekrana bastırılmak istenmiştir. Kodun ekran çıktısı aşağıdaki şekildedir:
Şekil x
Bu kodu analiz ederken, amacımız en kötü durumda, kodun ne kadar zaman alacağını bulmak olsun. Bu analiz şekline, worst case analysis (en kötü durum analizi) big-oh veya growth rate isimleri de verilmektedir.
Kodun ilk satırı koşulsuz bir şekilde bir kere çalışacaktır ve “n” isimli değişkeni tanımlayıp, hafızada yer açacak ve değişkene atama yaparak satırı bitirecektir. Bu işlemin maliyetine a diyelim.
Ekrana basma işleminin olduğu D satırında çalışacaktır ancak bu satırın çalışması birden fazla kere tekrar edilecektir çünkü döngülerin içerisindedir.
D satırı iki döngü içerisindedir dolayısıyla iki döngünün değerinin çarpımı kadar çalışacaktır. Bu döngülerin dönüş miktarı 9′dur. O halde D satırı 81 kere çalışır.
E satırı ise sadece bir döngü içerisindedir ve dolayısıyla B döngüsünün miktarı kadar çalışır. Bu değer de 9′dur.
Hesabımızı tamamlarsak, yukarıdaki kodun çalışma maliyeti:
M= A + 9B + 81C + 81D + 9E
Olarak bulunacaktır.
Bu değerleri biraz daha geneller ve n cinsinden yazarsak (n=10 değil farklı bir sayı olabilir diye genelleme yapmak istiyoruz)
M= 1 + n + n2 + n2 + n
M = 2n2 + 2n + 1
Yukarıdaki bu yeni maliyet hesabında basit satırların çalışma değerini 1 kabul ettik ve sonuçta 2. Dereceden bir çokterimli (polinom) bulduk.
Karmaşıklık sınıflarından büyük-o (big-oh) sınıfına göre analiz yaptığımızı hatırlarsak (en kötü durum analizi yapıldığı için, asimptotik üst sınır (aysmptotic upper bound) alınır)
M = O(n2)
Olarak maliyeti bulunmuş olunur.
SORU-26: Bellman Ford Algoritması hakkında bilgi veriniz.
Bu algoritmanın amacı, bir şekil (graph) üzerindeki, bir kaynaktan (source) bir hedefe(target veya sink) giden en kısa yolu bulmaktır. Bu anlamda, literatürde en kısa yol bulma algoritması (shortest path algorithm) olarak sınıflandırılabilir. Algoritma ağırlıklı şekiller (weighted graph) üzerinde çalışır. Kabaca, bütün düğümler için bütün kenarları dolaşır. Dolayısıyla performansı düğüm sayı ile kenar sayısının çarpımı olarak düşünülebilir. O( V E )
Algoritma aşağıdaki şekildedir:
Bütün düğümlere sonsuz mesafesini ata
Her düğüm için
Her kenar için
Düğümün üzerindeki mesafeden daha küçük mesafe bulduysan
Düğüm mesafesini ve düğüme gelinen düğümü güncelle
Yukarıdaki kodda görüldüğü üzere, iç içe iki döngü, kenar ve düğüm sayısı kadar dönmektedir. Ayrıca, bir düğümdeki değerin güncellenmesi sırasında, hangi düğümden gelindiği bilgisi de tutulmaktadır. Bu durum eksi yüklü kenarlar için çözüm oluşturur. Örneğin Dijkstra algoritmasında olan ve negatif değerli kenarlardan kaynaklanan döngüye girilme ihtimali, Bellman-Ford algoritmasında bulunmaz.
Algoritma Dijkstra algoritmasında olduğu gibi en küçük değere sahip olan kenardan gitmek yerine bütün graf üzerindeki kenarları test eder. Bu sayede aç gözlü yaklaşımının (greedy approach) handikabına düşmez ve her düğüme sadece bir kere bakarak en kısa yolu bulmuş olur.
Öncelikle eksi değerde düğüm bulunmayan bir örnek üzerinden algoritmayı çalıştıralım.
Şekil x
Öncelikle düğümlere değer ataması yapılıyor. Bütün düğümlere ¥ sonsuz değeri atanıyor.
Ayrıca başlangıç düğümü olarak A, Hedef düğüm olarak da E düğümünü tanımlayalım. Bu düğümleri algoritmanın sonunda kontrol için kullanacağız.
A başlangıç olduğu için 0 değerine sahip.
Şekil x
Önce dolaşacağımız düğümler için bir sıra oluşturalım. Bu örnekte alfabetik olarak düğümleri dolaşacağız ancak bu durum tamamen rast geledir.
Düğüm sıramız: A,B,C,D,E,F olmuş oluyor.
Şimdi de kenarları sıralayalım:
AB 2
AC 1
BE 2
BF 5
CF 2
CD 1
DF 5
DE 7
Bellman-Ford algoritması işte bu kenarları teker teker dolaşması itibariyle dijkstradan ayrılır. Sırayla yukarıdaki kenarları (Edges) dolaşır ve graftaki değerleri günceller.
Algoritma öncelikli olarak düğümleri dolaşıyor ve her düğüm için kenarları dolaşacak.
A düğümü için kenarları dolaşıyoruz. Kenarlardan sadece AB ve AC kenarları, A düğümü ile ilgili. Aslında bütün kenarlar dolaşılıyor olmasına rağmen, sadece bu iki kenar, graftaki sonucu etkileyecek.
AB 2, min(A,B) = 0 à 0+ 2 = 2
Şekil x
AC 1, min(A,C) = 0 => 0+1 = 1
Şekil x
Ardından B düğümüne geçiyoruz. Ve bu düğüm için bütün kenarları tekrar dolaşmaya başlıyoruz.
Bu durumda etkisi görülecek kenarlar:
AB 2
AC 1
BE 2
BF 5
CF 2
CD 1
Kenarlarıdır çünkü bunlar dışındaki kenarların bağladıkları düğümler sonsuz değerindedir ve güncelleme olmaz. Diğer bir deyişle grafın yukarıdaki şeklinde, A,B,C düğümlerinde sonsuz dışında değerler bulunuyor .O halde sadece bu düğümlere komşu olan kenarları almak yeterlidir.
BE 2, min (B,E ) 2, => 2 + 2 = 4
Şekil x
BF 5, min(B,F) = 2 => 2+5 = 7
Şekil x
CF 2, min (C,F) = 1 => 1+2 = 3, bu değer 7′den küçük olduğu için güncelliyoruz:
Şekil x
CD -1, min(C,D) = 1=> 1+(-1) = 0
Şekil x
Yukarıdaki son halimizde henüz 2 düğüm için işlem yapıldı ( A ve B düğümleri).
Şekil x
Graf 2 düğüm için kararlı hale ulaşmıştır ve diğer düğümler için işlem devam etse bile graftaki değerler değişmez.
İkinci Örnek
Yukarıdaki örnekte, düğümlerin ve kenarların rast gele dizildiğinden bahsetmiştik. Acaba bu dizilim rast gele olarak yukarıdakinden farklı olsaydı Bellman-Ford yine başarılı çalışır mıydı?
Düğüm diziliminin sonuca bir etkisi olmamakla beraber, kenar dizilimi sonucu etkiler:
Yukarıdaki dizilimi tersine çevirelim:
|
DF 5 |
|
DE 7 |
|
CF 2 |
|
CD 1 |
|
BF 5 |
|
BE 2 |
|
AC 1 |
|
AB 2 |
Bu yeni dizilime göre algoritmamızı çalıştırıyoruz:
A düğümü için kenarları dolaşıyoruz. Kenarlardan sadece AB ve AC kenarları, A düğümü ile ilgili. Aslında bütün kenarlar dolaşılıyor olmasına rağmen, sadece bu iki kenar, graftaki sonucu etkileyecek.
AC 1, min(A,C) = 0 => 0+1 = 1
Şekil x
AB 2, min(A,B) = 0 à 0+ 2 = 2
Şekil x
Ardından B düğümüne geçiyoruz. Ve bu düğüm için bütün kenarları tekrar dolaşmaya başlıyoruz.
Bu durumda etkisi görülecek kenarlar:
|
CF 2 |
|
CD 1 |
|
BF 5 |
|
BE 2 |
|
AC 1 |
|
AB 2 |
Kenarlarıdır çünkü bunlar dışındaki kenarların bağladıkları düğümler sonsuz değerindedir ve güncelleme olmaz. Diğer bir deyişle grafın yukarıdaki şeklinde, A,B,C düğümlerinde sonsuz dışında değerler bulunuyor .O halde sadece bu düğümlere komşu olan kenarları almak yeterlidir.
CF 2 için:
Şekil x
CD 1 için
Şekil x
BF 5 için , zaten daha kısa olan 3 değeri bulunmuştur
Şekil x
BE 2 için
Şekil x
AC ve AB kenarları için ise değişiklik olmaz.
Yukarıdaki bu yeni çalışmada dikkat edilirse, bir önceki çalışmadan farklı olarak F düğümü hiç 7 olmadan 3 değerini bulmuştur. Görüldüğü üzere kenar sıralaması, sonucu etkilememekle birlikte çalışma hızını etkileyebilir.
Eksi değerli Şekiller
Gelelim eksi değer olması durumuna.
Şekil x
Yukarıdaki şekilde görüldüğü üzere, BE ve CD kenarları eksi değerlidir. Bu şeklin çalışmasını inceleyelim:
Şekil x
Her şey, normal algoritmada olduğu gibi, başlangıç dışındaki düğümlere sonsuz atayarak başlıyor.
Bu algoritmanın eksi değerlerdeki başarısının anlaşılabilmesi için, şimdiye kadar ihtiyaç duymadığımız ve yazının başında belirtilen, hangi düğümden gelindiği bilgisine ihtiyaç vardır. Yani algoritmamızı çalıştırırken, her düğümdeki değerin güncellenmesi sırasında, hangi düğümden güncellendiği yazılır.
Ardından her düğüm için ve her kenar için işlem yapılıyor. Bu adımları daha önce detaylıca gösterdiğim için aşağıda düğüm bazlı hızlandırılmış olarak anlatıp, sadece eksi değerdeki farkı vurgulayacağım:
Şekil x
Bir sonraki adımda kenar sırasına göre aşağıdaki güncellemeler yapılır:
Şekil x
Bu haliyle, graftaki bütün düğümlere erişilmiştir. Fakat problem bitmez. Yani eksi değerli düğümler, çalışmanın hala devam etmesini gerektirir.
Şekil x
Yukarıdaki son hali, kararlı haldir.
Bu noktada şu soru sorulabilir: Peki neden tekrar eski değerli düğümler işlenerek, D ve E düğümleri güncellemiyor?
Bu soru haklı bir sorudur ancak yazının başında belirtildiği üzere, bellman-ford algoritması, her düğümde, kimden gelindiği bilgisini tutar. Dolayısıyla D düğümünü 0 olarak güncellemeden önce, D düğümüne, C düğümünden gelindiği ve E düğümüne de B düğümünden gelindiği kaydedilmiştir.
Dolayısıyla, eksi değerler tağılan bu döngülerde, tekrar edilip örneğin F düğümüne -4 değeri yazılamaz. Çünkü F düğümü zaten B düğümünden güncellenmiştir.
SORU-27: NL (Non-deterministic Logarithmic) hakkında bilgi veriniz.
Bilgisayar bilimlerinde de sıkça kullanılan ve matematiğin bir parçası olan karmaşıklı teorisi (complexity theory) içerisinde tanımlı olan bir karmaşıklık sınıfıdır (kümesidir, set)
Bu kümenin özelliği, bu kümenin üyesi olan, fonksiyon, denklem veya algoritmaların logaritmik zamanda veya logaritmik hafıza ihtiyacı ile çalışıp çalışamayacağının veya çözülüp çözülemeyeceğinin belirlenememesidir.
Bu kümenin tanım itibariyle tersi olan L (logarithmic) kümesinde bulunan problemler, algoritmalar veya fonksiyonlar ise logaritmik zamanda veya alanda çözülebilmektedir.
Bu tanımlarda geçen “zamanda veya alanda” ibaresi ile, karmaşıklığın zaman (time complexity) veya alan (memory complexity) olabileceği ifade edilmek istenmiştir. Yani, örneğin, bir algoritmanın zamansal çalışma karmaşıklığı (time complexity) NL sınıfından olurken, hafızadaki ihtiyacı (memory complexity) farklı bir sınıfta olabilir.
Logaritmik karmaşıklık konusunda bilinmesi gereken bir diğer konu ise RL sınıfıdır. Bu sınıf, tanım itibariyle olasılığa dayalı bir yapı izler. Randomized Logarithmic kelimelerinin baş harflerinden oluşan bu sınıftaki problemler, algoritmalar veya fonksiyonlar ise olasılıksal Turing Makinesi (Probabilistic Turing Machine) tarafından kabul edilirler.
Olasılıksal Turing makinesi, tanım itibariyle, sadece doğru durumları kabul eden ama yanlış durumları belirlerken hata yapabilen makinedir. Yani bir Klasik Turing makinesinde (Turing Machine) iki sonuçtan biri çıkar:
- Kabul (Accept)
- Red (Reject)
Normal bir Turing makinesi tasarım itibariyle bu iki durumu doğru olarak döndürebilmelidir. Olasılıksal Turing makinesi ilk şart olan kabul durumlarını doğru olarak döndürürken, ikinci ihtimal olan red durumlarında hata yapabilir.
RL sınıfı işte bu tip Turing makinelerinin logaritmik zamanda veya logaritmik alanda çözülebilmesi anlamındadır.
SORU-28: Bool Tatmin Problemi hakkında bilgi veriniz.
Litartürde, Boolean Satisfiability Problem olarak geçen ve boole cebirindeki denklemlerin doğruluğunun sağlanması olarak özetlenebilecek olan problemdir. Ayrıca çeşitli kaynaklarda bu probleme, problemin İngilizce tanımında geçen Satisfiability kelimesinin kısaltması olan SAT kelimesi olarak da rastlanabilir.
Bu problemi, bilgisayar bilimleri açısından ilginç kılan yanı ise, problemin yapısal olarak bir NP-Tam (NP-Complete) sınıfı problem olmasıdır.
Boole Cebiri, aslında mantıksal bir gösterim ifade etmektedir. Dolayısıyla mantıktaki karşılığı olarak probleme önermeler mantığı (propositional logic, kaziye mantığı) tatmin problemi ismi de verilebilir.
Boole cebirinden alışıldığı üzere mantıksal kurgu için tanımlı olan ve bilgisayar bilimlerinde değişken (variable) olarak isimlendirilebilecek belirticiler (literal, lafziler) bulunmaktadır. Bunlar basit birer sembol ile gösterilir.
Örneğin x ile gösterilen bir sembol çoğu zaman gerçek hayattaki bir kaziyeyi (önerme, proposition) ifade etmektedir.
Bu lafzi gösterimlerin (literal, belirticiler) üzerinde tanımlı ve yine boole cebiri içerisinde kabul edilebilecek işlemler (operators) tanımlanabilir. En sık kullanılan bool cebiri işlemleri ve(and), veya (or), tersi (not) şeklinde sayılabilir. Bu işlemler kullanılarak kurulan bir dizilimin sonucunun yine bir lafzi gösterim (literal) cinsinden elde edilmesi ve ya doğru (true) ya da yanlış (false) olması beklenir.
Bu tanımın üzerine örneğin sadece 3 adet lafzi gösterim (literal)’in ve (and) bağlacı normal şeklinde (conjunction normal form) olması durumuna 3SAT veya 3CNFSAT isimleri verilir. Bu problem NP-TAM (NP-Complete) bir problemdir.
Benzer tanım sadece 2 adet lafzi gösterim’in (literal) ve bağlacı normal şeklinde (conjunction normal form) olması durumuna ise 2SAT veya 2CNFSAT ismi verilir ve bu problem de NL-Tam (NL-Complete) bir problemdir.
Boole tatmin probleminin diğer önemli bir yanı da tarihsel olarak, 3SAT problemlerin, ilk NP-Complete problem olmasıdır. Bu konuda çalışan Stephen Cooke 1971 yılında bu problem grubunun NP-Complete olduğunu göstermiştir.
Ayrıca boole tatmin probleminin birinci derece mantık (First order predicate calculus) içerisindeki çözümünün aranması da ayrıca bir problemdir.
Bilindiği üzere birinci derece mantığın, üzerinde tanımlı olduğu lafzi gösterimler (literals) için bir takın etki alanı tanımlamaları (domain) mümkündür. Bu tanımlamalar ile verilen bir boole cebiri, diziliminin çözümlenmesi bir seviye daha karmaşık hale gelmektedir.
Örneğin aşağıdaki problemin çözümünü bu anlamda değerlendirebiliriz:
∃a (Şadi (a) ∧ Mühendis(a)) → Öyle bazı a’lar vardır ki, bu a’nın ismi Şadi’dir ve bu a mühendistir.
Bu tip birinci derece mantık dizilimlerinin çözüm karmaşıklığı Co-NP problem olarak geçmektedir.
SORU-29: Ve/Veya bağlacı normal şekilleri (Conjunction / Disjunction Normal Form) hakkında bilgi veriniz.
Bu gösterim, bool cebirinde (boolean algebra) kullanılan ve kaziyeleri (önerme, proposition) ve (and) bağlacı ile bağlamanın özel bir şeklidir. Kısaca CNF (conjuction normal form) olarak ifade edilir.
Bu özel şeklin taşıdığı kuralları aşağıdaki şekilde sıralayabiliriz:
Ters (not) işlemi, sadece önermelerin lafzi gösterimlerin (literal) başında bulunacaktır. Bir kaziyenin (önerme, propositon) başında bulunmayacaktır.
Örneğin ¬A şeklindeki tek lafzi gösterim üzerinde olan ters işlemi kabul edilirken ¬(A V B) şekli kabul edilemez.
Ve (and) işlemi, iki operand almaktadır. Yani a ve b şeklindeki bir işlemde a ve b adında iki lafzi gösterim (literal) bulunmaktadır. Bu işlemin herhangi bir operandı, basit bir lafzi gösterim (literal) veya disjunction (veya operatörü (or opeartor) ile oluşturulmuş bir) kaziye (önerme, proposition) olmalıdır.
Örneğin aşağıdaki gösterimler bu kurala uymaktadır:
A Λ B
(A V B ) Λ (C V D)
A Λ (C V D V F)
Ancak aşağıdaki gösterimler bu kurala uymaz
A Λ (B Λ C)
A Λ (B V C Λ D)
(A Λ B) Λ C
Ancak yukarıdaki gösterimler CNF (conjuction normal form) yani ve bağlacı normal şekline çevrilebilir. Bu çevirimler aşağıda gösterilmiştir (yukarıdaki her satır için sırasıyla)
A Λ B Λ C
A Λ ( B V C ) Λ ( B V D)
A Λ B Λ C
Görüldüğü üzere yukarıdaki yeni hallerinde, verilen gösterimler ve bağlacı üzerinde basitleştirilmiştir.
Disjunction Normal Form (DNF, Veya bağlacı normal şekli) ise yukarıdaki ve bağlacı için söylenen her şeyin, veya bağlacı için geçerli olması durumudur.
Kısaca bu şekilde kabul edilemeyecek gösterimleri ve çevrilmiş şekillerini aşağıda verelim:
|
DNF şeklinde olmayanlar |
DNF karşılığı |
|
(A V B) Λ C |
(A Λ C) V (B Λ C) |
|
A Λ (B V C Λ D) |
(A Λ B) V (A Λ (C Λ D)) |
|
¬(A Λ B) |
¬A V ¬B |
Tablo x
İki gösterimin doğruluğunun tespiti. Yani verilen bir boole cebiri diziliminin gerçekten CNF veya DNF olduğunun sınanması, veya verilen bir denklemin sonucunun doğru (true) veya yanlış (false) olarak bulunabilmesi, bu denklemdeki lafzi terim (literal) sayısına bağlıdır. Örneğin 3CNF bir denklemin, yani sadece 3 terimin conjunction (ve bağlacı ile bağlanmasından) oluşan bir denklemin karmaşıklığı NP-Complete olurken, bu terim sayısı 2′ye indirildiğinde yani 2CNF bir denklem söz konusu olduğunda, karmaşıklık P zamanda (polinom zamanda) çözülebilmektedir.
SORU-30: Karmaşıklık Sınıfları (Complexity Classes) hakkında bilgi veriniz.
Bilgisayar bilimlerine matematikten miras kalan karmaşıklık teorisi (complexity theory) konularından birisidir. Aslında problem veya algoritmaların çözüme ulaşmadaki karmaşıklığını ölçmek için kullanılır.
Bu tanımın ardında, her problem veya algoritmanın bir fonksiyon gibi düşünülebilmesi, ve matematikteki fonksiyonların karmaşıklığını sınıflandırmak için kullanılan karmaşıklık sınıflarının (complexity classes, asimptotik notasyonlar) , algoritmalar için uygulanması yatmaktadır.
Bilgisayar bilimlerinde kullanılan karmaşıklık sınıfları 5 tanedir.
- Küçük-o (small-o)
- Büyük-O (big-o, veya big-oh diye de geçer)
- Teta (Theta Θ, sadece büyük tetadan bahsedebiliz)
- Büyük omega (big-Ω )
- Küçük omega(small-ω )
Bu beş sınıf, aynı fonksiyon için uygulanan ve bir fonksiyonun farklı özelliklerini anlatan sınıftır.
Bu sınıflar, bir algoritma hakkında bilgi edinmemizi sağlar. Bu bilgileri aşağıdaki şekilde sıralayabiliriz:
- Bir algoritmanın ölçeği (Scalability)
- Bir algoritmanın zaman ve hafıza ihtiyacı (hızı ve ne kadar yer kaplayacağı) (time and memory efficiency)
Bu sonuçlara, algoritmanın kullanıldığı giriş bilgisine göre ulaşılır. Bir algoritmanın işlediği veri miktarına göre ne kadar zaman ve ne kadar yer gerektiğini gösterir.
Örnek
Örneğin klasik bir arama algoritması olan doğrusal arama (linear search) algoritmasını ele alalım. Bu algoritma verilen bir dizide karışık olarak duran sayılardan bir tanesini bulmayı hedefler.
int a[10] = { 3, 7, 2, 5, 6, 1, 8, -12, 11, 45};
Yukarıdaki dizide bulunan bir sayının kaçıncı sırada olduğunu aramak isteyelim. Örneğin 8 kaçıncı sıradaki sayıdır?
Algoritma, ilk elemandan başlayarak son elemana kadar bütün sayılara bakar ve aranan sayıya rastlarsa sonlanır. Bu durumda aranan 8 sayısını bulmak için sırasıyla 3,7,2,5,6 ve 1 sayılarına bakılacak, nihayet 8 sayısı bulununca 7. Sırada bulunduğu sonucu ile algoritma bitecektir.
Algoritmanın yer ihtiyacı ne kadardır?
Bu algoritmanın üzerinde çalıştığı veri büyüklüğü dizinin büyüklüğüdür ve bu değer 10′dur. Dolayısıyla arama işleminin n sayı içerisinden yapılacağını düşünürsek, n sayıyı hafızada tutmamız gerektiğini görürüz. Demek ki algoritmamızın hafıza karmaşıklığı (memory complexity) n’dir.
Algoritmanın çalışma hızı nedir?
Arama işleminin n adet sayı üzerinde yapıldığını söylersek, aşağıdaki 3 durumu incelememiz gerekir:
- En kötü ihtimalle kaç adımda bulunur? (worst case)
- En iyi ihtimalle kaç adımda bulunur? (best case)
- Ortalama kaç adımda bulunur? (average case)
Bu ihtimal analizini doğrusal arama algoritmamıza uyguladığımızda, karşılaşabileceğimiz en kötü durum aranan sayının dizinin en sonunda bulunması veya dizide hiç bulunmamasıdır. Bu durumda dizideki bütün elemanlara bakılması gerekecektir. Dolayısıyla en kötü durumda karmaşıklığımız n sayı için n olacaktır.
En iyi ihtimal ise, ilk bakılan sayının, aranan sayı olmasıdır. Bu durumda tek bir bakma işlemi yeterlidir. Bu durumdaki karmaşıklığımız ise 1 olacaktır.
Ortalama durum ise bu algoritmanın çok sefer çalışması sonucunda istatistiksel olarak ortalama kaç elemana bakılacağıdır. Bu dizide bulunan sayıların hepsinin aranma oranlarının eşit olduğunu kabul edersek, ortalama durum n/2 olur.
İşte yukarıdaki bu durum analizleri, bizim karmaşıklık sınıflarımızı veren analizlerdir.
O – En kötü durum analizi
Θ – Ortalama durum analizi
Ω- En iyi durum analizi
Aynı zamanda, bir fonksiyon için de o gösterimi üst sınırı (upper bound), gösterimi ise alt sınırı (lower bound) ifade eder.
Örnek olarak aşağıdaki fonksiyonu ele alacak olursak.
f(x) = 3x5
Bu fonksiyona bir üst sınır çizmek istenirse, örneğin 7x5 fonksiyonu çizilebilir.
Şekil x
Mavi fonksiyon 7x5 ve kırmızı fonksiyon ise f(x) fonksiyonudur.
Yukarıdaki şekilde görüldüğü üzere, x=0 noktasından büyük değerler için yani x>0 için, mavi renkle gösterilen fonksiyon, kırmızı fonksiyonun üst sınırını oluşturmaktadır ve denilebilir ki, kırmızı fonksiyon hiçbir zaman mavi fonksiyonun üzerine geçemez.
Bu durumda mavi fonksiyon, kırmızının üst sınırı (upper bound) olmuş olur. Diğer bir deyişle, mavi fonksiyon, kırmızı fonksiyondan daha kötü bir fonksiyondur ve kırmızı fonksiyon en kötü ihtimalle mavi fonksiyon kadar kötü olabilir.
Bu durumu büyük-o ile gösterirsek:
f(x) ∈ O (x5)
şeklinde yazmamız yeterlidir. Bu gösterimin açılımı aşağıda verilmiştir:
f(x) ∈ O(g(x)) , öyle bir c değeri bulunursa ki, x0>0 için, x>x0 şartıyla, bütün f(x) ≤ c g(x) olsun.
Bu tanım, kısaca, şayet f(x) ∈ O(g(x)) bağlantısı verilirse, herhangi bir x0 noktasından başlanarak sonsuza kadar bütün f(x) ≤ c g(x) koşulunu sağlayan bir c değerinin bulunabileceğini anlatmaktadır.
Yukarıdaki tanımın tam tersi omega için geçerlidir.
f(x) ∈ Ω(g(x)) , öyle bir c değeri bulunursa ki, x0>0 için, x>x0 şartıyla, bütün f(x) ≥ c g(x) olsun.
Bu tanım, kısaca, şayet f(x) ∈ Ω(g(x)) bağlantısı verilirse, herhangi bir x0 noktasından başlanarak sonsuza kadar bütün f(x) ≥ c g(x) koşulunu sağlayan bir c değerinin bulunabileceğini anlatmaktadır.
Bu iki tanımda dikkat edilecek bir nokta, f(x) ile c*g(x) arasındaki bağlantının eşitliği de kapsamasıdır. Yani örneğin big-omega için, c*g(x) değeri f(x) fonksiyonundan küçük veya eşit olabilir.
Şayet bu tanımdaki eşitliği kaldırırsak, küçük-o ve küçük-ω tanımını elde ederiz. Yani küçük-o için, c*g(x) değerinin f(x) değerinden sürekli olarak büyük olması gerekir.
Theta gösterimi ise ortalama durum, yani eşitlik durumu içindir. Burada aranan şart f(x) ∈ Θ(g(x)) için c1*g(x)≥f(x)≥c2*g(x)’dir. Yani g(x) fonksiyonunun f(x) fonksiyonu ile aynı şekilde hareket ettiğini göstereniki sabit sayı, c1 ve c2 bulunabiliyorsa f(x) ∈ Θ(g(x)) denilebilir.
Bu durumda yukarıda listelediğimiz 5 sınıf aşağıdaki şekilde ilişkilendirebiliriz:
Şekil x
Yukarıdaki şekilde anlatılmak istenen, sınıflar arasında, bazı kesişim durumlarının olduğudur. Görüldüğü üzere, en kötü durum (big-o) ile en iyi durum (big-Ω) kesişmesi sadece eşitlik durumunda olur. Bu durum ise teta ile gösterilmektedir. Benzer şekilde küçük-o ve küçük omega hiçbir zaman kesişemez ve büyük-o ile küçük-o arasında ve büyük omega ile küçük omega arasındaki fark alanında eşitlik durumları bulunur.
SORU-31: Bellman Ford Algoritması hakkında bilgi veriniz.
Algoritma aslında Dijkstra algoritmasından daha kötü bir performansa sahiptir ancak graftaki ağırlıkların eksi olması durumunda Dijkstra’nın tersine başarılı çalışır.
Algoritma Dijkstra algoritmasında olduğu gibi en küçük değere sahip olan kenardan gitmek yerine bütün graf üzerindeki kenarları test eder. Bu sayede aç gözlü yaklaşımının (greedy approach) handikabına düşmez ve her düğüme sadece bir kere bakarak en kısa yolu bulmuş olur.
Şekil x
Algoritmanın çalışmasını yukarıdaki gibi eksi değerlere sahip bir şekil üzerinden anlamaya çalışalım.
Öncelikle düğümlere değer ataması yapılıyor. Başlangıç düğümüne 0, doğrudan erişilen düğümlere erişim değerleri ve erişilemeyen düğümlere ¥ sonsuz değeri atanıyor. Hedef düğüm olarak da E düğümünü tanımlayalım.
Yukarıdaki örnekte A düğümünden başlayacağımız düşünelim:
Şekil x
Şimdi şekilde kenarları sıralayalım:
AB 2
AC 1
BE -2
BF 5
CF 2
CD -1
DF 5
DE 7
Bellman ford algoritması işte bu kenarları teker teker dolaşması itibariyle dijkstradan ayrılır. Sırayla yukarıdaki kenarları (Edges) dolaşır ve graftaki değerleri günceller.
Yukarıda, rastgele sıra ile dizilen bu kenarları sırasıyla dolaşalım ve bu dolaşma sırasında şu işlemi yapalım.
Örneğin AB kenarı , A ve B düğümleri arasında. Bu düğümlerden düşük değere sahip olan düğüm üzerine kenar değeri eklenip yüksek değere sahip olan düğüm üzerine yazılıyor.
Sırayla gidecek olursak:
AB 2, min(A,B) = 0 à 0+ 2 = 2
Şekil x
AC 1, min(A,C) = 0 => 0+1 = 1
Şekil x
BE 2, min (B,E ) 2, => 2 + 2 = 4
Şekil x
BF 5, min(B,F) = 2 => 2+5 = 7
Şekil x
CF 2, min (C,F) = 1 => 1+2 = 3, bu değer 7′den küçük olduğu için güncelliyoruz:
Şekil x
CD -1, min(C,D) = 1=> 1+(-1) = 0
Şekil x
DF 5, min (D,F) = 0 => 0+5 = 5 F’nin değerinden daha büyük o yüzden güncellemiyoruz.
DE 7, min(D,E) = 0 => 0+7 = 7, yine E’nin değerinden büyük olduğu için güncellenmiyor.
Sonuçta graf aşağıdaki şekilde bitmiştir:
Şekil x
Bu graftaki bütün düğümlerde, A düğümünden ne kadar maliyetle gidildiği bulunmuştur. Örnek hedef E düğümü ise, ulaşım maliyeti 4′dür.
Görüldüğü üzere eksi değere sahip düğümler olmasına rağmen herhangi bir problem yaşanmamıştır.
SORU-32: Edmonds Karp Algoritması hakkında bilgi veriniz.
Azami akış (maximum flow) problemini örneğin şehirler arasında bağlı boru hattına veya tedarik zincirine benzetebiliriz. Örneğin aşağıdaki şekli ele alalım:
Şekil x
Buradaki düğümler, şehirleri ve düğümler arasındaki kenarlar (edges) ise şehirler arasındaki boru hatlarının kapasitesini belirtsin. Amacımız A düğümünden E düğüme azami miktarda akış sağlayabilmek olsun.
Ford-Fulkerson çözüm için yukarıdaki şekilde öncelikle hedef düğüme giden yolu bulur. Algoritma bu arama işlemi sırasında şayet derin öncelikli arama (depth first search ,DFS) kullanıyorsa ford fulkerson olarak isimlendirilir. Şayet aynı algoritma bu arama işlemi sırasında sığ öncelikli arama (breadth first search, BFS) kullanırsa bu durumda da edmonds karp algoritması olarak isimlendirilir.
Biz yazımızın konusu olan Edmonds Karp algoritmasını anlamak için sığ öncelikli olarak arama işlemini gerçekleştirelim:
Şekil x
BFS (sığ öncelikli arama) sonucunda bulunan yollarımız aşağıda listelenmiş olsun:
- A-D-E
- A-B-E
- A-C-D-E
- A-C-F-B-E
- A-C-F-D-E
- A-B-F-D-E
İlk bulacağımız yol (path) A-D-E olsun bu durumda algoritma bu yol üzerindeki en küçük kapasiteyi bulmaya çalışır:
min(A-D,D-E) = min(5,7) = 5
bu bulunan sayı aslında bu yol üzerinden akabilecek maksimum değerdir. Dolayısıyla bu yoldan 5 kapasitesinde akış yapılmasına karar verilir ve şekil üzerinde bu durum işaretlenir:
Şekil x
Yukarıdaki şekilde buluna kırmızı sayılar, o yoldaki anlık akış miktarlarıdır.
Ardından derin öncelikli arama işlemi devam eder ve örneğin A-B-E yolunu bulur.
min(A-B, B-E) = min(4,2) = 2
Şekil x
Bir sonraki yolumuz olan A-C-D-E yolunu hesaplayalım.
min(A-C,C-D,D-E) = min(1,1,2) = 1
Yukarıda dikkat edilecek bir nokta, D-E aralığının şeklin ilk halinde olan 7 olarak alınması yerine 2 olarak alınmasıdır. Bunun sebebi şeklin bu kenarındaki 5 miktarındaki kapasitenin şu anda kullanılıyor olması ve artık geriye 2 miktarında kapasite kalmasıdır.
Şekil x
Bu adımsan sonra BFS algoritmamız ile bulduğumuz aşağıdaki yolları atlamamız gerekecektir:
- A-C-F-B-E
- A-C-F-D-E
Bunun sebebi bu yollarda bulunan A-C aralığının artık 0 kapasitesinin kalmış olması ve bu yollardan daha fazla akış elde edilememesidir.
Son olarak BFS algoritmamızın bulduğu A-B-F-D-E yolunu hesaplayalım:
min (A-B,B-F,F-D,D-E) = min ( 2,5,5,1) = 1
Şekil x
Yukarıdaki şekilde bütün düğümler arasındaki akışlar gösterilmiştir. Gerçekten de graftaki E düğümüne (hedef düğüme) gidebilen yollar maksimum kapasite ile doldurulmuştur.
SORU-33: Dijkstra Algoritması hakkında bilgi veriniz.
Bilgisayar bilimlerinde kullanılan ve algoritmayı literatüre kazandıran kişinin ismini taşıyan dijkstra algoritması, verilen bir şekilde (graph) en kısa yolu (shortest path) bulmak için kullanılır.
Bu algoritmanın çalışmasını örnek bir şekil( graph ) üzerinden göstermeye çalışalım. Bu gösterimin ardından Dijkstra algoritmasının başarısını ve zafiyetlerini tartışabiliriz.
Şekil x
Örnek olarak yukarıda bulunan şekli ele alalım. Dijkstra algoritması herhangi bir şekildeki bir düğümden diğer bütün düğümlere giden en kısa yolu hesaplar.
Örneğimizde başlangıç düğümü (node) olarak A düğümünü seçtiğimizi düşünelim ve algoritmanın çalışmasını bu düğümden başlayarak gösterelim.
Algoritma başlangıçta bütün düğümlere henüz erişim olmadığını kabul ederek sonsuz (¥ ) değeri atar. Yani başlangıç durumunda henüz hiçbir düğüme gidemiyoruz.
Şekil x
Ardından başlangıç düğümünün komşusu olan bütün düğümleri dolaşarak bu düğümlere ulaşım mesafesini günceller.
Şekil x
Bu güncelleme işleminden sonra güncellenen düğümlerin komşularını günceller ve bütün düğümler güncellenene ve şekil üzerinde yeni bir güncelleme olmayana kadar bu işlem devam eder.
Örneğin yukarıda A düğümünün komşusu olan düğümler (B ve C) güncellendiler. Bir sonraki adımda bu düğümlerin komşuları güncellenecektir. İstediğimiz bir düğüm ile başlayalım. Örneğin önce B sonra C düğümünün komşularını güncelleyelim:
Şekil x
Yukarıdaki şekilde, B düğümünün komşusu olan E ve F düğümleri güncellenmiştir. Bu güncelleme işlemi sırasında B düğümünün üzerindeki mevcut maliyet ile E ve F düğümlerine gidişten doğan maliyet toplanmıştır.
Örneğin E düğümü için 2 + 5 = 7 ve F düğümü için 2 + 2 = 4 değerleri bulunmuştur.
Şimdi bir sonraki düğüm olan C düğümünden güncelleme yapalım:
Şekil x
Bu aşamada güncellenen düğümler, C düğümünün komşusu olan D ve F düğümleridir. Bu düğümlerden, F düğümü daha önce güncellenmişti ve değer olarak 7 taşımaktaydı, ancak yeni gelen değer, F için C üzerinden 1 + 2 = 3 olarak hesaplanmış ve bu değer daha önceden hesaplanan 7 değerinden düşük olduğu için 7′nin üzerine yazılarak 3 olmuştur.
Algoritmanın çalışmasını sonraki düğümlerin güncellenmesi ile sürdürelim. Bu sefer örneğin D düğümünün komşularını güncelleyelim ( şu anda D, F ve E düğümleri aynı derecede yeni güncellenmiştir bunlardan herhangi birisi ile başlanması Dijkstra algoritmasına uygundur. Ancak recursive (özyineli) bir kodlama yapılıyorsa koda göre elbette yakın olan komşular güncellenecektir)
Şekil x
D düğümünün hiçbir komşusu güncellenmemiştir bunun sebebi hesaplanan değerlerin mevcut değerlerden yüksek olması ve dolayısıyla daha iyi bir sonuç bulunamamasıdır.
F için 2 + 5 = 7 > 3
E için 2 + 7 = 9 > 4
Dolayısıyla bir sonraki düğümü seçerek devam ediyoruz
Şekil x
F düğümünün komşularında da bir değişiklik olmuyor ve son olarak E düğümü deneniyor:
Şekil x
Son halimizde de bir değişiklik olmayarak şeklimiz (graph) kararlı bir halde ( daha fazla değişiklik olmadığı için) dolaşılmış ve bitmiş oluyor.
Şekil x
Yukarıdaki bu grafta, düğümler üzerinde yazılı olan değerler, A düğümünden başlanarak her düğüme gidilebilen en kısa yol mesafesini vermektedir. Örneğin şekildeki F düğümüne ulaşma maliyeti 3 olarak hesaplanmıştır ve Dijkstra algoritması, A düğümünden F düğümüne daha kısa bir yol bulunamayacağını iddia eder ( eşit farklı yollar bulunabilir ama en kısa yol yine de 3 olur)
Dijkstra algoritmasının zayıf yönü
Algoritma ne yazık ki eksi (-) değer taşıyan bir kenar bulunması halinde başarılı çalışmaz. Bunun sebebi eksi (-) değerdeki kenarın sürekli olarak mevcut durumdan daha iyi bir sonuç üretmesi ve algoritmanın hiçbir zaman için kararlı hale gelememesidir.
Örneğin aşağıdaki basit şekli ele alalım:
Şekil x
Yukarıdaki bu şekilde B-C kenarı (edge) -2 değerinde verilmiştir. Bu durumda A düğümünden başlayarak en kısa yolları hesaplamak istersek, öncelikli olarak A düğümünden komşusu olan düğümlere güncelleme yapılacaktır:
Şekil x
Ardından bu düğümlerin komşuları güncellenecektir. İki düğümde birbirine -2 değerini ekleyerek daha kısa bir yol bulacak ve birbirlerini güncelleyerek daha kısa sonuçlara ulaşacaktır.
Şekil x
Bu işlem ne yazık ki sonsuza kadar giden bir sürecin başlangıcıdır ve hiçbir zaman daha iyi bir sonuç bulma işlemi bitmez.
Şekil x
Sürekli olarak mevcut durumdan daha iyi bir durum bulunacak ve daha küçük değerler güncellenecektir. Bu yüzden Dijkstra algoritması, eksi (-) değer taşıyan şekillerde (graph) başarılı çalışmaz.
SORU-34: Cardinality (Sayısallık) hakkında bilgi veriniz.
Algoritma analizi (algorithm analysis) ve hesaplama teorisinde (theory of computation) sıkça kullanılan anlamıyla bir kümenin eleman sayısını belirtir.
Ayrıca matematik ve bilgisayar bilimleri açısından önemli bir yönü de kümelerin sonsuz olması durumunda kümeler arasındaki ilişkinin gösterilmesinde işe yaramasıdır.
Bu durumu öncelikle sonsuz iki küme tanımlayarak anlamaya çalışalım.
Tam sayılar kümesini ele alalım. Tam sayılar kümesi sonsuz eleman içermektedir. Eksi sonsuzdan 0′a kadar ve 0′dan da artı sonsuza kadar giden sayılardan oluşur.
Şekil x
Dolayısıyla tam sayılar kümesinde, sonsuz eleman olduğunu söylememiz doğrudur.
Benzer şekilde pozitif tamsayılar kümesi sıfırdan artı sonsuza kadar sonsuz sayıda eleman içerir:
Şekil x
Bu iki kümenin de sonsuz olduğunu söylediğimize göre gerçekten bu iki kümenin sonsuzlukları aynı mıdır? Yoksa kümelerden birisi daha büyük diğeri daha küçük sonsuz mudur?
Bu soru ile kardinallik (sayısallık, cardinality) konusuna başlayabiliriz. Bu soruyu cevaplamadan önce iki küme arasında eşit miktarda eleman bulunmasını tanımlamamız gerekir:
Sayısallık açısından iki küme birbirine bağlanmak istenirse, bunun anlamı bu kümeler arasında birebir (one-to-one) ve üzerine (onto) bağlantı bulunması demektir.
Şekil x
Yukarıdaki örnekte, iki küme arasında bire bir ve örten bir bağlantı gösterilmektedir. Burada birebir olması, birinci kümeden her elemanın ikinci kümeden tek bir elemanı gösteriyor olmasını ve ikinci kümedeki her elemanın da birinci kümede tek elemanı gösteriyor olması demektir. Şayet örneğin “a” elemanı hem 1 hem de 2 ile ilişkilendirilmiş olsaydı bu durumda birebir ilişkiden bahsedilemezdi. Örten olma özelliği (onto) ise boşta hiç eleman kalmamış olması yani bütün elemanların bir şekilde diğer kümede ilişkilendirilmiş olması anlamındadır.
Kümeler teorisi (set theory) ile ilgili bu kısa hatırlatmadan sonra konumuz olan sayısallık ilişkisine dönebiliriz.
Şayet kümelerin ikisi de yukarıdaki örnekte olduğu gibi sonlu değil de yazının başında verildiği gibi sonsuz ise bu durumda sayısallık bağlantısını nasıl kurabiliriz?
Sayısallık açısından, tam sayılar kümesi ve pozitif tam sayılar kümesinin eşit olduğunu söyleyebiliriz. Bunu göstermek için yukarıda bahsettiğimiz üzere, iki küme arasındaki ilişkinin örten ve birebir olduklarını göstermemiz gerekir.
Bunu daha iyi modelleyebilmek için sonsuzluk yönünü aynı tarafa çevirip ardından sayıları sıralayalım:
Şekil x
Tamsayılar kümesini yukarıdaki şekilde çevirecek olursak kümenin elemanlarını aşağıdaki şekilde yazabiliriz:
Şekil x
Yukarıdaki resimde solda gösterilen pozitif tam sayılar (Z+) kümesi ile sağda gösterilen tamsayılar kümesi (Z) arasında birebir ve örten bir ilişki kurulmuş olunur. Bunun sebebi iki kümedeki her eleman diğer kümedeki bir elemana mutlaka ilişkilendirilmiş ve bu ilişki birebir olmuştur.
Yukarıdaki bu görsel ilişkiyi matematiksel olarak aşağıdaki şekilde yazmak da mümkündür:
X e Z ve Y e Z+ olmak üzere X = Y / 2 * ( -1 ) Y mod 2
Yukarıdaki formülde tam sayı bölmesi yapıldığı kabul edilmiştir. (yani noktadan sonraki değerler göz ardı edilmiştir)
Gerçekten de Y = 1 için X = ½ *1 = 0 sonucu veya Y = 15 için X = 15 / 2 * -1 = -7 sonucu bulunmaktadır.
Bu bağlantının tersini yazmak da mümkündür:
Şekil x
Örneğin X = 0 için Y = -1 * 0 * 2 + 1 = 1 olarak bulunur veya diğer bir örnek olarak X = -7 için Y = -1 * -7 *2 + 1= 15 olarak bulunur.
Görüldüğü üzere iki yönlüde bütün elemanları diğer kümedeki elemanlara bağlayan bir bağlantı yazılması mümkündür.
Böylelikle yukarıdaki şartımızı sağlamış ve iki küme arasında eşit miktarda eleman bulunduğunu göstermiş oluyoruz.
Bu konuyu anlatırken sorulmuş bir soruyu cevaplamak istiyorum: “Kümelerin venn diyagramlarında da, denklemlerin her ikisinde de Z kümesi, Z+ kümesine göre daha yavaş ilerliyor. Yani Y için 16 sayısına ulaşıldığında X için henüz 8 sayısına yeni ulaşılmış olunuyor. Bu durumda tam sayılar kümesi, pozitif tam sayılar kümesinden daha büyük değil midir?”
Bu soru ilk başta mantıklı gibi görülse de cevabı kesin olarak hayırdır. Bunun sebebi kümelerin sonsuz oluşudur. Yani sonsuzun sonu yoktur J Bu durumu şöyle açıklayalım, yukarıdaki formülde yazılan X ve Y sayıları arasında neredeyse X=Y/2 veya Y = 2*X gibi bir bağlantı vardır. Bu durumda yukarıdaki soruya temel teşkil eden düşünceyi cevaplamak için sonsuzdaki bir sayıya n diyerek X=n için Y = 2n ‘dir sonucuna ulaşmak ve dolayısıyla n sonsuz ise 2n sonsuzdan daha büyük bir sonsuzdur gibi bir hataya düşmek mümkündür. Ancak buradaki hata iki türlü cevaplanabilir,
- Örnekteki n bir sayı olmak üzere, kümelerdeki son sayının n olduğunu söyleyemeyiz
- Sayı kümesindeki n, sonsuzda bir sayı olmak şartıyla 2n’in sonsuzda olmadığını söyleyemeyiz.
Dolayısıyla bu iki kümede de eşit sayıda eleman bulunur denilebilir.
Rasyonel sayılar (kesirli sayılar, rational numbers) ile pozitif tam sayılar kümesi arasındaki sayısallık ilişkisi
Yukarıdaki bu yaklaşım kullanılarak, kesirli sayılar ( rational numbers) kümesinin, tam sayılar kümesi ile aynı miktarda sonsuz eleman içerdiğini de ispatlayabiliriz. İhtiyacımız olan tek şey yukarıdaki örnekte bahsedildiği üzere pozitif tam sayılar kümesi, veya tam sayılar kümesi ile, kesirli sayılar (rasyonel sayılar) arasında bir bağlantı kurabilmek ve bu bağlantıyı modellemektir.
Öncelikle kesirli sayıları, iki boyutlu uzayda modellemekle işe başlayabiliriz. Kesirli sayılar kümesini aşağıdaki tabloda olduğu gibi yazmak mümkündür:
|
1 |
2 |
3 |
. |
n |
|
|
1 |
1/1 |
1/2 |
1/3 |
. |
1/n |
|
2 |
2/1 |
2/2 |
2/3 |
. |
|
|
3 |
3/1 |
3/2 |
3/3 |
. |
|
|
. |
. |
. |
. |
. |
|
|
n |
. |
. |
. |
. |
n/n |
Yukarıdaki tabloda görüldüğü üzere ilk satır ve ilk sütun birer pozitif tam sayı içermek üzere, tablonun diğer elemanları bu iki sayının (üsttekinin soldakine) bölümüdür. Dolayısıyla aslında problemi aşağıdaki şekilde pozitif tamsayılar olarak modellemiş oluruz:
Şekil x
Diğer bir deyişle şimdilik elimizde (Z+ x Z+) <-> Q+ şeklinde bir bağlantı bulunmaktadır. Bu bağlantıyı biraz daha ilerleterek Z <-> Q şeklinde çevirmeye çalışalım. Çünkü bu şekilde bir bağlantı bizim bu iki kümedeki eleman sayılarının eşit olduğunu kardinallik açısından ispatlamamız olacaktır.
Şekil x
Yukarıdaki şekilde gösterilen ok yönlerinde bu sayıları, pozitif tam sayılar kümesi ile ilişkilendirirsek hem bir bağlantı yakalamış hem de birebir ve örten bir ilişki elde etmiş oluruz. Bu durumda sayılarımızı aşağıdaki şekilde ilişkilendireceğiz:
|
Z+ |
Q+ |
|
1 |
1/1 |
|
2 |
2/1 |
|
3 |
1/2 |
|
4 |
3/1 |
|
5 |
1/3 |
|
6 |
4/1 |
|
7 |
3/2 |
|
… |
… |
Yukarıda görüldüğü üzere, tam sayılar kümesindeki bütün sayılar, rasyonel sayılar kümesi ile ilişkilendirilmiştir. Ayrıca dikkat edilmesi gereken bir husus, 2/2 veya 3/3 gibi tam sayı karşılığı 1 olan ve aslında eşit olan sayıların bu ilişkide yer almamasıdır. Bu sayıların ilişkiye yerleştirilmesi durumunda birebir olma özelliği bozulacaktır.
Yukarıdaki gösterim sayesinde, bu iki kümenin, yani rasyonel sayılar kümesi ile tam sayılar kümesi arasında bir ilişki birebir ve örten olarak kurulmuş ve bu sayede bu iki kümenin eleman sayısının eşit olduğu ve dolayısıyla bu iki kümenin sayısallık (kardinallik, cardinality) açısından eşit olduğu söylenebilir.
Sayılabilirlik (countability)
Diğer bir deyişle rasyonel sayılar kümesi sayılabilir bir kümedir (countable set) denilebilir. Aslında sayılabilirliğin (countability) tanımı, bir kümenin pozitif tam sayılar kümesi ile aynı kardinalliğe getirilebilmesidir. Yani herhangi bir kümeyi, yukarıdaki örneklere benzer şekilde pozitif tam sayılar ile birebir ve örten bir şekilde ilişkilendirebiliyorsak bu küme sayılabilir kümedir, aksi halde sayılamaz bir kümedir (uncountable set).
Bu sayılamazlık durumunu ve dolayısıyla kardinallik açısından eşit olmayan iki sonsuz kümeyi aşağıda örneklemeye çalışalım. Bu sefer kullanacağımız örnek gerçek sayılar (real numbers, reel sayılar) ile tam sayılar kümesi (integers) olsun.
Tam sayılar ve Reel sayılar arasında sayısallık ilişkisi
Bu örnekte, tam sayılar kümesi ve reel sayılar kümesi arasında sayısallık açısından eşitlik olmadığını ispatlamaya çalışalım.
Öncelikle ispatımızı, olmayana ergi (proof by contradiction) olarak modelliyoruz. Yani öncelikle reel sayılar kümesinin sayılabilir olduğunu kabul edip, bu sayı kümesi ile pozitif tam sayılar kümesi arasında bir ilişki kuracağız. Ardından bu ilişkinin kurulamayacağını ispatlamaya çalışacağız.
Öncelikle ilişki gösterimini ve ispatı daha iyi anlayabilmek için bu ispatta ikilik tabanda sayılar kullanalım. Yani 2 sayısı ikilik tabanda 10 olarak gösterilir. Dolayısıyla 0.2 sayısı 0.10 olarak gösterilir. Benzer şekilde 0.15438 sayısı 0.11110001001110 olarak gösterilecektir. Aslında bu gösterimin herhangi özel bir önemli olmamakla birlikte ispatı daha görülür kılmak için kullanacağız.
Şimdi doğru çalışan bir ilişkimiz olduğunu kabul edelim ve aşağıdaki şekilde gösterildiği gibi, tam sayılar ile reel sayılar kümesini ilişkilendirelim:
|
Z+ |
Q+ |
|
1 |
0.110101010100101… |
|
2 |
0.010101010110101… |
|
3 |
0.100100100101110… |
|
4 |
0.001010101010001… |
|
5 |
0.111110101010000… |
|
6 |
0.111000011111111… |
|
… |
… |
Yukarıdaki şekilde görüldüğü üzere, Z+ <-> Q+ ilişkisi kurulmuştur. Bu ilişkide herhangi bir matematiksel bağlantı bulunmadığı gibi sonuçta bütün ilişkilerde yukarıdakine benzer bir tablo ortaya çıkacaktır. Okuyucu birazdan yapacağımız ispatı, farklı sayılar veya herhangi bir matematiksel bağlantı için de deneyebilir sonuç değişmez.
Yukarıdaki şekilde tamsayılar ve reel sayılar arasında bir ilişki kurduktan sonra, kantor (Georg Cantor) tarafından ilk kez ispatlanan sayılamazlık durumu, köşegende (diagon) bulunan sayıların ters çevrilmesi ile elde edilir.
Yukarıdaki her sayının kaçıncı satırdaysa, o bitini alarak yeni bir sayı oluşturuyoruz.
|
Z+ |
Q+ |
|
1 |
0.110101010100101… |
|
2 |
0.010101010110101… |
|
3 |
0.100100100101110… |
|
4 |
0.001010101010001… |
|
5 |
0.111110101010000… |
|
6 |
0.111000011111111… |
|
… |
… |
Yukarıda, siyah renkle gösterilen bu sayılardan oluşan yeni sayımız: 0.110010… olarak belirlenebilir. Şimdi bu sayıdaki bütün bitlerin tersini alalım : 0.001101… olarak yeni bir sayı bulunacaktır.
Şimdi bu iki kümenin eşit olmadığını gösteren sorumuzu sorabiliriz, bulduğumuz bu yeni sayı, acaba yukarıdaki ilişkide hangi satırda yer almaktadır?
Cevap: Hiçbir satırda.
Bunun sebebi, her satırdan 1 bit alınarak oluşturulan yukarıdaki örnek sayının, tersi alındığında, her satırdan alınan bu bitin tersi alınmış ve dolayısıyla her satıra denk gelen bitin tersinden oluşan yeni bir sayı üretilmiş olmasıdır. Dolayısıyla hiçbir satırda ürettiğimiz bu yeni sayı bulunamaz.
Öyleyse, Z+ kümesi ile Q+ kümesini bağlamak için hangi bağlantı kullanılırsa kullanılsın, nasıl bir sayma yöntemi geliştirilirse geliştirilsin sonuçta yukarıdakine benzer bir durum olacak ve bu ilişkide bulunmayan bir reel sayı üretilerek iki küme arasındaki örten (onto) bağlantısı bozulacak ve reel sayılar kümesinde, tam sayılar kümesi ile ilişkilendirilmemiş bir eleman bulunacaktır.
Bu durumda reel sayılar kümesinin, tam sayılar kümesinden daha büyük olduğunu ve dolayısıyla sayılamaz olduğunu söyleyebiliriz.
SORU-35: Kırmızı-Siyah Ağaçları (Red Black Trees)
Bilgisayar bilimlerinde, veriyi ağaçta (tree) tutarken, ağacın dengeli (balanced) olmasını sağlayan bir algoritmadır. Algoritma, veriyi tutuş şekli sayesinde, arama, ekleme veya silme gibi temel işlemlerin en kötü durum analizi (worst case analysis) O(logn)’dir, yani algoritma n elman için bu işlemleri en kötü O(logn) zamanda yapmaktadır.
Kırmızı-siyah ağaçlar (red-black trees) tanım itibariyle ikili arama ağaçlarıdır (binary search tree) ve bu anlamda, herhangi bir düğümün solunda kendisinden küçük ve sağında ise büyük verilerin durması beklenir. Ağaçta ayrıca her düğüm için bir renk özelliği tutulur. Yani bir düğüm kırmızı veya siyah renk özelliği taşıyabilir. Ağaçtaki düğümlerin taşıması gereken bu özellikler aşağıdaki şekilde sıralanabilir:
- Ağaçtaki her düğüm kırmızı ya da siyahtır
- Kök düğüm (root node) her zaman için siyahtır.
- Bütün yaprak düğümler (leaf nodes) siyahtır
- Herhangi bir kırmızı düğümün bütün çocukları siyahtır.
- Herhangi bir düğümden, yaprak düğüme kadar gidilen bütün yollarda eşit sayıda siyah düğüm bulunur.
Yukarıdaki bu kurallar ışığında, herhangi bir düğümden, yapraklara kadar olan yolun, gidilebilecek en kısa yolun iki mislinden kısa olduğu garanti edilebilir. Diğer bir deyişle, ağacın aynı seviyedeki düğümleri aynı renktir. Ayrıca ağaçtaki renklendirme kökten başlayarak, siyah – kırmızı – siyah – kırmızı sıralamasıyla değişmektedir.
Örneğin aşağıdaki ağacı ele alalım:
Yukarıda görülen örnek ağaçta, kök düğüm siyah, yapraktaki boş düğümler (null) siyah ve bu düğümler dışındaki her düğümün çocukları, kendisinin ters rengindedir. Örneğin kırmızı olan 13 düğümünün çocukları siyah, siyah olan 25 düğümünün çocukları ise kırmızıdır. Bu anlamda kutu ile gösterilen yaprak düğümler göz ardı edilirse, aynı seviyedeki düğümler aynı renkte olmaktadır.
Arama işlemi (search)
Ağaç üzerinde bir değerin aranması, ikili arama ağacında (binary search tree) gibi yapılır. Yani aranan değer önce kök düğümde (root node) aranır, şayet aranan değer daha büyükse sağa, küçükse sola devam edilir. Nihayetinde aranan değer bulunana veya boş (null) değere ulaşılana kadar bu işlem devam eder.
Ekleme işlemi (insertion)
Ağaca bir düğümün nasıl eklendiğini algoritmasal olarak aşağıdaki şekilde açıklayabiliriz:
Ekleme işlemi normal bir ikili arama ağacına (binary search tree) ekler gibi başlar. Bu işlem sırasında yeni düğümün kırmızı olacağını kabul ederiz.
Ekleme işlemi sırasında uyulması gereken 3 temel kural vardır:
- Kök düğüm (root node) her zaman için siyahtır.
- Herhangi bir düğümden, yapraklara kadar uzanan herhangi bir yolda, eşit sayıda siyah düğüm bulunur.
- Bir kırmızı düğümün, kırmızı çocuğu bulunamaz.
Yukarıdaki istenmeyen durumlardan birisi oluştuğunda, ağaçtaki düğümlerin rengi değiştirilir ya da değiştirilemiyorsa ağaçta dengelemek için döndürme (rotation) işlemi yapılır.
Örneğin, yukarıdaki şekilde gösterildiği üzere 16 sayısını ağaca eklemek isteyelim.
16 değeri, 17′den küçük olduğu için, klasik bir ikili arama ağacında hareket eder gibi , ağacın sol tarafında devam edilecek ve 13 ile karşılaştırılacak.
13′ten büyük olduğu için sağa bakılıyor. Ve 15′ten de büyük olduğu için 15′in sağına ekleniyor.
Görüldüğü üzere yeni eklenen 16, 15′ten büyük ve sağındaki boş yere yerleştirilmiştir. Bu yerleştirme sonucunda iki ardışık kırmızı durumu oluşmadığı için sorun yoktur. Yani bir siyah düğüm altına kırmızı düğüm eklenmiştir. Şimdi örnek olarak sırasıyla 30 ve 32 sayılarını ekleyelim.
Ağaca yukarıdaki şekilde 30 değeri eklenince, ağacın en sağına yerleşmekte ve istenmeyen bir durum olan iki kırmızı arka arkaya gelmektedir. Çözüm olarak ağaçtaki düğümlerin rengi değiştirilmelidir.
Öncelikle, 30′un hemen üzerinde bulunan 27 düğümü, iki kırmızı düğüm arka arkaya olamayacağı için siyaha çevrilir. Siyaha çevrilen 27 numaralı düğümün büyük babası 23 numaralı düğümdür. Dolayısıyla 27 numaralı düğümün amcası 18 numaralı düğüm olur. 25 numaralı düğümün kırmızıya çevrilmesi, 23 numaralı büyük baba için problem oluşturur çünkü bir kırmızı düğümün çocukları siyah olmalıdır. Aynı zamanda problem 17 numaralı düğümden yaprağa kadar giden yolda da eşit miktarda siyah düğüm bulunmalıdır. Yukarıdaki örnekte 23 numaralı düğümün siyah kalması durumunda, 17 numaralı düğümden gidilebilen sağ yol ile sol yol arasında düğüm sayıları farklı olmaktadır.
Çözüm olarak 17 numaralı düğüm kırmızı yapılabilir ancak bu durumda da kök düğümün kırmızı olamayacağı kuralı ile ihtilafa düşülür. Dolayısıyla bu adımda çözüm 13 ve 23 numaralı düğümlerin siyah yapılmasıdır.
Ağacın çalışma şeklini daha iyi anlayabilmek için ağaca bir de 32 değerinde bir düğüm eklemeyi deneyelim:
Yukarıdaki örnekte görüldüğü üzere, 32 değeri, bir önceki adımda eklediğimi 30 değerinin sağına gelmektedir. Hemen dikkat edilebilecek bir problem, 30-32 ikilisinin arka arkaya kırmızı olmasıdır. Bu durumda 30 düğümünün siyah yağılması veya 32 düğümünün siyah yapılması problemi çözmez çünkü kökten yapraklara kadar giden yolda eşit sayıda siyah düğüm bulunmalıdır. Bu yola yeni bir siyah düğüm eklenmesi bu dengeyi bozar. Çözüm olarak saat yönünün tersine döndürme işlemi uygulanıp çocukların siyah yapılması gerekir. Bu durumu aşağıdaki şekil üzerinden açıklamaya çalışalım:
Yukarıdaki 2 problem bulunuyor:
- İki kırmızı düğüm arka arkaya
- Yolda tek siyah düğüm bulunmasına izin verilmiş, yapraklara kadar giden ikinci bir siyah düğüm çıkarma hakkımız yok.
Yukarıdaki yeni halinde, kural bozulmaksızın hem tek siyah ile yaprağa ulaşılıyor hem de iki kırmızı ihlalinden kurtulunuyor.
Benzer bir çözüm aşağıdaki şekilde olabilirdi:
Ancak yukarıdaki bu yeni halimiz ağacın bütünüyle uyuşmamaktadır. Yani döndürme işleminin yapıldığı ağacın üst düğümü 25 olduğu ve kırmızı olduğu için bu ikinci çözüm yeni bir kırmızı kırmızı komşuluğu doğuracaktır. Dolayısıyla ilk örnekte olduğu gibi problemi çözebiliriz:
Yukarıdaki yeni halimizde kuralların tamamına uyarak ağaca yeni sayıyı eklemiş bulunuyoruz. Benzer bir durumu oluşturup daha yukarıdan döndürme (rotation) işleminin nasıl yapıldığını, ağaca 29 sayısını ekleyerek görebiliriz. Bakın 29 sayısı eklenince de yapılan işlem aslında aynıdır sadece daha büyük bir döndürme işlemi gerçekleştirilmiş olacaktır:
İki kırmızı durumu oluşuyor. Bu noktada artık ikisinden birisinin siyah olması sorunu çözmez çünkü yaprağa kadar olan yolda her halükarda bir fazla siyah düğüm oluşur.
Çözüm olarak sırasıyla kırmızı-siyah dönüşümü yapıyoruz:
Görüldüğü üzere köke kadar giden yolda kırmızı-kırmızı durumunu engellemek için düğümler bir kırmızı bir de siyah sırasına dönüştürüldü. Şimdi problem 23 numaralı düğümün sağ ve sol taraflarındaki yol uzunluğunun farklı olması. Yani 23 numaralı düğümün sol tarafındaki problem çözülürse 17 numaralı düğüm için bir problem yok
Döndürme işlemi gerçekleştiriyoruz:
Yukarıda görüldüğü üzere 25 numaralı düğüm23 ve 30 numaralı düğümler arasındayken, bu düğümlerin atası durumuna geçmiştir.
SORU-36: Serseri sıralaması (Stooge Sort)
Bilgisayar bilimlerinde kullanılan ve tek boyutlu bir veri yapısı üzerinde (örneğin dizi (array) ) sıralama yapmaya yarayan bir algoritmadır. Algoritmanın çalışması birleştirme sıralaması (merge sort) veya hızlı sıralama (quick sort) algoritmalarına benzetilebilir. Bunun sebebi algoritmanın, sıralamak istenen sayıları 2/3 oranında iki parçaya bölmesi ve kalan sayıları kendi aralarında sıralamasıdır.
Algoritmanın açıklaması ve kodlanması
Bu anlamda algoritmanın çalışması aşağıdaki adımlarla izah edilebilir:
- Dizinin sonundaki sayı, başındaki sayıdan küçükse bu sayıların yerini değiştir (swap)
- Şayet işlem yapılan elaman sayısı 3 veya daha fazlaysa
- Elemanların ilk 2/3′ünü serseri sıralamasına ver
- Elamanların kalan 1/3′ünü serseri sıralamasına ver
- Elamanların ilk 2/3′ünü tekrar serseri sıralamasına ver
Yukarıdaki algoritmadan görüleceği üzere dizinin serseri sıralaması sırasında (stooge sort) 2/3. Elemandan itibaren iki parçaya bölündüğü düşünülebilir. Algoritmanın kodlaması aşağıdaki şekilde yapılabilir:
Yukarıdaki algoritmanın çalışması sonucu aşağıda verilmiştir:
Görüldüğü üzere sıralama algoritması kodun 15. Satırında, sıralama işlemi yapılan sayı aralığının 2/3′üncü elemanını bulmaktadır. Ardından kodun 16. Satırında ilk 2/3 ve kodun 17 satırında kalan 1/3 elemanlar sıralanmakta. En sonunda ise tekrar elemanların ilk 2/3′ü sıralanmaktadır.
Örnek çalışma
Yukarıdaki algoritmayı kullanarak örneğin aşağıdaki dizinin sıralanmasını adım adım göstermeye çalışalım.
Dizimiz şu şekilde verilsin:
{15,11,4,6,8,3}
İlk adımda, dizinin ilk ve son değerleri karşılaştırılacak ve son değer, ilk değerden küçük olduğu için yer değiştirilecek:
{3,11,4,6,8,15}
Ardından diziyi 2/3 nispetinde iki parçaya bölecek ve ayrı ayrı sıralamaya çalışacak. K değeri burada 6/3 = 2 , 5-2 = 3 olarak bulunur.
{{3,11,4,6} {8,15}}
İlk parçadaki ilke eleman olan 3, son eleman olan 6′dan küçük dolayısıyla sorun yok ve listeyi gene parçalıyoruz.K değeri bu sefer 3/3 = 1 , 3-1 =2 olarak bulunur:
{{{3,11,4},{6}} {8,15}}
Yeni parça için bir problem bulunmuyor çünkü 3<4 durumu bulunuyor. Parçalama işlemine devam ediyoruz : 3/3 = 1 , 2-1 = 1 olarak bulunuyor:
{{{{3,11},{4}},{6}} {8,15}}
Yukarıdaki kodda, 3 elemandan düşük sayıya ulaştığımız için, artık bir alt satırda bulunan ve geri kalan 1/3′lük elemanları sıralayan kısma geçebiliriz. Bu adımda sadece 4′ten oluşan sayılar sıralanacak ki bu değer de zaten 4′tür. (kodun 17. Satırı)
Ardından kodun 18. Satırına devam edip ilk 2/3 elemanı sıralıyoruz:
{{{{3,11},{4}},{6}} {8,15}}
Özyineli olarak çalışan kodumuz üç farklı sıralama işlemini bitirip, bu işlemleri çağıran ve özyineli yığınında (recursion stack) bir üst seviyede duran fonksiyonu çalıştırıyor. Bu durumda bir üst parça için geri kalan 1/3 sıralanıyor:
{{{{3,11},{4}},{6}} {8,15}}
Sonra kodun ilk 2/3′ü tekrar sıralanıyor:
{{{3,4,11},{6}} {8,15}}
Aynı işlem bir üst seviyeye çıkılarak devam ediyor:
{{{3,4,11},{6}} {8,15}}
{{3,4,6,11}, {8,15}}
{3,4,6,8,11,15}
Algoritma performansı.
Algoritma problemi iki parçaya böler. Birinci parça 1/3 ikinci parça ise 2/3 eleman içermektedir ve 2/3 eleman içeren kısım iki kere işlenir. Bu durumda performans için O(nlog(3) / log(1.5)) = O(n2.8) denilebilir.
SORU-37: İkili Arama Algoritması (Binary Search Algorithm)
Bilgisayar bilimlerinde bir bilgi kaynağı veya veri yapısı üzerinde problemi her adımda iki parçaya bölerek yapılan arama algoritmasının ismidir. Bu anlamda bazı kaynaklarda bölerek arama olarak da geçmektedir.
Arama algoritması, yapı olarak parçala fethet (divide and conquere) yaklaşımının bir uygulamasıdır.
Bu yazı kapsamında diziler üzerinde ikili arama işleminin nasıl yapıldığı anlatılacaktır. Ancak algoritma, diziler dışında çok farklı veri yapıları ve veri kaynakları için de kullanılabilir. Algoritmanın her durumda çalışması aşağıdaki şekildedir.
- Problemde aranacak uzayın tam orta noktasına bak
- Şayet aranan değer bulunduysa bit
- Şayet bakılan değer aranan değerden büyükse arama işlemini problem uzayının küçük elamanlarında devam ettir.
- Şayet bakılan değer arana değerden küçükse arama işlemini problem uzayının büyük elemanlarında devam ettir.
- Şayet bakılan aralık 1 veya daha küçükse aranan değer bulunamadı olarak bitir.
Yukarıdaki müsvedde kod (pseudo code) ele alındığında problem her seferinde aranan sayıya göre ortadan ikiye bölünmektedir. Bu anlamda problemin log2(n) adımda çözülmesi beklenir. Burada logaritmik fonksiyonların üssel fonksiyonların tersi olduğu ve her adımda problemi iki parçaya böldüğümüzü hatırlayınız.
Arama algoritmasının bir dizi üzerinde başarılı çalışması için dizinin sıralı olması gerekir. Yukarıdaki koddan anlaşılacağı üzere ortadaki elemana bakıldığında, aranan sayı ya bakılan sayıdan büyük ya da küçüktür. Dolayısıyla algoritmamız, ya bakılan sayıdan küçük olan sayılar arasında arama yapacak ya da büyük olan sayılar arasında arama yapacaktır.
Bu durumu örnek bir dizi (array) üzerinden açıklamaya çalışalım. Örneğin dizimiz aşağıdaki şekilde verilmiş olsun:
int a[10]={2,3,5,6,9,12,32,54,74,111};
Bu dizi üzerinde 10 eleman bulunduğuna göre aranan sayı her ne olursa olsun ilk bakılacak değer ortadaki değer olan (5. Değer) 12 sayısıdır.
Dizi üzerinde arama işlemi için örnek olarak 3 sayısını aradığımızı düşünelim. Ve aşağıdaki şekilde dizide arama işlemine devam edelim:
Ortadaki değere baktıktan sonra sayı küçük olduğu için altında kalan (solundaki) sayıların ortasındaki değere (yani 2. elemana) bakıyoruz.
Aradığımız sayı (yani 3) dizide baktığımız sayıdan küçük olduğu için arama işlemine yine baktığımız sayıdan küçük olan (solunda olan) sayılar ile devam ediyor ve ortadaki sayı olan dizinin 1. Elemanına bakıyoruz.
Aradığımız sayıyı bulmuş oluyoruz ve çalışmamız bitiyor.
Farklı bir örnek olarak aranan sayının 37 olduğunu (ve dizide olmayan bir sayı olduğunu) düşünelim. Çalışmamız, yine dizinin tam ortasındaki eleman ile başlayacaktır:
Aradığımız sayı baktığımız sayıdan büyük olduğu için dizinin bakılan değerden büyük elemanlarında aramaya devam ediyoruz. Bu alanın ortasındaki değer dizinin 7. Elemanı olur ve 54′tür.
Bu değer aradığımız 37 sayısından büyük olduğu için geri kalan sayılar arasından küçük sayılara bakıyoruz. Bu durumda geri kalan sayılarımız 12′den büyük ve 54′ten küçük sayılardır ve ortasındaki eleman 32′dir.
Sayımız 32 sayısından büyük (aradığımız sayı 37 idi) bu durumda bakılacak başka sayı kalmadığı için (hem 54 hem de 32 sayılarına baktık ve arada başka sayı bulunmuyor) çalışmamızı bitirip aranan sayının dizide olmadığını söyleyebiliriz.
Arama algoritmasını kodlarken yukarıda karşılaşacağımız bir problem, dizinin hangi elemanlarına bakıyor olduğumuz ve bu elemanların orta değerinin bulunmasıdır.
Bu problemin çözümü için dizide arama yapılan elemanlardan en küçük ve en büyük indisli sayıları tutmak genelde kolay bir çözüm sunar.
Aşağıdaki açıklamalar sırasında en küçük indis için kırmız, en büyük indis için yeşil ve orta değer için mavi renkler kullanılacaktır.
İlk örneğimiz olan 3 sayısını ararken indis değerlerimiz aşağıdaki şekildedir:
Dizide aranan sayının bulunabileceği en küçük indis 0 ve en büyük indis 10 olarak kabul edilirse
Dizide bulunan 5. Sayıya bakıyoruz (10 + 0) / 2 = 5. Ayrıca artık aradığımız sayının 5. Elemandan daha büyük olma ihtimali bulunmuyor. Dolayısıyla aranabilecek en büyük sayı 5. Elemandadır diyerek en büyük indisi dizinin 5. Elemanı olarak ayarlıyoruz.
Arama işlemine ortadaki sayı ile devam edeceğiz (5+0)/2 = 2 olduğu için 2. Elemana bakıyoruz.
Aradığımız sayı baktığımız sayıdan küçük olduğu için artık 5′ten büyük sayılara bakmanın bir anlamı yoktur diyerek en büyük indisi dizinin 2. Elemanı olarak atıyoruz. Baktığımız sayı aradığımız sayıya eşit olduğu için de sayıyı bulduk diyerek çalışmamızı bitiriyoruz.
İkinci örnek olan ve 37′yi aradığımız örnekteki indis değerlerine bakacak olursak. İlk olarak en büyük ve en küçük olarak dizinin ilk ve sonuncu elemanlarını alıyoruz.
Bakacağımız değer, arama işleminin yapıldığı uzaydaki orta değer olacağı için (10+0)/2 = 5 sonucuna göre dizinin 5. Elemanına bakıyoruz. Bu değer aradığımız değerden küçük olduğu için arama uzayının, bakılan değerden büyük olan kısmı ile devam ediyoruz. Ayrıca baktığımız bu değerden daha küçük sayıları artık aramanın anlamı olmadığı için en küçük indis olarak 5. İndisi atayabiliriz..
Artık aradığımız değerler 5 ile 10. Değerler arasında olduğuna göre bakacağımız değer (10+5)/2 = 7 olarak atanmalıdır. Aradığımız değer olan 37 , baktığımız değer olan 54′ten küçük olduğu için artık 54′ten büyük değerleri aranmayacak şekilde en büyük indisi 54 olarak atıyor ve orta değer olan (7+5) /2 = 6. İndisteki 32 değerine bakıyoruz.
Arama işlemi bu adımda bitiyor çünkü aranan sayı olan 32 ile en büyük değer olan 54 arasında başka bir sayı olamayacağına göre ve aradığımız değer bu arada olacağına göre işlem bitebilir.
Yukarıda verilen örnek üzerinde çalışabilecek C kodunu aşağıdaki şekilde yazabiliriz:
Yukarıdaki kodda, dikkat edilecek bir püf noktası 7. Ve 8. Satırlardaki -1 ve 10 değerleridir. Bunlar arama işleminin arasında yapılacağı indisleri belirten en büyük indis ve en küçük indis değerleridir. Bu değerler dikkat edildiği üzere dizinin tanımlı olduğu indislerin dışındadır. Yani yukarıda 10 elemanlı bir dizi söz konusudur dolayısıyla bu dizinin -1 ve 10 numaralı elemanları yoktur.
Yukarıdaki kodda bu şekilde bir değerden başlamanın sebebi, dizi üzerinde arama yapılırken köşede kalan elemanların aranmamasıdır. Okuyucu dilerse yukarıdaki kodda, eb = 9 yazarak 123 değerini veya ek=0 yazarak 2 değerini aratmayı deneyebilir.
Algoritmanın Performansı
İkili arama algoritması (binary search algorithm) arama yaptığı uzayı her adımda iki eşit parçaya bölerek devam ettiği için O(log2(n) ) performansına sahiptir. Elbette arama algoritması en iyi ihtimalle ilk baktığı değeri bulabilir, bu durumda 1 erişim sonuca erişmek için yeterli olacaktır.
Algoritmanın sıralı olmayan, karışık bir dizi üzerinde çalışması için öncelikle dizinin sıralanması gerekir. Bu işlem en iyi sıralama algoritmasına göre ( birleştirme sıralaması (merge sort) veya hızlı sıralama algoritması (quick sort) ile) O(nlog2(n)) adımda yapılmaktadır. Dolayısıyla dizinin önce sıralanması ardından da ikili arama algoritmasına göre aranması işlemi için
n log2(n) + log2(n)
= log2(n) (n+1)
İşlem yapılması gerekecektir. Öte yandan çok daha kötü bir performansa sahip olan doğrusal arama (linear search) algoritması aynı işlemi O(n) performansı ile yapabilmektedir. Dolayısıyla karışık bir dizi içerisinde arama işlemi yapılacaksa, doğrusal arama algoritması her zaman için en basit ve hızlı algoritmadır.
Bu durumun bir istinası, arama işleminin sürekli yapılması durumudur. Örneğin bir bankada, bankamatiğe müşterinin her kartını takışında kaydının arandığını düşünelim. 10 milyon müşterisi bulunan bir banka için, müşterinin her aranması en kötü ihtimalle 10 milyon arama gerektirecektir.
Öte yandan veriyi bir kere sıraladıktan sonra her arama işlemi için sadece log2(10.000.000) ~24 işlem yeterli olmaktadır. Bu aramanın gerçekleştirilebilmesi için gereken sıralama maliyeti ise :
n log2(n) = 10.000.000 log2( 10.000.000) = 24.000.000
işlem gerektirmektedir. Dolayısıyla bu bankada 24′ten fazla arama gerekiyorsa (ki 10 milyon müşterisi bulunan bir banka için bir gün içerisinde binlerce kere müşteri bilgisine erişilmesi gerektiğini kabul edebiliriz). 25. Aramadan sonraki aramalarda sıralama maliyeti çıkarılmış ve performans olarak avantajlı hale geçilmiş olunur.
C++ dili
Gelen bir istek üzerine yukarıdaki kodun aynısını C++ diline çevirerek aşağıda yayınlıyorum. Aşağıdaki kodun görüleceği üzere C dilinde yazılmış olan yukarıdaki koddan bir farkı yoktur. Sadece ekran çıktılarının printf yerine cout olarak değiştirilmesi yeterlidir.
C# dili
Aynı kodu C# (csharp) dilince çevirdiğimizde aşağıdaki kodu yazmak mümkündür (Visual Studio 2008 Express Edition ile test edilmiştir):
JAVA ile
Aynı kodun java ile kodlanmış hali aşağıdadır:
SORU-38: Güvercin Yuvası Kaidesi (Pigeonhole Principle)
Bilgisayar bilimleri de dahil olmak üzere pek çok matematik temelli bilim ve mühendislik alanında kullanılan oldukça basit bir umdedir. İsmini güvercin yuvalarından alan bu kaideye göre yuva sayısından fazla güvercin varsa, ve bütün güvercinler bir yuvaya girecekse, en az bir yuvaya birden fazla güvercin girmek zorundadır.
Bu durumu sembollerle göstermemiz gerekirse n tane yuva ve m tane güvercin için m > n durumunda en az bir yuvada birden fazla güvercin bulunmalıdır.
Bilgisayar bilimlerinde çok sayıda sonsuz küme ile uğraşırken oldukça kullanışlı olan bu kaide, örneğin özetleme fonksiyonlarında (hashing function) bir çakışma (collision) ihtimalinin hesaplanmasında ya da kayıpsız sıkıştırma algoritmasının (lossless compression algorithm) istatistiksel analizinde kullanılabilir.
Örnek: N adet pozitif tam sayı arasında en az iki sayının farkı N-1′e tam bölünebilir.
Yukarıdaki iddiayı (hipotez) güvercin yuvası prensibi ile ispatlayalım. Öncelikle N adet tam sayımız olduğunu biliyoruz. Bu sayılara a1, a2, …, aN diyelim ve herhangi bir ai sayısı için N-1bölümünden kalan değerini ri diyelim. Yani ri = ai mod(N-1) olsun.
Şimdi güvercin yuvası prensibini kullanıyoruz ve diyoruz ki elimizde en fazla N-1 tane farklı kalan değeri olabilir. Yani ri değerinden en fazla N-1 tane olabilir. Sebebi ise basit, mod(N-1) içinde en fazla N-1 farklı sayı olabilir. Peki elimizde kaç sayı var ? N sayı. Dolayısıyla en az iki sayının bölümünden kalan değer aynı olacaktır. Diğer bir değişle N farklı sayının N-1 farklı kalana bölünmesi durumunda en az iki sayı aynı kalan sahip olacaktır. Bu sayıların farkı da aynı kalana sahip olacağından iddiamızı ispatlamış oluruz.
Örnek: N pozitif tam sayı için, sayıların bir kısmının yada tamamının toplamı N ile kalansız bölünebilir.
Bu iddianın ispatında da sayıları matematiksel olarak modelleyelim. Öncelikle toplamları b ile ifade edersek, b1 = (a1) mod(N), b2 =(a1 + a2) mod(N), b3 = (a1+a2+a3) mod(N), …, bN = (a1 + … + aN) mod(N) olarak göstermek mümkündür. Şayet sayılardan birisi 0 ise, yani b değerlerinden birisinin 0 olduğunu bulabiliyorsak, bu durumda zaten ispat yapılmış olur. Çünkü iddiamızda N ile kalansız bölünebileceğinden bahsediyorduk.
Şayet sayılardan birisi 0 değilse bu durumda toplamları elde ettiğimiz sayıların seçiminde a1 ‘den başlayarak ai gibi bir sayıya kadar olanları almak yerine (ai+1 + … + aj) mod(N) = 0, olduğunu gösterebiliriz.
Gerçekten de mod N işlemi sonucunda N-1 farklı sayı çıkacağını biliyoruz. Elimizde N sayı bulunduğunu düşünürsek o zaman yukarıdaki gösterime göre en az iki tane b değeri aynı olmalıdır. Örneğin bu değerler bi = bj olsun ve i < j olduğunu kabul edelim. Bu durumda (ai+1 + … + aj) mod(N) = 0 olduğu gösterilmiş olur.
SORU-39: Arılar Algoritması (Bees Algorithm)
Bilgisayar bilimlerinde kullanılan arama algoritmalarından (search algorithms) birisinin ismidir. Bu algoritmada amaç belirli bir en iyi noktasını (optimum point) bulmaktır.
Bu arama işlemi sırasında arıların bal yapmak için kullandıkları arama metodu modellenmiştir. Algoritmanın farklı çeşitleri bulunmasına karşılık en basit haliyle bir komşu arama algoritmasına benzetilebilir.
Algoritmanın açıklaması
Algoritmanın çalışması sırasında kullanılan bazı değerleri tanımlayalım.
n: ortamda gezinen ve hedefi arayan arı sayısı
m: ortamda aranacak noktaların sayısı. (arı ve bal örneği düşünülürse çiçek sayısı olabilir)
e: ortamdaki m çiçekten bulunan en iyilerinin sayısı
nep: en iyi olarak bulunmuş e tane çiçeği bulan arı sayısı
nsp: en iyi olarak bulunmayan çiçekleri (yani m – e sayıdaki çiçekteki) arıların sayısı
ngh: ortamı ve bitiş koşulunu belirleyen parametre
Algoritmanın çalışması aşağıdaki şekilde adımlar halinde yazılabilir. (müsvedde kod (pseudo code))
- ortamın ilklenmesi (çiçeklerin hazırlanması).
- Başarı değerlerinin (fitness) atanması.
- While (istenen başarıdan düşükken)
- Arama için komşuların(çiçeklerin) belirlenmesi.
- Seçilen komşulara(çiçeklere) arıların gitmesi. ( en iyi çiçeklere daha çok arı yollanarak)
- Gidilen çiçeklerdeki başarının ölçülmesi.
- Kalan arıların kalan çiçeklere dağıtılması.
- End While.
Yukarıdaki algoritma görüldüğü üzere klasik bir komşu aramasından farklı olarak bulduğu iyi çiçeklere (optimum noktalara) daha fazla arı yollayarak bir sonraki aramada şansı arttırmaktadır.
Bir örnek üzerinde algoritmanın çalışması
Bu durumu görsel bir örnek üzerinden açıklayalım.
Arama yapılacak grafiğin yukarıdaki şekilde olduğunu kabul edelim. Bu grafik örnek olarak verilmiş olup grafik aslında hesaplanması zaman alan ve bütün noktaları bilinmeyen bir grafik olarak kabul edilir. Yani arama algoritmalarında amaç zaten bütün grafik noktalarını hesaplamadan bir optimum nokta bulmaktır. Biz konuyu açıklamak için grafiği açık olarak göstereceğiz.
Bu yazı şadi evren şeker tarafından yazılmış ve bilgisayarkavramlari.com sitesinde yayınlanmıştır. Bu içeriğin kopyalanması veya farklı bir sitede yayınlanması hırsızlıktır ve telif hakları yasası gereği suçtur.
Grafikte öncelikle arı hikayesinde olduğu üzere arama yapılacak noktaları belirleyeceğiz. Bu noktalara arıların bal topladıkları çiçekler diyebiliriz. Rast gele olarak aşağıdaki şekilde bu noktaların belirlendiğini kabul edelim:
Örneğimizde en küçük noktayı aradığımızı kabul ediyoruz. Bu durumda daha düşük noktadaki değerler bizim için daha kıymetli. Yukarıdaki örnekte 8 çiçek bulunuyor (m tane) ve örneğin arı sayımız 12 olsun. İlk durumda her çiçeğe birer arı hareket edip başarıyı ölçüyorlar (fitness).
İyi durumda olan çiçekler tespit ediliyor (e tane). Bu örnekte 3 noktayı iyi olarak kabul edersek aşağıdaki çiçekler öncelikli olacaktır.
Bu çiçekler bir sonraki çalışmada daha fazla arı alacak ve yakın noktalar daha çok aranacak.
İkinci adımda (döngünün ikinci dönüşünde) elimizdeki iyi olan 3 çiçek dışında yeni çiçekler tespit ediyoruz. Ayrıca iyi olan çiçeklerin yakınlarına fazladan arı yolluyoruz.
Yukarıdaki şekilde, iyi olarak bulunan çiçeklere ilave arı giderken geri kalan arılar farklı çiçekler tespit edip bu noktalarda arama yapmaktadır. Sonuçta bulunan en iyi noktalar aşağıdaki şekilde işaretlenmiştir.
Arama algoritmasının temsili olarak sonraki adımını aşağıdaki şekilde gösterebiliriz:
Yukarıda örnek bir grafik üzerinde arı arama algoritmasının çalışmasını gösterdik. Bu algoritma anlatıldığı üzere arıların bir bahçedeki çiçekler üzerinde bal yapmak için dolaşmaları ve bir arının başarılı bir çiçek bulması üzerine bu çiçeğe daha fazla arının yönlenmesi hikayesi gibi bir grafik üzerinde arama yapmaktadır.
Yukarıda algoritmanın çalışması birkaç adımda gösterilmiştir. Algoritma benzer şekilde istenen başarı değerine ulaşana kadar çalışmaya devam etmektedir.
Algoritmanın uygulama alanları
- Yapay sinir ağlarının eğitimi
- Üretim sistemlerinde iş planlaması
- Tasarım problemlerinde verimli alan hesaplanması (feasible region)
- Veri sınıflandırması (clustering)
SORU-40: Internal Path Reduction Trees ( İç Yol İndirgeme Ağaçları)
Bilgisayar bilimlerinde kullanılan veri saklama ve veriye kolay ulaşma yöntemlerinden birisi de ağaçlardır. Çok farklı şekillerde ağaçların kodlanması ve modellenmesi mümkündür. Bu özel ağaçlardan birisi de iç yol indirgeme ağaçlarıdır (internal path reduction tree, IPR Tree).
Bir ipr ağacı kısaca bir ikili ağaçtır (binary tree). Ayrıca IPR ağaçlarının dengeli olması şarttır (balanced tree).
Aynı şekilde ağacı sürekli olarak dengeli tutan ve bir ikili ağaç olan (binary tree) AVL ağaçları ile kıyaslandığında performans olarak eşit veya daha iyi olması söz konusudur. Yani IPR ağaçları AVL ağaçlarından ( avl tree) kötü performansta olamazlar, iyi veya en kötü durumda eşit performans gösterirler.
IPR ağaçlarının derinlik hesaplamasında ağacın iç yol uzunluğu (internal path length) hesaplanır. Bu değerin hesaplanışını hatırlayacak olursak, ağaçtaki iç düğümlere erişmek için yapılan işlem sayısının toplamıdır. aşağıdaki temsili ağaçta iç yol uzunluğu 3′tür.
Tam bu noktada kaynaklardaki bir ayrılıktan bahsetmekte yarar vardır. Bazı kaynaklar kök düğüme (root node) erişimin maliyetini 0 kabul ederken bazı kaynaklar bu maliyeti 1 kabul etmektedir. Örneğin yukarıdaki örnekte şayet kök düğüme erişim maliyeti 0 kabul edilirse ağacın toplam iç yol uzunluğu
0+ 1 + 2 + 0 = 3
Olarak hesaplanır. Ancak bazı kaynaklar iç yol uzunluğunu hesaplarken kök düğümün maliyetini 1 olarak alıp bütün düğümlere (sadece iç düğümlere (internal nodes) değil, hem iç hem de dış düğümlere (external nodes) olan uzaklık) olarak hesaplamaktadır.
Bu durumda yukarıdaki ağacın iç yol uzunluğu:
1 + 2 + 2 + 3 + 3 + 4 = 15
Olarak bulunur.
IPR ağacının çalışması sırasında istenen bir hesaplama yöntemi kullanılabilir, sonuç değişmemektedir.
IPR ağacı üzerinde yapılabilecek işlemleri aşağıdaki şekilde sıralayabiliriz:
Yukarıda sıralanan dört ayrı işlem, IPR ağaçlarında karşılaşılabilecek durumlardır. Bu durumları sıralayacak olursak, yukarıdaki 4 şekil için sırasıyla:
n c > n b => sola döndür
n x > n a => çift sola döndür
n c > n a => sağa döndür
n x > n a => çift sağa döndür
olarak sıralanabilir.
Yukarıdaki açıklamalardan sonra aşağıdaki kelimeleri bir ağaca yerleştirerek IPR ağacının nasıl çalıştığını görelim. Burada dikkat edilecek bir husus, IPR ağacının sadece yerleştirme (insertion) kuralının ikili arama ağacından (binary search tree) farklı olduğudur. Bunun dışında, ağaçta arama yapma işlemi veya bir bilgiyi değiştirme işlemi ikili arama ağacı (binary search tree) ile aynıdır.
Örnek olarak yerleştireceğimiz (insert) kelimeler aşağıdaki kelimeler olsun:
Şadi, Evren, ŞEKER, www, bilgisayar, kavramları, com
Yukarıdaki 7 kelimeyi verilen sırayla yerleştirmeye çalışalım:
Yukarıdaki ekleme işlemleri sırasında, klasik bir ikili arama ağacına ekleme dışında bir işlem yapılmamıştır. Ancak ağacın son halinde, eklenen son kelime ( “com” kelimesi) ile birlikte ağacın dengesi bozulmuştur. Bu aşamaya kadar ağaçta bir dengesizlik bulunmamaktadır.
Ağacın son halindeki durum çift sağa döndürme gerektirip, döndürülmüş hali aşağıda gösterilmiştir:
Bu döndürme işlemi iki aşamada yapılmıştır. Birinci aşamada çift sağa döndürürken x = kavramları, z = evren ve y = şadi şeklinde düşünülebilir. Ardından oluşan yeni ağacın hem solunda hem de sağında yeniden döndürme işlemleri icra edilmiştir.
SORU-41: Sürahi Problemi (Water Jug Problem)
Bilgisayar bilimlerinde klasik olarak kaynaklarda geçen ve problem çözümünü belirli bir alanda bulmayı hedefleyen problemlerden birisidir.
Problemi tanımlayacak olursak:
5 litrelik tamamen dolu ve 2 litrelik boş bir bidon ile başlanarak, 2 litrelik bidonda 1 litre elde edilmesi için gereken adımları bulunuz.
Problemin çözüm adımlarını aşağıdaki şekilde bir karar ağacına ve dolayısıyla ihtimaller silsilesine bağlamak mümkündür:
Problemi tanımlarken 5 lt ve 2 lt iki bidonu aşağıdaki şekilde gösterlim:
İlk durumda, 5lt dolu olduğuna göre aşağıdaki şekilde gösterebiliriz:
Şimdi bu durumdan başlanarak izlenebilecek yolları bir ağaç yapısında gösterelim:
Görüldüğü üzere problemde birkaç farklı ihtimalin izlenmesi söz konusudur. Yukarıdaki yapı için bir ağaç yapısı kullanılabileceği gibi bir graf yapısı da kullanılabilir. Yukarıdaki her adımdan gidilebilecek diğer adımlar ok ile gösterilmiştir. Sol alt köşede bulunan durum ise son durumdur ve bu duruma ulaşılınca hedefe ulaşıldığı için daha fazla ihtimal hesabı yapılmaz.
Yukarıdaki bu problemi farklı şekil arama algoritmaları ile (graph search algorithms) farklı şekillerde dolaşmak ve hedefe ulaşmak mümkündür. Yukarıdaki şekildeki her ihtimali aşağıdaki şekilde bir harf ile gösterecek olursak:
Derin öncelikli arama (depth first search) : A B C E F G
Sığ önceliki arama (Breadth first search) : A H B C A D E H F D G
Derin Sarmallı arama (iterative deepining search) : A B H A B C D A A B C E H A B C E F D A B C E F G
Görüldüğü üzere bu tip problemlerde derin öncelikli arama algoritmalarının daha hızlı sonuca ulaştığı söylenebilir. Genelde çözüm hedefi derinde olacağı ve başlangıca en uzak noktada bulunacağı için derin öncelikli arama , sığ öncelikli aramaya göre daha iyi sonuç verir.
SORU-42: Sınırlı Derin Öncelikli Arama (Depth-Limited Search)
Bilgisayar bilimlerinde kullanılan arama algoritmalarından birisidir. Bu algoritma esas olarak derin öncelikli arama (depth first search DFS) ile aynı çalışmaktadır ancak tek farkı arama işlemi sırasında özellikle dairelere (cycles) takılma ihtimaline karşı sınır önlemi alınmış olmasıdır.
Örneğin aşağıdaki şekli ele alalım:
Yukarıdaki şekil tanım itibariyle bir ağaç özelliği göstermektedir. Yani yönlü ve daire içermeyen bir şekildir (directed acyclic graph). Ancak aynı şekle aşağıdaki gibi basit bir bağlantı daha eklenseydi artık ağaç olmayacaktı:
Yukarıdaki yeni şekilde derin öncelikli bir arama yaptığımızı ve A düğümünden işleme başladığımızı düşünelim. Örneğin orta sıra (infix) ve L N R ( sol üst sağ , left node right) sırasıyla arama yaptığımızı düşünelim. Daire içermeyen ilk şekilde LNR sırasına göre aşağıdaki sonucun çıkması beklenir:
DBEAFC
Ancak ikinci şekilde LNR sırasına göre önce en soldaki terim yazılmaya çalışılacak, Böylece A->B->D->A->B->D->A->B->D->A sırasıyla namütenahi dönülecek ve hiçbir zaman bitmeyecek bir fasit daireye girilecektir (Sonsuz döngü). Bu durum literatürde sol özyineleme (left recursion) olarak geçer. Yani şeklimizin (veya herhangi bir yapının) sol tarafında kendini tekrarlayan bir durum bulunmakta dolayısıyla derin öncelikli arama yapılamamaktadır.
Çözüm olarak bu yazının da konusu olan sınırlı derin öncelikli arama (depth limited search, DLS) algoritması kullanılabilir. Bu algoritmada gidilebilecek düğüm sayısına bir tahdit konulmakta ve ancak verilen sayıda düğüme gidilebilmektedir.
Algoritmanın kodlanması
Yukarıda izah edilen algoritma aşağıdaki şekilde kodlanabilir:
Yukarıdaki özyineli fonksiyonda (recursive function) bakılan düğüm hedef olana kadar dolaşma işlemi devam etmektedir. Dolaşma işlemi sırasında klasik derin öncelikli aramalarda kullanılan yığın (stack) kullanılmış ve geçilen düğümler geri dönülüp aranmak üzere yığında tutulmuştur.
Şayet aranan düğüm verilen derinlikten daha derin değilse arama işlemi devam etmektedir ancak verilen derinlik geçildiği zaman arama işlemi daha derine gitmemekte ve artık o ana kadar aranmak üzere yığınladığı düğümleri işlemektedir.
Yukarıda anlatılan algoritma bilgisiz bir arama algoritmasıdır (uninformed search algorithm) ve ayrıca algoritmanın hafıza karmaşıklığı (memory complexity) sınırlıdır çünkü algoritmada aranabilecek düğüm sayısında bir sınır bulunmaktadır.
SORU-43: Tepe Tırmanma Algoritması (Hill Climbing Algorithm)
Bilgisayar bilimlerinde kullanılan arama algoritmalarından birisidir. Arama işleminin yapıldığı grafikteki tepelerden ismini alır. Basitçe bir grafikte bulunan en düşük noktanın aranması sırasında grafikte yapılan hareketin aslında tepe tırmanmaya benzemesinden ismini almaktadır.
Örneğin yukarıdaki şekilde gösterilen ok temsili bir tepe tırmanma işlemidir. Burada arama yapan algoritma aslında bir çukur bulmuş ancak daha iyisi için tepe tırmanmaktadır denilebilir.
Elbette olayı iki boyutlu bir grafik üzerinde yapılan bir arama veya tırmanılan bir tepe olarak görmek, algoritmanın önemini kavramayı engelleyebilir. Aslında burada anlatılmak istenen bilgisayar bilimleri de dahil olmak üzere, çeşitli bilim ve mühendislik alanlarında, birden fazla çözümü olan problemler için en iyi çözümün arandığı iyileştirme (optimization) problemleridir.
Yani şayet bir sistemin ya da bir programın daha iyi hale getirilmesi isteniyorsa, sistemin verdiği sonuçlara göre arama işlemi yapılarak iyileştirme amaçlanabilir. İşte bu noktada tepe tırmanma algoritması (hill climbing algorithm) de dahil olmak üzere çeşitli arama algoritmaları devreye girer. Örneğin literatürde sıkça kullanılan seyyar tüccar problemi (travelling salesman problem) bu problemlerden birisidir. Tepe tırmanma algoritması verilen bir harita için verilen bir sonucu iyileştirmeye çalışır. Elbette amaç bütün ihtimalleri denemeden iyi bir sonuç bulabilmektir.
Tepe tırmanma algoritması, arama algoritmaları arasındaki en iyi sonucu verene algoritma değildir. Ancak kodlanması ve tasarımının basit oluşundan dolayı sıklıkla kullanılır.
Tepe tırmanma algoritması çeşitleri
Algoritmanın üzerinde çeşitli iyileştirmeler yapılarak daha iyi sonuçlar elde edilmeye çalışılmıştır. Literatürde sıkça kullanılan bir iki tepe tırmanma algoritmasını farkları ile birlikte açıklamaya çalışalım.
Klasik tepe tırmanma algoritmasında amaç arama için merkez kabul edilen (veya başlangıç noktamız diyebileceğimiz) noktadan komşu olan noktaları gezerek daha iyi sonuçlar elde etmektir. Temel olarak bir grafikte rastgele seçilen bir nokta için 3 farklı ihtimal bulunmaktadır:
- Noktanın bir tarafında problem iyileşirken diğer tarafında kötüleşmektedir. Bu durumda iyi tarafa doğru tırmanma algoritmamız devam eder.
- Noktanın iki tarafında da problem sonucu kötüleşmektedir. Bu durumda bulunduğumuz nokta problem için en iyi noktalardan (optimum points) birisidir. Elbette bu en iyi sonuç olmayabilir yani bu sonuçtan daha iyi sonuçlar olabilir ancak klasik tepe tırmanma algoritması bu diğer sonuçları bulamaz ve bu noktada kalır.
- Noktanın iki tarafında da problem iyileşiyordur. Yani tesadüfen bulduğumuz nokta aslında problem için ulaşılabilecek en kötü noktalardan birisidir. Bu durumda tepe tırmanma algoritması yönlerden birisini seçerek tırmanmaya devam eder. Farklı bir çeşit olarak iki yöne de tırmanan algoritma bulunmaktadır.
Klasik tepe tırmanma algoritmasından farklı olarak steepest ascent hill climbing algoritmasında, bulunabilen bütün sonuçlar arasından bir seçim yapılır. Bu algoritmada da klasik tepe tırmanma algoritmasında da sorun aynıdır. Şayet arama işlemi sırasında bir yerel çukura (local minimum) rastlanılırsa bu durumdan algoritma kendisini kurtaramayarak en doğru sonucu bulamayabilir.
Buna karşılık olasılıksal tepe tırmanma algoritmasında ( stochastic hill climbing algorithm) bütün komşuların aranması ve komşuların verdiği sonuca göre hareket etmek yerine, rast gele olarak bir komşunun seçilmesi söz konusudur. Şayet gidilen bu komşu beklenen yönde bir iyileştirme sağlıyorsa, bu yönde aramaya (tırmanmaya) devam edilir, şayet beklenen iyileştirme sağlanamıyorsa, bu durumda daha farklı bir komşu denenir.
Yukarıdaki tepe tırmanma algoritmalarının yanında rastgele başlangıç tepe tırmanma algoritması (random restart hill climbing algorithm) şaşırtıcı derecede iyi sonuç veren bir algoritmadır. Bu algoritma basitçe bir x durumunu başlangıç kabul eder ve daha iyi bir durum bulunca başlangıç durumunu bu daha iyi duruma kaydırır. Algoritma iyi durum buldukça başlangıç durumunu kaydıran ancak bulamadığı durumlarda da aramaya devam eden bir yapıya sahiptir. Rastgele başlangıç tepe tırmanma algoritmasına bazı kaynaklarda pompalı tüfek tepe tırmanma algoritması (shotgun hill climbing algorithm) ismi de verilmektedir.
Tepe tırmanma algoritmaları genel olarak yerel bir başarı noktasında (local optimum point) takılmak gibi bir zafiyete sahiptirler. Daha iyi sonuçlar için simulated annealing gibi arama algoritmaları kullanılabilir.
Örneğin yukarıdaki şekilde x ve y noktaları arasında bir düzlük bulunmaktadır. Başlangıç noktası olarak bu aralıktaki herhangi bir noktadan başlanırsa algoritma komşuları aradığında daha iyi veya daha kötü bir sonuç bulamayacağı için hatalı karar verebilir.
Ayrıca tepe tırmanma algoritmaları problem sonuçlarında nadiren de olsa aynı sonucun elde edilmesi durumunda belirsizce hatalı sonuçlar verebilir. Bu şekildeki durumlara algoritmadaki düzlükler ismi verilir ve algoritma hangi yöne gidilirse gidilsin daha iyi bir sonuç çıkaramaz (hep aynı sonucu çıkarır). Elbette doğası gereği tepe tırmanmaya hazırlanmış algoritmamız, tepe bulamayınca hata yapmaktadır.
Algoritmanın kodlanması
Algoritma iki boyutlu bir grafik için rastgele yön seçimi yapılması durumunda aşağıdaki şekilde kodlanabilir:
Yukarıdaki kodda, basitçe başlangıç noktasından başlanarak, daha iyi sonuçlar bulunduğu sürece bir sonraki komşu noktaya hareket öngörülmüştür.
Ancak problemin çok boyutlu olması yani bir noktanın birden fazla komşusu bulunması durumunda bütün komşuların kontrol edilmesi ve en iyisinin bulunması daha sonra bu komşuya doğru hareket edilmesi gerekeceği için algoritmayı aşağıdaki şekilde yazmak mümkündür:
Yukarıdaki yeni kodda, bir noktanın birden çok komşusu olduğu kabul edilmiş ve bütün komşuları dolaşılarak en iyi komşu alınmış, ardından daha iyi komşu bulundukça bu işlem devam ettirilmiştir. Nihayetinde hiçbir komşu, bulunan son komşudan daha iyi olmayınca program sonlandırılmıştır.
SORU-44: DFA Metin Arama Algoritması (DFA Text Search)
Bilgisayar bilimlerinde, bir metnin içerisinde farklı bir metnin veya bir kelimenin aranması sırasında kullanılan algoritmalardan birisidir. Algoritma, aranan kelime için bir otomat (automaton) oluşturur ve hedef metin içerisinde bu otomata göre arama işlemi yapar.
Oluşturulana otomatın DFA (deterministic finite automaton, belirli sonlu otomat) olması gerekmektedir.
Algoritmanın çalışması iki aşamada incelenebilir:
- Aranan kelimeye özgü bir otomatın inşa edilmesi
- İnşa edilen bu otomatın bir metin içerisinde arama işlemi için kullanılması
Yukarıdaki bu adımları sırasıyla anlatmaya çalışalım ve ayrıca performanslarını inceleyelim.
Otomatın inşası
Bilindiği üzere bir DFA ifade edilirken şu dört terimden faydalanılır: A(x) =(Q, q0, T, E). Bu tanımın üzerinde arama yapacağı dili x harfleri kümesinden oluşan bir dil (language) ( *x) olarak tanımlarsak, otomatı oluşturan bu terimlerin metin arama işlemi için uyarlanmış hallerini şöyle açıklayabiliriz.
- Q kümesi x harflerinden oluşan ve bütün olasılıkları ifade eden kümedir ve Q={ , x[0], x[0 .. 1], … , x[0 .. m-2], x} olarak gösterilebilir.
- q0, otomatın başlangıç terimidir ve başlangıçta otomatta boş küme olduğunu düşünürsek q0= ; olarak gösterilebilir.
- T, otomatın üzerinde tanımlı olduğu dili oluşturan sembolleri gösterir. Bu örnekte dilimizin *x şeklinde tanımlı olmasından dolayı T = {x} olarak gösterilebilir.
- Son olarak E teriminin tanımını Q kümesinde tanımlı bütün “q” değerleri ve dilinde tanımlı herhangi bir “a” terimi için (q, a, qa) şeklinde tanımlayabiliriz. Bu tanımın doğruluğu, “a” değerinin “x” değerinin bir ön eki olması şartına bağlayabiliriz. Şayet bir ön ek olarak kabul edilmezse, yine (q,a,p) üçlüsünün E kümesinde olması şartıyla, p değeri x’in bir ön eki olan qa için en uzun ek olur.
Yukarıdaki otomat inşa edilirken m uzunluğunda bir kelime olduğu kabul edilirse (ki yukarıdaki Q kümesinin elemanları bu uzunlukta verilmiştir) inşa için gereken süre O( m + r) ve inşa için gereken hafıza ise O(mr) olarak hesaplanabilir.
Algoritmanın araması
Algoritma, yukarıdaki şekilde bir DFA inşa ettikten sonra bu DFA’i kullanarak hedef metin içerisinde arama yapar. Bu arama işlemi n boyutundaki bir metin için O(n) zamanda yapılabilir.
Bu arama işleminin çalışmasını bir örnek üzerinden inceleyelim.
Algoritmanın çalışması
Örnek olarak “bilgi” kelimesini “bilbilgisayarkavramlari” metninin içerisinde aramaya çalışalım.
Öncelikle kelimemiz için bir belirli otomat (DFA) oluşturmamız gerekiyor.
Dilimizdeki ( *x) alfabemizi tanımlayacak olursak:
{a,b,g,i,k,l,m,r,s,v,y} harflerinden oluşan bir dil olarak tanımlanabilir.
Bu dil üzerinde belirli bir sonlu otomat “bilgi” kelimesi için aşağıdaki şekilde inşa edilebilir:
d 0 { a -> 0 b -> 1 g -> 0 i -> 0 k -> 0 l -> 0 m -> 0 r -> 0 s -> 0 v -> 0 y -> 0 }
d 1 { a -> 0 b -> 1 g -> 0 i -> 2 k -> 0 l -> 0 m -> 0 r -> 0 s -> 0 v -> 0 y -> 0 }
d 2 { a -> 0 b -> 1 g -> 0 i -> 0 k -> 0 l -> 3 m -> 0 r -> 0 s -> 0 v -> 0 y -> 0 }
d 3 { a -> 0 b -> 1 g -> 4 i -> 0 k -> 0 l -> 0 m -> 0 r -> 0 s -> 0 v -> 0 y -> 0 }
d 4 { a -> 0 b -> 1 g -> 0 i -> 5 k -> 0 l -> 0 m -> 0 r -> 0 s -> 0 v -> 0 y -> 0 }
d 5 { a -> 0 b -> 1 g -> 0 i -> 0 k -> 0 l -> 0 m -> 0 r -> 0 s -> 0 v -> 0 y -> 0 }
Yukarıda verilen durumlar için gidilecek durumlar listelenmiştir. Bu otomatı daha iyi anlayabilmek için aşağıdaki şekilde çizebiliriz:
Yukarıdaki otomatta, 0. Durum başlangıç durumu olmaktadır. Ayrıca 5. Duruma her ulaşılmasında aranan metin bulunmuş demektir.
Yukarıdaki otomatın örnek metin üzerindeki çalışmasını adım adım açıklamaya çalışalım:
bilbilgisayarkavramlari |
İlk olarak hedef metinden b harfi alınıyor. Bu harf otomatımızın ilk geçişine tekabül ediyor ve dolayısıyla 0. Durumdan 1. Duruma geçiyoruz.
Geçiş: 0.b -> b
- durumdayken ikinci harfi hedef metinden okuyoruz:
Bilbilgisayarkavramlari |
Şu anda 1. Durumdan 2. Duruma geçmek için şartımız oluştu
Geçiş: b.i -> bi
Bu işlem aşağıdaki şekilde devam ettirilir:
BIlbilgisayarkavramlari | Geçiş: bi.l -> bil BILbilgisayarkavramlari | Geçiş: bil.b -> b
Yeni gelen harf, otomattaki 3. Durumdan 1. Duruma dönmeyi gerektiren b harfidir. Dolayısıyla aradığımız kelimenin ilk 3 harfi bulunmasına karşılık kelimenin tamamı bulunamamış ve başa dönülmüştür.
bilBilgisayarkavramlari | Geçiş: b.i -> bi bilBIlgisayarkavramlari | Geçiş: bi.l -> bil bilBILgisayarkavramlari | Geçiş: bil.g -> bilg bilBILGisayarkavramlari | Geçiş: bilg.i -> bilgi bilBILGIsayarkavramlari | Geçiş: bilgi.s -> 0 bilBILGIsayarkavramlari | Geçiş: 0.a -> 0 bilBILGIsayarkavramlari | Geçiş: 0.y -> 0 bilBILGIsayarkavramlari | Geçiş: 0.a -> 0 bilBILGIsayarkavramlari | Geçiş: 0.r -> 0 bilBILGIsayarkavramlari | Geçiş: 0.k -> 0 bilBILGIsayarkavramlari | Geçiş: 0.a -> 0 bilBILGIsayarkavramlari | Geçiş: 0.v -> 0 bilBILGIsayarkavramlari | Geçiş: 0.r -> 0 bilBILGIsayarkavramlari | Geçiş: 0.a -> 0 bilBILGIsayarkavramlari | Geçiş: 0.m -> 0 bilBILGIsayarkavramlari | Geçiş: 0.l -> 0 bilBILGIsayarkavramlari | Geçiş: 0.a -> 0 bilBILGIsayarkavramlari | Geçiş: 0.r -> 0 bilBILGIsayarkavramlari | Geçiş: 0.i -> 0
Sonuç olarak, yukarıdaki büyük harfle gösterilen alanda, aranan kelime bulunmuştur.
Algoritmanın kodlanması
Algoritmanın kodu C dili için aşağıdaki şekilde yazılabilir.
Yukarıdaki kodda, arama işlemini yapan ana fonksiyon, otomat fonksiyonudur. Arama işlemi sırasında bu fonksiyon çağrılarak işlem başlatılır. Bu fonksiyonun otomatı inşa etmek için kullandığı inşa fonksiyonu, kodun 27. Satırında çağrılmıştır. Bu çağırma işleminden önce bir otomaton oluşturulmuş ve atıf ile çağırma (call by reference) kullanılarak inşa fonksiyonuna geçirilmiştir.
Kod, 8 ile 14. Satırlar arasında bir DFA inşa etmekte ve 30 ile 33. Satırlar arasında ise bu otomatı kullanarak hedef metin içerisinde arama yapmaktadır.
SORU-45: Kaba Kuvvet Metin Arama Algoritması (Bruteforce Text Search Algorithm)
Bilgisayar bilimlerinde bir metnin içerisinde başka bir metnin aranması için kullanılan en ilkel ve dolayısıyla en düşük performanslı arama algoritmasıdır (search algorithm). Algoritma hedef metinde, aranan metni harf harf bulmaya çalışır. Bu yapısından dolayı diziler üzerinde kullanılan doğrusal arama (linear search) algoritmasına oldukça benzer ve literatürde doğrusal metin araması (linear text search) ismi de verilmektedir.
1 Algoritmanın başarısı
Kaba kuvvet algoritması, isminden de anlaşılacağı üzere çok zeki olmayan ve başarısını bilgisayarın yüksek hızda çalışmasından alan bir algoritmadır. Algoritma basitçe metinin tamamını çok zeki olmayan bir şekilde dolaşır ve aranan kelimenin ilk harfini bulana kadar bu işleme devam eder. Bulduğu anda geri kalan harfleri eşleştirmeye çalışır. Şayet harflerden birisini eşleştiremezse, kelimenin ilk harfini bulduğu yere geri dönerek arama işlemine devam eder. Gerçi çok zeki olmadığı için kelimenin tamamını eşleştirse bile yine de ilk harfi bulduğu yere geri dönerek arama işlemine devam eder.
2 Algoritmanın çalışması ve bir örnek
Kaba kuvvet metin arama algoritmasının (bruteforce text search algorithm) çalışmasını aşağıdaki örnek üzerinden anlamaya çalışalım.
Aranan kelimemiz : bilgi
Aranan metin: wwwbilgisayarkavramlaricom
Olarak veriliyor olsun. Bu durumda algoritma ilk harften başlayarak “bilgi” kelimesini aranan metin içerisinde bulmaya çalışacaktır.
wwwbilgisayarkavramlaricom b....
Öncelikle ilk harften başlanarak harfler karşılaştırılıyor. Aranan kelimenin ilk harfi “b” olduğu için bu harf bulunana kadar arama işlemi devam ediyor:
wwwbilgisayarkavramlaricom b.... wwwbilgisayarkavramlaricom b.... wwwbilgisayarkavramlaricom BILGI
“b” harfi ile başlayan bir yer bulundu. Artık diğer harfler karşılaştırılabilir. Sırasıyla “I”,”L”.. harfleri karşılaştırılıyor ve harfler tuttuğu sürece karşılaştırma işlemi devam ediyor. Şayet harflerden birisi beklenen sırada gelmezse karşılaştırma işlemi kesilip kalınan yerden devam ediliyor.
wwwBILGIsayarkavramlaricom b....
Aslında bu harflere bakılmış olmasına rağmen yine de aranıyor. Malum kaba kuvvet arama algoritması akıllı bir algoritma değildir ve bütün ihtimalleri dener. Dolayısıyla aslında bakmış olduğumu ve bakılmasının bir anlamı olmayan bu harflere de bu algoritma kapsamında bakılıyor.
wwwBILGIsayarkavramlaricom b....
Arama işlemi aşağıdaki şekilde geri kalan harflerin kontrolü ile devam ediyor:
wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b.... wwwBILGIsayarkavramlaricom b....
Yukarıdaki arama işlemi sonucunda büyük harfle gösterilen kısımda aranan kelime bulunmuştur. Toplam 26 harflik bir metin içerisinde 5 harflik “bilgi” kelimesi aranmıştır ve 22 adımda bulunmuştur.
Artın aranan kelimenin sığmayacağı son harflere bakılmamıştır. Örneğin aranan metnin son 4 harfi olan “icom” alt metni (sub string) üzerinde arama işlemi anlamsızdır çükü buraya “bilgi” kelimesi sığamaz.
3 Algoritmanın kodlanması
Algoritmanın C, C++, JAVA veya C# gibi diller için kodlaması aşağıdaki iki iç içe döngü (nested loop) şeklinde yapılabilir:
Yukarıdaki kodda, n boyutundaki y hedef dizgisi (string) içerisinde m boyutunda x dizgisinin arandığı kabul edilmiştir. Döngü basitçe n-m kere dönmektedir (yukarıdaki örnekte 22 kere dönemsi gibi) ve şayet aranan kelimenin ilk harfi bulunursa, 8. Satırdaki iç döngü dönmeye başlar. Harfler tutuştukça dönme işlemi devam ettirilir. Nihayetinde 11. Satırdaki koşul gerçekleşince, yani tutuşan harflerin sayısı, aranan kelimenin boyutunu geçince, yani aradığımız kelimedeki harf kadar harf, birbirini tutunca sonuç gösterilir. Bu işlem metnin sonuna kadar tekrarlanır.
SORU-46: Arama Algoritmaları (Search Algorithms)
Bilgisayar bilimlerinde, çeşitli veri yapılarının (data structures) üzerinde bir bilginin aranması sırasına kullanılan algoritmaların genel ismidir. Örneğin bir dosyada bir kelimenin aranması, bir ağaç yapısında (tree) bir düğümün (node) aranması veya bir dizi (array) üzerinde bir verinin aranması gibi durumlar bu algoritmaların çalışma alanlarına girer.
Yapısal olarak arama algoritmalarını iki grupta toplamak mümkündür.
- Uninformed Search (Bilmeden arama)
- Informed Search (Bilerek arama)
Arama işleminin bilmeyerek yapılması demek, arama algoritmasının, probleme özgü kolaylıkları barındırmaması demektir. Yani her durumda aynı şekilde çalışan algoritmalara uninformed search (bilmeden arama) ismi verilir. Bu aramaların bazıları şunlardır:
- Listeler (diziler (array)) üzerinde çalışan arama algoritmaları:
- Doğrusal Arama (Linear Search)
- İkili arama (binary search)
- Interpolasyon Araması (Ara değer araması, Interpolation Search)
- Şekiller (graflar (Graphs) ) üzerinde çalışan algoritmalar
- Sabit Maliyetli Arama (Uniform Cost Search)
- Floyd Warshall algoritması
- Prim’s Algoritması
- Kruskal Algoritması
- Dijkstra Algoritması
- Bellman Ford Algoritması
- İkili arama ağacı (Binary Search Tree)
- Prüfer dizilimi
- Ağaçlarda Sığ öncelikli arama (breadth first search)
- Şekillerde (Graph) sığ öncelikli arama (Breadth First Search, BFS)
- Derin öncelikli arama (depth first search)
- Derin Limitli Arama (Depth Limited Search) Algoritması
- Yinelemeli Derinleşen Derin Öncelikli Arama Algoritması (Iterative Deepining Depth First Search, IDDFS)
- Patricia Ağaçları
- Trie Ağaçları (metin ağaçları, trie trees)
- B-ağaçları (B-Tree)
- Metin arama algoritmaları (bir yazı içerisinde belirli bir dizgiyi (string) arayan algoritmalar)
Arama işleminin bilerek yapılması ise, algoritmanın probleme ait bazı özellikleri bünyesinde barındırması ve dolayısıyla arama algoritmasının problem bazlı değişiklik göstermesi demektir. Bu algoritmaların bazıları a aşağıda listelenmiştir:
- Minimax Ağaçları
- Simulated Annealing (Benzetimli Tavlama) algoritması
- Tepe Tırmanma Algoritması (Hill Climbing Algorithm)
- Arı sürüsü arama algoritması (bees search algorithm)
- A* Araması (astar search)
- Geri izleme (backtracking)
- Işın arama (beam search)
SORU-47: Şanslı Sıralama (Lucky Sort)
Sadece teorik olarak literatürde geçen bir sıralama algoritmasıdır (sorting algorithm). Buna göre sıralanacak olan dizi şanslı bir şekilde zaten sıralı verilmiştir. Dolayısıyla dizinin sıralanmasına gerek yoktur. Hatta bu kabulü yaptığımız için dizinin sıralı olup olmadığını kontrol etmemize de gerek yoktur (ne de olsa şanslıyız J ) dolayısıyla giriş dizisi her zaman sıralı olan sıralama algoritmasıdır. Anlaşılacağı üzere hiçbir işe yaramaz. Sadece teorik olarak O(0) zamanda sıralama yaptığının bilinmesi ve bu durumun hem en iyi hem de en kötü zaman olduğunun anlaşılması yeterlidir. Algoritmaya çeşitli kaynaklarda şans sıralaması, lucksort, luckysort şeklinde isimler de verilir.
Bazı kaynaklarda bogo sıralamasının (rastgele sıralamanın) şanslı bir şekilde ilk atışta tutması olarak geçmektedir ancak bu kesinlikle yanlıştır. Çünkü bogo sıralamasında bir dizinin sıralı olup olmadığı en az bir kere kontrol edilmelidir. Bu yüzden bogo sıralamasında şanslı da olsak (ve diğer bütün sıralama algoritmaları için de bu durum geçerli) en iyi ihtimalle O(n) zamanda algoritma sona erer. Şanslı sıralamanın avantajı ise şanslı olduğumuzu biliyor olmamız ve kontrol etmeye bile ihtiyaç duymamamızdır.
SORU-48: Bogo Sıralama (Bogosort)
Bilgisayar bilimlerinde özellikle eğitim amacıyla kullanılan bir sıralama algoritmasıdır. Algoritmanın çalışması oldukça basittir, bogosort, verilen bir diziyi sıralamak için rast gele bir dizilim üretir ve sıralı olup olmadığına bakar, şayet sıralıysa algoritma sona erer, şayet sıralı değilse rastgele olarak yeni bir dizilim elde eder, ta ki sayılar sıralanana kadar sayıları rastgele dizmeye devam eder.
Bu algoritma basitçe bir dizinin sıralı olana kadar rastgele dizilmesi olarak ifade edilebilir. Algoritmaya rastgele sıralama (random sort) veya maymun sıralaması (monkey sort) veya çifteli tüfek sıralaması (shotgun sort) anlamında isimlerde verilmektedir.
Bu algoritmanın gerçek hayatta kullanılabilirliği işlem süresinin uzunluğundan dolayı yoktur. Ancak eğitim amaçlı olarak literatürde geçmektedir.
Algoritmanın en iyi ihtimali O(n) olarak düşünülebilir çünkü şayet verilen dizi sıralıysa sadece dizinin sıralı olduğunu kontrol etmek için n tane elemanın üzerinden bir kere geçilmesi yeterlidir.
Buna karşılık en kötü ihtimalle sonsuza kadar çalışabilecek bir algoritmadır. Algoritmanın ortalama performansı için biraz istatistik bilgilerimizi tazelememiz gerekir.
Bilindiği üzere bir dizideki n elemanın farklı dizilim sayısına permütasyon ismi verilir. Bir dizideki n elemanın n! Tane farklı dizilme ihtimali bulunur.
Bu durumda her dizilimin n elemanlı bir kontrolden geçeceğini düşünürsek algoritmanın ortalama performansı (Average case) O(n n!) olarak bulunur.
Aşağı sürüklemeli otomatlar (push down automaton) yapı olarak birer otomat makineleridir. Normal bir sonlu otomattan farkı, belirli (deterministic) olması ve ilave bir yığın (stack) bulundurmasıdır. Yani makinemiz basitçe her adımda ne yapacağından tam olarak emindir (belirli ,deterministict) ve veri depolamak için hafızada bulunan bir yığından (stack) istifade edebilir. Düzeltme (Tarık Bey’e teşekkürler): PDA’ler için kullanılan otomata (automaton) göre belirli (deterministic) veya belirsiz (nondeterministic) olma ihtimali vardır. Yani kullanılan otomat belirliyse (Deterministic) bu PDA de belirli aşağı sürüklemeli otomat ( Deterministic Push Down Automaton DPDA) olarak isimlendirilir. Şayet tersine kullanılan otomat belirsizse (non deterministic) bu durumda otomat, belirsiz aşağı sürüklemeli otomat (NonDeterministic PushDownAutomata NPDA) olarak isimlendirilir. Bir sonlu otomattan (finite state machine) farkı ise yığın (stack) kullanılmasıdır.
Bilindiği üzere yığın (stack) yapısının temel iki fonksiyonu bulunur. Koyma (push) ve alma (pop) işlemleri isimlerinden de anlaşılacağı üzere yığına koyma ve yığından bir veriyi alma işlemini gerçekleştirir. PDA içerisinde bu fonksiyonlar aynen bulunur. Buna ilave olarak bir pda’i açıkça tanımlayabilmek için 6 bilgi gerekir. Bu bilgiler aşağıdaki şekilde gösterilebilir:
(Q,S,G, d ,q0,F)
Yukarıdaki satır bir aşağı sürüklemeli otomat için kabul edilen en standart gösterim şeklidir. Kaynaklarda aynı bilgiyi ifade etmek için farklı semboller kullanılmaktadır ancak semboller değişse de pda için bu bilgiler gerekir:
Q: makinemizde (otomatımızda ,automaton) bulunan durumları (states) gösterir.
S: makinemizin kabul ettiği girişte kullanılabilecek alfabenin (alphabet) kümesidir.
G: yığında (stack) kullanılabilecek alfabenin (alphabet) kümesidir.
d: Q ile gösterilen durumlar (states) arasındaki geçişlerin kümesidir.
q0: başlangıç durumudur ( initial state)
F: Bitiş durumudur (final state)
Yukarıdaki bu akademik gösterim ile neyin kastedildiğini bir örnek üzerinden anlamaya çalışalım. Örnek olarak çok klasik bir makine olan 0n1 n probleminin çözüm makinesini pda olarak göstermek isteyelim. Diğer bir deyişle makinemiz bir giriş kelimesini alacak ve bu kelimedeki 0′ların sayısı 1′lerin sayısına eşitse ve o’lar 1′lerden önce geliyorsa bu kelimeyi kabul edecek, şayet 0′ların sayısı ve 1′lerin sayısı eşit değil veya sıralamada bir hata varsa bu girdiyi kabul etmeyecek.
Çözmeye çalıştığımız problemi daha iyi anlayabilmek için bir iki girdinin kabul edilip edilmeyeceğini inceleyelim:
l : kabul, n= 0 için doğru (burada l sembolü ile boş girdi kastedilmiştir)
01 : kabul , n = 1 için doğru
10 : ret , sıralama hatası, 0′lar 1′lerin önünde olmalı
0011: kabul n = 2 için doğru
0101 : ret, sıralama hatası , bütün 0′lar, 1′lerin önünde olmalı
00011: ret, 0′ların sayısı ile 1′lerin sayısı tutmuyor
Şimdi yukarıdaki problemin çözümü olan aşağı sürüklemeli otomatımızı tasarlayalım. Yukarıdaki tanımda 6 unsurun bulunması gerektiğinden bahsetmiştik. Sırayla bunlara cevap arayalım. Önce makinemizi tasarlayarak başlayalım. Makinenin tasarımı için bir yığın (stack) kullanacağız. Biliyoruz ki şayet makineye gelen 0′ları sırasıyla koyarsak (push) ve 0′lar bittikten sonra gelen her 1 için yığından bir eleman alırsak (pop) bu durumda makine girdi kelimesi bittiğinde yığını boş bulursa (yığındaki harfler ile girdideki harfler aynı anda biterse) kelimeyi kabul edecek aksi halde reddedecektir.
Bu tasarımı bir iki örnek ile anlamaya çalışalım. Örneğin girdimiz 0011 olsun.
İlk durumda makinemiz girdinin ilk harfi olan 0′ı okuyacak ve 0 gördükçe yığına koyacak (push)
Yığına 0′ları koyduktan sonra her gördüğü 1 için yığından bir 0 çıkaracak (pop):
Görüldüğü üzere 2 adet 1 harfi için 2 adet 0 harfi yığından çıkarılmıştır (pop) ve sonuçta girdi kelimenin sonuna ulaşılmış ve yığında aynı anda boşalmıştır. Dolayısıyla 0011 kelimesini kabul ederiz.
Bu yazı şadi evren şeker tarafından yazılmış ve bilgisayarkavramlari.com sitesinde yayınlanmıştır. Bu içeriğin kopyalanması veya farklı bir sitede yayınlanması hırsızlıktır ve telif hakları yasası gereği suçtur.
Benzer makinemiz için bu sefer reddedilecek bir girdiyi tecrübe edelim. Misal girdimiz 001 olsaydı ne olurdu?
İlk durumda makinemiz girdinin ilk harfi olan 0′ı okuyacak ve 0 gördükçe yığına koyacak (push)
Yığına 0′ları koyduktan sonra her gördüğü 1 için yığından bir 0 çıkaracak (pop):
Görüldüğü üzere girdi kelimesinin sonuna gelindiğinde hâlâ yığında bir harf bulunmakta bu durumda girdiyi reddetmek zorundayız.
Farklı bir örnek olarak 0101 girdisini tecrübe edelim:
İlk durumda makinemiz girdinin ilk harfi olan 0′ı okuyacak ve 0 gördükçe yığına koyacak (push)
Yığına 0′ları koyduktan sonra her gördüğü 1 için yığından bir 0 çıkaracak (pop):
Bu defa sıfırları yığına koyup ardından her 1 için bir çıkarma (pop) işlemi yaptıktan sonra hâlâ girdi kelimesinin bitmediğini görüyoruz. Bu durumda girdi kelimemizi kabul etmiyoruz.
Tasarımını ve testini yaptığımız yukarıdaki makinemizi bir sonlu durum makinesi olarak çizmek istersek aşağıdaki şekli elde ederiz:
Yukarıdaki makine tasarımımızı kısaca gözden geçirecek olursak. Kabul edilen durumlar q1 ve q4 durumlarıdır. Yani l (boş kelime) durumunu kabul için q1 diğer kabul edilir durumlar için de q4 durumu (state) tasarlanmıştır. Tasarımda bulunan q2 durumu geçiş durumudur. Yani q2 durumunda bulunduğu sürece makine girdiden bir harf okumakta ve bu harf 0 olmaktadır. Okunan harf 0 olduğu sürece de bu değer yığına (stack) konulmaktadır (push). Bu durum (yani q2 durumu) girdiden bir harf olarak 1 gelmesinde bozulur. Şayet girdiden 1 harfi okunursa bu defa durum değiştirilerek q3 durumuna geçilir ve bu q3 durumunda da girdiden 1 harfi okundukça yığından 0 harfi çıkarılır (pop).
Son durumda şayet girdi boşsa ve yığın da boşsa q4 durumuna yani kabul durumuna geçilir. Bunun dışındaki ihtimallerde q2 yada q3 gibi kabul edilmeyen bir duruma takılır ve makinemiz girdiyi reddeder.
Bu yazı şadi evren şeker tarafından yazılmış ve bilgisayarkavramlari.com sitesinde yayınlanmıştır. Bu içeriğin kopyalanması veya farklı bir sitede yayınlanması hırsızlıktır ve telif hakları yasası gereği suçtur.
Yukarıdaki makinemiz görüldüğü üzere başarılı bir şekilde çalışıyor. Bu makineyi bir aşağı sürüklemeli otomat (push down automaton) olarak yazacak olursak aşağıdaki tanımları yapmamız gerekir:
S={0,1} olacaktır çünkü girişte ya 0 ya da 1 gelebilmektedir. Bunun dışında bir harfin gelmesi söz konusu değildir.
G={0,#} olacaktır. Burada yığında olabilecek harfler kümesi gösterilirken dikkat edilirse yığına sadece 0 harfini koyuyoruz (istenildiği kadar). Dolayısıyla 1 harfi bu kümede bulunmaz. Bu kümede bulunan # sembolü ise yığının boş olduğunu ifade için konulmuştur. Yani yığın boş olduğunda bunu da bir şekilde göstermemiz gerekir. Bu boş durumu temsilen # sembolü kullanılmıştır. Yine farklı kaynaklarda farklı geçen bir semboldür ancak anlamsal olarak bütün PDA gösterimlerinde böyle bir sembol bulunmalıdır.
Q= { q1, q2, q3, q4 } olarak yazılabilir çünkü yukarıdaki makine tasarımında da görüldüğü üzere 4 durum bulunmaktadır.
F = { q1, q4 } olarak yazılabilir çünkü yukarıdaki makinede kabul edilen 2 durum vardır ve bunlar da q1 ve q4 durumlarıdır.
q0 = { q1 }olarak yazılabilir. Burada şekilden de anlaşılacağı üzere başlangıç durumumuz q1 durumudur.
Son olarak sonlu durum makinemizin (finite state machine) tasarımını göstermemiz gerekmektedir. Bu gösterim için yine farklı kaynaklarda farklı yöntemler kullanılmaktadır. Örneğin yukarıdaki şekilde bir sonlu durum makinesi çizilmesi yeterli görülebilirken bazı kaynaklarda aşağıdaki gösterim kullanılmıştır. Hangi gösterim kullanılırsa kullanılsın sonuçta bu adımda anlatılan şey bir sonlu durum makinesidir ve bu makinede bulunan ve makineyi bir PDA yapan ise bir yığın (stack) kullanılmasıdır.
|
Giriş Yığını |
0 |
1 |
l |
||||||
|
0 |
# |
l |
0 |
# |
l |
0 |
# |
l |
|
|
q1 |
q2,0 |
||||||||
|
q2 |
q3, l |
||||||||
|
q3 |
q3, l |
q4, l |
|||||||
|
q4 |
|||||||||
Yukarıdaki gösterim aslında şekil olarak çizilen sonlu durum makinesini tablo olarak göstermekten başka bir işe yaramaz. Bu tabloda 9 sütün bulunmaktadır. Bunlar sırasıyla 0,1 ve l durumlarından yine 0,1 ve l durumlarına geçişleri gösterir. Örneğin tablonun ilk satırı 0′dan 0 durumuna geçişi son sütünü ise l‘dan l durumuna geçişi gösterir.
Tablodaki 4 satır ise makinemizdeki durumları gösterir. Makinenin anlamlı olabilmesi için başlangıç durumunun { q1 } olduğunu ve kabul edilir bitiş durumlarının { q1, q4 } olduğunu bilmek gerekir (ki bu bilgiyi zaten veriyoruz).
Yukarıdaki tablonun okunmasını daha net anlamak için bir örnek durumu inceleyelim. Örneğin makinemiz girdide bulunan bütün 0′ları okumuş ve yeni bir harf olarak 1 ile karşılaşmış olsun. Bu durumda q2 durumundan q3 durumuna geçiş yapması gerekir. Şekilde görüldüğü üzere bunu yapacağı tek durum bir 1 okunması halinde q3 geçmesini söyleyen satırdır.
SORU-50: Program doğruluğu ( Program correctness)
Bilgisayar bilimlerinde bir programın istenen özellikleri yerine getirip getirememesine verilen isimdir. Buna göre şayet bir program, beklenen özellikleri tam ve eksiksiz yerine getiriyor, istenmeyen sonuçlar ortaya çıkmıyor ve program başladıktan sonra her durumda başarılı bir şekilde bitiyorsa bu programa tam doğru ( total correctness) ismi verilir.
Durma probeleminden (halting problem) bilindiği üzere bir programın bitip bitmemesinin test edilmesi ayrı bir muammadır. Dolayısıyla tam doğru bir programın elde edilmesi veya ispatlanması farklı güçlükleri beraberinde getirir. Şayet bir program, kendinden beklenen özellikleri başarılı bir şekilde yerine getiriyor ancak bitip bitmemeyi garanti edemiyorsa (bir programın biteceği ispatlanamıyorsa) bu durumda programa kısmi doğru (partial correctness) ismi verilir.
Aslında programların doğruluğu (correctness), karar problemlerinin (decision problems) çözülmesidir.
Tam bu noktada bilgisayar bilimleri konusunda veciz sözleri ile konuya aydınlık getiren Dijkstranın bir sözünü hatırlamakta yarar var. “Testler hataların varlığını gösterebilir ama yokluğunu gösteremez.”
Bu yazı şadi evren şeker tarafından yazılmış ve bilgisayarkavramlari.com sitesinde yayınlanmıştır. Bu içeriğin kopyalanması veya farklı bir sitede yayınlanması hırsızlıktır ve telif hakları yasası gereği suçtur.
Örneğin doğal sayılar kümesi üzerinde sayıların asal olup olmadığını test eden bir program yazdığımızı düşünelim. Bu program basitçe bir sayıyı alacak ve sayının çarpanlarını bulacaktır. Şayet sayının kendisi ve 1 dışında başka çarpanı yoksa bu sayıyı asal sayı ilan edecektir. Bu şekilde bir program yazmamızda herhangi bir sorun yoktur ancak programın biteceğini garanti edemeyiz. Elbette buradaki problem doğal sayıların bir sonu olup olmadığıdır. Yani program bütün asal doğal sayıları bulmaya çalışırsa sonsuz sayıda işlem yapması gerekir (ya da sonsuz bilinen bir değerse sonlu sayıda). Bu noktada bilgisayarın hafıza gibi kısıtları devreye girer. Yani sonsuz sayı işleyecek bir hafıza kapasitesi henüz bilgisayarlarda bulunmamaktadır.
Bu ispat sırasında programın bir lambda cebirine (lambda calculus) çevrilmesi ve bu cebirde doğruluğunun ispatlanması mümkündür. Bu tip ispat yöntemlerine program çıkarımı (program extraction) ismi verilir. Örneğin Curry Howard karşılığı (curry howard correspondence) yaklaşımı buna bir örnek olarak gösterilebilir.
Bir programın doğruluğunun tetkikine, program tetkiki (program verification) ismi verilir.
Program doğrulamanın kullanıldığı yerler
Program doğrulama, bir programın geliştirilme sürecinde, son adım olan test aşamasına gelinmeden hataların bulunması ve programın kodlanma hatta tasarım aşamasında hatalarının yakalanmasını sağladığı için oldukça önemlidir. Örneğin bitmiş bir programdaki bir hatanın test ile bulunması bütün tasarım ve kodlama adımlarına geri dönülmesini ve dolayısıyla hatanın telafisi için harcanan maliyetin yüksek olmasını sağlarken, daha henüz kodlama aşamsında hatanın tespiti maliyeti düşürmektedir.
Ayrıca yazılan programın hatasının test aşamasında hiçbir zaman bulunamaması da söz konusu olabilir. Bu ihtimale karşı programın matematiksel olarak doğruluğunun ispatlanması çok önemlidir.
SORU-51: Rastgele Sayılar (Random Numbers)
Bilgisayar bilimlerinde çeşitli amaçlarla rastgele sayılara ihtiyaç duyulur. Örneğin şifreleme algoritmalarında önemli bir role sahip olan rastgele sayılar şifreleme işleminin gizliliği ve güvenilirlik açısından önemlidir. Benzer şekilde bir oyun programlanırken veya bir simülasyon sırasında rastgele cereyan eden olaylar modellenirken rastgele sayılara (random numbers) ihtiyaç duyulur ve bu sayıların gerçekten rastgele olması beklenir.
Örneğin kaydırma şifrelemesi (shift cipher) kullanımı sırasında rast gele bir anahtar üretilmesi istendiğini düşünelim. Bilgisayarın her zaman için aynı rast gele sayıyı üretmesi istenmeyen bir durumdur çünkü bu durumda sisteme bir kere saldırarak anahtarı ele geçiren saldırganın bundan sonraki saldırılarda anahtarı bulmak için vakit kaybetmesine gerek kalmaz. Benzer şekilde farklı sayılar üreten ama sayıların tahmin edilmesi mümkün olduğu durumlarda da sistemin karmaşıklığı azaltılarak sistemin getirdiği karmaşıklık sonucu beklenenden oldukça kısa sürelerde sisteme saldırı gerçekleşebilir.
Bu anlamda ne yazık ki bilgisayarların rastgele sayı üretiminde oldukça başarısız olduklarını söylemek gerekir. Bilgisayarlar tasarımları ve yapıları itibariyle rastgeleliğe yer bırakmayan belirli ve her adımında olacak şeylerin önceden tasarlandığı makinelerdir. Bu anlamda bilgisayarlarda rastgele bir bilgi üretmek oldukça zordur.
Bilgisayarlar kullanılarak rast gele sayı üretimi için en kolay yollardan birisi müsvette rastgele sayı üretimi yöntemleridir (pseudo random number generator). Bu sayı üretim yöntemlerindeki amaç aslında belirli bir matematiksel fonksiyona dayanarak bir dizi sayı üretmektir. Ancak bu matematiksel fonksiyon gizlenerek üretilen sayının tahmin edilmesi engellenmeye çalışılır.
Örneğin en basit müsvette rastgele sayı üreteçlerinden birisi olan aşağıdaki örneği inceleyelim:
F(i) = 3i mod 7
Yukarıdaki fonksiyonu i = {1,2,3,4… n} gibi sayılar için hesaplarsak her seferinde aynı sayı serisinin tekrarladığını görürüz. Bu anlamda bu sayı serisine dairesel grup (cyclic group) ismi verilebilir. Ancak burada enteresan olan bir özellik aslında mod 7 olması itibariyle 7 sayıdan oluşacak olan sayıların hangi sırayla geleceğinin tahminin güç olmasıdır. Örneğin bu sayı serimizi hesaplarsak :
{1,3,2,6,4,5,1} sayılarını buluruz. (30 mod 7 = 1, 31 mod 7 = 3, 32 mod 7 = 2 şeklinde devam eden seri)
Şimdi bu noktada durup düşünelim. Örneğin yukarıdaki F fonksiyonu ile son üretilen sayı 6 olsun. Bir sonraki sayının kaç olacağını şayet F fonksiyonunu bilmiyorsak tahmin etmemiz güçtür. Elbette buradaki örnekte mod 7 alındığı için üretilebilecek sayılar 7 tanedir ve bu ihtimal sayısı oldukça azdır. Ancak daha yüksek mod tabanlarında örneğin 20 haneli bir modüler fonksiyonda bu değerin tahmin edilmesi çok daha güç olur.
Ancak yine de sayılarımız arasında bir bağlantı bulunmaktadır ve kötü niyetli birisi (örneğin oyunda hile yapmak isteyen birisi veya şifreleme sistemine saldırmak isteyen birisi) bu bağlantıyı çözerek (veya casusluk marifetiyle öğrenerek) kötü niyetli olarak istifade edebilir.
Bu anlamda Güvenli rastgele’nin tanımını yapmamız gerekir.
Güvenli Rastgele (Secure Random)
Güvenli rastgele sayılar her defasında bir besleme (seed) değeri ile üretilen sayılardır. Buna göre sayının üretilmesi belirli bir fonksiyona bağlı değildir. Fonksiyonun en az bir yerinde dışarıdan gelen bir besleme değeri bulunur ve bu değere göre fonksiyonun ürettiği sonuç değişir.
Genelde bilgisayarlar açısından dışarıdan besleme almak zordur. Ya ilave bir donanıma ya da ne yaptığının farkında olan eğitimli bir kişiye ihtiyaç duyulur. Bunların ikisi de maliyetli olduğu için daha az maliyetli ve dolayısıyla daha az güvenli olan zaman faktörü kullanılır.
Bu yazı şadi evren şeker tarafından yazılmış ve bilgisayarkavramlari.com sitesinde yayınlanmaktadır.
Yani bilgisayarın saati kullanılarak rastgele sayı üreteci beslenir. Umulur ki saldırgan taraf bu besleme zamanını bilmediği için rastgele sayıyı tahmin etmesi de zor olur. Ancak bir önceki müsvette rastgele sayı üretimine göre daha güvenli olan bu yöntem de ne yazık ki tam olarak güvenli değildir çünkü saldırgan taraf her zaman için besleme zamanını tahmin edebilir.
İlave donanım kullanılması durumunda örneğin kararsız bir elektronik devreden veya atom altı parçacıklardan veya doğal bazı olaylardan (örneğin güneşteki patlamalardan) faydalanılabilir. Elbette faydalanılan besleyici kaynağın (seeder) karmaşıklığına göre sistemin maliyeti artacaktır.
Diğer bir sık kullanılan besleme (seeder) yöntemi ise insan faktörüdür. Sonuçta bilgisayarların çoğunun etrafında insanlar bulunmaktadır ve insanların yaptığı eylemler tam olarak düzenli sayılmaz. Hatta bazı durumlarda tamamen düzensizdir J.
Örneğin bir insanın klavyede yazı yazması sırasında tuşlara basış hızı rastgele kabul edilebilir. En tecrübeli klavye kullanan insanın bile her harfe basış hızı ve her defasında basış hızı mili saniyeler cinsinden mutlaka farklılık gösterir. Bu özellik kullanılarak örneğin bir kullanıcının bir metni yazması sırasında tuşlara basış zamanlarından rastgele sayılar üretilebilir.
Diğer bir yöntem ise bilgisayarın kararsızlığıdır. Evet yapı olarak bilgisayarlar kararlıdır ancak günümüz bilgisayarlarında çok işlemli (multi processing) işletim sistemleri çalışmaktadır ve bir işlemin yapılma süresi o andaki bilgisayarın durumuna göre değişmektedir. Örneğin bilgisayar saatinin mili saniye hanesindeki değeri okumak istersek ve bu işlemi belirli bir sürede bir tekrar edersek bilgisayar bize hep aynı sonucu verecektir. Ancak bu tekrarları belirli bir kompleksliğin üzerindeki işten sonra yaparsak bu durumda bilgisayar elindeki kaynağa göre farklı zamanlarda işi bitireceği için bize verdiği saat bilgisi değişebilecektir. Tabi saldırgan taraf bu durumda bilgisayarımızdaki o anda çalışan işlem miktarını tahmin etmek zorunda kalacaktır.
Ancak bu son örneklerde gösterilen ve literatürde kendinden beslemeli (self-seeding) olarak geçen yöntemler sonuçta bilgisayarda bulunan bazı özellikleri kullandığı için tahmin edilmesi ve saldırılması nispeten kolay yöntemlerdir.
SORU-52: Matematiksel Tümevarım Teoremi (Mathematical Induction Principle)
Bilgisayar bilimlerinin de için de bulunduğu pek çok mühendislik ve bilim disiplinlerinin kullandığı ispat yöntemlerindendir. Temel olarak mantıktaki istikra (tümevarım) yaklaşımından faydalanır.
Basitçe bir eşitliği ispatlamak için, eşitliğin her iki tarafı da birer kere ilerlettirilir (bir sonraki terimler için hesaplanır) şayet bu durum eşitliği bozmuyorsa ve eşitlik bir seri (sequence) için doğrulanabilir durumdaysa bir de başlangıç durumu ispatlanabiliyorsa bu seri eşitliği ispatlamak için yeterli bulunur.
Matematiksel indüksiyon prensibi olarak da Türkçeye çevrilen bu ispat yöntemini aşağıdaki örnek üzerinden anlamaya çalışalım:
Örneğin ispatlamak istediğimiz kaziye (önerme) şöyle olsun:
Pozitif ilk n tek sayının toplamı n2 ‘dir.
Bu iddiayı anlamak için n = 5 durumunu inceleyelim (bu adım ispatın bir parçası değildir sadece eşitliği anlamak için yapıyoruz)
1 + 3 + 5 + 7 + 9 = 25 (ilk 5 tek sayının toplamı)
52 = 25
dolayısıyla eşitliğimiz bu örnek için doğru çıktı. Şimdi bu eşitliği matematiksel tümevarım ile ispatlayalım. Öncelikle eşitliğin iki tarafını da n cinsinden yazmaya çalışalım.
1 + 3 + 5 + … + 2n – 1 = n2
şeklinde yazılabilir çünkü tek sayıları veren formül olarak 2n-1 terimini kullanabiliriz. Toplam (summation) olarak gösterecek olursak aşağıdaki gösterim de doğrudur:
Matematiksel tümevarım için önce başlangıç adımı (basis step) belirlenir. Bu durumda pozitif tam sayılar kastedildiği için n=1 alabiliriz.
2 1 – 1 = 12 olarak bu başlangıç adımı doğrulunu ispatlamış oluruz. Bu adım önemlidir çünkü birazdan ispatımızı üzerine oturtacağımız serinin bir yerde durması gerekir ve durduğu nokta bu başlangıç noktasıdır.
Ardından istikra adımı (inductive step) gelmektedir. Bu adım için eşitliğin iki tarafı da birer terim ilerletilir:
1 + 3 + 5 + … + 2n-1 + 2n+1= (n+1)2
Yukarıdaki haliyle denklemimizin doğruluğunu ispatlamaya çalışalım. Eşitliğin sol tarafındaki 1 + 3 + 5 + … + 2n-1 ifadesinin daha önceden n2 olduğunu biliyorduk (iddia ediyorduk) öyleyse denklemi aşağıdaki hale getirebiliriz:
n2 + 2n+1= (n+1)2
Yukarıdaki yeni denklemi aritmetik olarak çözerek ispatımıza devam edelim:
n2 + 2n+1=n2 + 2n+1
0=0
totolojisine ulaşılmış olur ve eşitlik doğrudur.
Yukarıdaki örnekte görüleceği üzere herhangi bir n sayısı için eşitlik ispatlanmıştır. Buradaki önemli nokta n sayısı ile n+1 sayısı arasındaki geçiştir. Yani n sayısı hangi sayı olursa olsun (başlangıç adımımızdaki 1′den büyük ve sonsuza kadar herhangi bir sayı) eşitlik doğru çalışır.
SORU-53: Özyineli Diller (Recursive Languages)
Özyineli diller matematik, mantık veya bilgisayar bilimlerinde geçen muntazam dillerden (formal language) birisidir. Genellikle kararverilebilir yani Turing makinesi (Turing machine) tarafından işlenebilir diller olarak kabul edilirler.
Özyineli diller Chomsky hiyerarşisinde yer almamaktadır.
Bir özyineli dili tanımlamak için iki önemli tanım yapılır. Birincisi dilin içerdiği alfabeden üretilebilen güç kümesinin (power set) özyineli alt kümesi (recursive subset) olmasıdır.
İkincisi ise Turing makinesi tarafından kabul edilebilir bir muntazam dil (formal language) olmasıdır. Yani Turing makinesi bu dili kabul edecek ve bu dil dışındaki durumlarda duracaktır (halting).
Bütün özyineli diller aynı zamanda özyineli sayılabilirdirler. Bütün CFG veya düzenli ifadeler özyineli dil olarak kabul edilebilir.
SORU-54: Karar Problemi (Decision Problem)
Bilgisayar bilimlerinin de içinde bulunduğu pek çok bilim ve mühendislik dalını yakından ilgilendiren hesaplanabilirlik teorisi (computability theory) konusundaki problemlerden birisidir.
Problemi basitçe tanımlama gerekirse bir koşulun (ki biz buna karar ismini vereceğiz) sağlanıp sağlanamadığını evet-hayır şeklinde ikili olarak (duality) sorgulamaktır.
Örneğin x gibi bir sayının ikiye tam bölünüp bölünememesi bir karar problemidir.
Bir karar probleminin sonucunun bulunup bulunamayacağı belirliyse bu tip problemlere kararı mümkün problemler (kararlaştırılabilir problemler, decidable problems) denilmektedir. Örneğin bir sayının ikiye bölümünden kalanın 0 olup olmaması bu tipten bir problemdir. Ancak bazı problemler bu gruba girmez. Örneğin pi sayısının noktadan sonraki bütün hanelerinin bulunamsı gibi. Bu problemin çözülüp çözülemeyeceği belirsizdir. Dolayısıyla problem kararı mümkün problem sınıfından değildir.
Bilgisayar bilimleri mantığı ile konuya yaklaşacak olursak bir problemin algoritmik olarak gösterilmesi veya göterilememesi karar probleminin mümkün olup olmamasını belirlemektedir.
Örneğin durma problemi (halting problem) bu anlamda önemli kararı mümkün olmayan problemlerdendir (undecidable problems).
Bu anlamda kararı mümkün problemleri üç grupta incelemek mümkündür:
kararı mümkün problemler (decidable problems): Şayet algoritma bir özyineli küme (recursive set) ise çözümü mümkün problemdir
Kararı kısmen mümkün problemler (semidecidable, partially decidable, provable, solvable) : Şayet algoritma özyineli sayılabilir küme ise (recursively enumarable set) bu durumda algoritmaya çözümü kısmen mümkün problem ismi verilir
Kararı mümkün olmayan problemler (undecidable problems): kararı kısmen mümkün problemler de dahil olmak üzere şayet bir algoritma özyineli bir küme (recurise set) üretemiyorsa bu gruba dahil edilir.
SORU-55: Turing Makinesi (Turing Machine)
Bilgisayar bilimlerinin önemli bir kısmını oluşturan otomatlar (Automata) ve Algoritma Analizi (Algorithm analysis) çalıştırmalarının altındaki dil bilimin en temel taşlarından birisidir.1936 yılında Alan Turing tarafından ortaya atılan makine tasarımı günümüzde pekçok teori ve standardın belirlenmesinde önemli rol oynar.
Turing Makinesinin Tanımı
Basitçe bir kafadan (head) ve bir de teyp bandından (tape) oluşan bir makinedir.
Makinede yapılabilecek işlemler
- Yazmak
- Okumak
- Bandı ileri sarmak
- Bandı geri sarmak
şeklinde sıralanabilir.
Chomsky hiyerarşisi ve Turing Makinesi
Bütün teori bu basit dört işlem üzerine kurulmuştur ve sadece yukarıdaki bu işlemleri kullanarak bir işin yapılıp yapılamayacağı veya bir dilin bu basit 4 işleme indirgenip indirgenemeyeceğine göre diller ve işlemler tasnif edilmiştir.
Bu sınıflandırma yukarıdaki venn şeması ile gösterilmiştir. Aynı zamanda chomsky hiyerarşisi (chomsky hierarchy) için 1. seviye (type-1) olan ve Turing makinesi ile kabul edilebilen diller bütün tip-2 ve tip-3 dilleri yani içerk bağımsız dilleri ve düzenli dilleri kapsamaktadır. Ayrıca ilave olarak içerik bağımsız dillerin işleyemediği (üretemediği veya parçalayamadığı (parse) ) anbncn şeklindeki kelimeleri de işleyebilmektedir. Düzenli ifadelerin işleyememesi konusunda bilgi için düzenli ifadelerde pompalama savı (pumping lemma in regular expressions) ve içerik bağımsız dillerin işlemeyemesi için de içerik bağımsız dillerde pompalama savı (pumping lemma for CFG) başlıklı yazıları okuyabilirsiniz.
Turing Makinesinin Akademik Tanımı
Turing makineleri literatürde akademik olarak aşağıdaki şekilde tanımlanır:
Burada M ile gösterilen makinenin parçaları aşağıda listelenmiştir:
Q sembolü sonlu sayıdaki durumların kümesidir. Yani makinenin işleme sırasında aldığı durumardır.
Γ sembolü dilde bulunan bütün harfleri içeren alfabeyi gösterir. Örneğin ikilik tabandaki sayılar ile işlem yapılıyorsa {0,1} şeklinde kabul edilir.
Σ sembolü ile makineye verilecek girdiler (input) kümesi gösterilir. Girdi kümesi dildeki harfler dışında bir sembol taşıyamayacağı için Σ ⊆ Γ demek doğru olur.
δ sembolü dilde bulunan ve makinenin çalışması sırasında kullanacağı geçişleri (transitions) tutmaktadır.
◊ sembolü teyp bandı üzerindeki boşlukları ifade etmektedir. Yani teyp üzerinde hiçbir bilgi yokken bu sembol okunur.
q0 sembolü makinenin başlangıç durumunu (state) tutmaktadır ve dolayısıyla q0 ⊆ Q olmak zorundadır.
F sembolü makinenin bitiş durumunu (state) tutmaktadır ve yine F ⊆ Q olmak zorundadır.
Örnek Turing Makinesi
Yukarıdaki sembolleri kullanarak örnek bir Turing makinesini aşağıdaki şekilde inşa edebiliriz.
Örneğin basit bir kelime olan a* düzenli ifadesini (regular expression) Turing makinesi ile gösterelim ve bize verilen aaa şeklindeki 3 a yı makinemizin kabul edip etmediğine bakalım.
Tanım itibariyle makinemizi aşağıdaki şekilde tanımlayalım:
M = { {q0,q1} , { a } , { a,x } , { q0 a→a R q0 , q0 x→x L q1} , q0 , x , q1 }
Yukarıdaki bu makineyi yorumlayacak olursak:
Q değeri olarak {q0,q1} verilmiştir. Yani makinemizin ik idurumu olacaktır.
Γ değeri olarak { a,x } verilmiştir. Yani makinemizdeki kullanılan semboller a ve x’ten ibarettir.
Σ değeri olara {a} verilmiştir. Yani makinemize sadece a girdisi kabul edilmektedir.
δ değeri olarak iki geçiş verilmiştir { q0 a→a R q0 , q0 x→x L q1} buraadki R sağa sarma L ise sola sarmadır ve görüleceği üzere Q değerindeki durumlar arasındaki geçişleri tutmaktadır.
◊ değeri olarak x sembolü verilmiştir. Buradan x sembolünün aslında boş sembolü olduğu ve bantta hiçbir değer yokken okunan değer olduğu anlaşılmaktadır.
q0 ile makinenin başlangıç durumundaki hali belirtilmiştir.
F değeri olarak q1 değeri verilmiştir. Demek ki makinemiz q1 durumuna geldiğinde bitmektedir (halt) ve bu duruma gelmesi halinde bu duruma kadar olan girdileri kabul etmiş olur.
Yukarıdaki bu tanımı görsel olarak göstermek de mümkündür:
Yukarıdaki bu temsili resimde verilen turing makinesi çizilmiştir.
Makinemizin örnek çalışmasını ve bant durumunu adım adım inceleyelim.
Birinci adımda bandımızda aaa (3 adet a) yazılı olduğunu kabul edelim ve makinemizin bu aaa değerini kabul edip etmeyeceğini adım adım görelim. Zaten istediğimiz de aaa değerini kabul eden bir makine yapabilmekti.
Yukarıdaki ilk durumda bant üzerinde beklenen ve kabul edilip edilmeyeceği merak edilen değerimiz bulunuyor. Makinemizin kafasının okuduğu değer a sembolü. Makinemizin geçiş tasarımına göre q0 halinde başlıyoruz ve a geldiğinde teybi sağa sarıp yine q0 durumunda kalmamız gerekiyor.
Yeni durumda kafamızın okuduğu değer banttaki 2. a harfi ve bu durumda yine q0 durumundayken teybi sağa sarıp yine q0 durumunda kalmamız tasarlanmış
- durumda kafamızın okuduğu değer yine a sembolü olmakta ve daha önceki 2 duruma benzer şekilde q0 durumundayken a sembolü okumanın sonucu olarak teybi sağa sarıp q0 durumunda sabit kalıyoruz.
- adımda teypten okuduğumuz değer boşluk sembolü x oluyor. Bu değer makinemizin tasarımında q1 durumuna gitmemiz olarak tasarlanmış ve teybe sola sarma emri veriyoruz.
Makinenin son durumunda q1 durumu makinenin kabul ve bitiş durumu olarak tasarlanmıştı ( makinenin tasarımındaki F kümesi) dolayısıyla çalışmamız burada sonlanmış ve giriş olarak aaa girdisini kabul etmiş oluyoruz.
- Örnek
Hasan Bey’in sorusu üzerine bir örnek makine daha ekleme ihtiyacı zuhur etti. Makinemiz {a,b} sembolleri için çalışsın ve ilk durum olarak bandın en solunda başlayarak bantta bulunan sembolleri silmek için tasarlansın. Bu tasarımı aşağıdaki temsili resimde görülen otomat ile yapabiliriz:
Görüldüğü üzere makinemizde 4 durum bulunuyor, bunlardan en sağda olan h durumu bitişi (halt) temsil ediyor. Şimdi bu makinenin bir misal olarak “aabb” yazılı bir bantta silme işlemini nasıl yaptığını adım adım izah etmeye çalışalım.
Aşağıda, makinenin her adımda nasıl davranacağı bant üzerinde gösterilmiş ve altında açıklanmıştır. Sarı renge boyalı olan kutular, kafanın o anda üzerinde durduğu bant konumunu temsil etmektedir.
Netice olarak Hasan Bey’in sorusuna temel teşkil eden ve örneğin q1 üzerindeki döngülerden birisi olan b/a,R geçişi, banttan b okunduğunda banta a değerini yaz manasındadır.
Yine bu yazıya yapılan yorumlarda sorulan bir sorunun cevabını aşağıdaki bağlantıda çözdüm. Soru, eşit a ve b içeren Turing makinesinin tasarımı idi, bağlantıya tıklayarak okuyabilirsiniz.
SORU-56: Özyineli sayılabilir küme (Recursively Enumerable Sets)
Hesaplanabilirlik teorisine (Computability Theory) bir sayı kümesi elemanlarının tamamının bir algoritma için çalışıp son bulma şartını sağlıyorsa özyineli sayılabilir küme olarak sınıflandırılır.
Daha basit bir anlatımla kümede bulunan bütün elemanlar bir algoritma için, o algoritmanın bitmesini sağlayacak elemanlar olmalıdır.
Daha akademik bir tanımla bir özyineli hesaplanabilir fonksiyon (Recursively Computable Function) için etki alanı olarak S: s1, s2, s3, … kümesi tanımlanıyorsa bu kümedeki bütün elemanlar s1, s2, s3, …için fonksiyona girdi (argument, parameter) olma imkanı bulunmalıdır.
Tanım itibariyle özyineli kümenin (recursive set) alt kümesi kabul edilen bu kümenin farkı kümenin elemanı olmayan durumlarda sonucun kestirilememesidir.
Yani özyineli kümenin tanımının hatırlayacak olursak:
f(si) = 1 si ∈ S
f(si) = 0 si ∉ S
şeklinde gösterilen ve S kümesinin her elemanı olan si için 1 veya 0 şeklinde bir sonuç çıkaran kümeydi. Özyineli sayılabilir küme ise
f(si) = 0 si ∈ S
f(si) = belirsiz / hesaplanamaz si ∉ S
şeklinde gösterilen bir fonksiyon olmalıdır.
SORU-57: Hesaplanabilir Fonksiyon (Computable Function)
Hesaplanabilirlik teorisinin (Computability Theory) temel taşlarından birisi olan özel bir fonksiyon (function) tipidir. Bu fonksiyonların özelliği herhangi bir formal dilbilgisi (formal grammer) yardımıyla açıklanmayan fonksiyonlar olmalarıdır.
Genellikle karıştırıldıkları için karmaşıklık teorisi (complexity theory) ile hesaplanabilirlik teorisi (computability theory) arasındaki farkı bu fonksiyonlar üzerinde de vurguluamakta yarar vardır. Basitçe bir fonksiyonun hesaplanabilir olması o fonksiyonun bir sonucunun çıkması anlamına gelir.
Örneğin Church-Turing testine göre bir fonksiyonun hesaplanabilir olması bu fonksiyonun bir makine ile (veya elektronik devre ile veya bilgisayar programı ile) modellenebiliyor olmasını ve sınırsız zaman ve depolama imkanı (storage) verildiğinde bir şekilde biteceği anlamına gelir.
Bu bağlamda bir fonksiyonun hesaplanabilir olması en verimli şekilde hesaplanması (efficiency) veya en kısa zamanda veya en kısa depolama ihtiyacıyla çalışıp bitmesi anlamından farklıdır. Bu tip kaygılar daha çok karmaşıklık teorisi (complexity theory) konusunun çalışma alanına girmektedir.
Hesaplanabilir fonksiyonları basitçe bir parametre havuzundaki elemanlar için sonuç döndüren fonksiyonlar olarak düşünebiliriz. Örneğin f : A1 x A2 x … x An → B şeklinde tanımlı bir fonksiyon alalım. Bu durumda f(a1, a2, …, an) = b olarak gösterilebilir ve ai ∈ Ai, i = 1, …, n ve b ∈ B’dir denilebilir. Klasik bir fonksiyonu bu şekilde tanımladıktan sonra hesaplanabilir fonksiyonu bu fonksiyonda sonuç bulan anlamında ↓ sembolü ile gösterek
f(a1, a2, …, an)↓ = b ile gösterilen fonksiyonun a1, a2, …, an argümanları için b sonucunu veren fonksiyon olduğunu söyleyebiliriz.
Örneğin bir dilin hesaplanabilir olması bu dildeki bütün kelimeler için doğru ve bu dilden olmayan bütün kelimeler için de yanlış sonucunu veren bir fonksiyon bulunabilmesine bağlıdır. Bu fonksiyona da hesaplanabilir fonksiyon ismi verilir.
Örneğin bir düzenli ifade (regular expression) ile gösterilen dildeki bütün üretilebilen kelimeler için doğru sonucu veren fonksiyon ve üretilemeyen bütün kelimeler için yanlış sonucu veren fonksiyon bu tip bir fonksiyondur.
Bu tanımı biraz daha genişleterek bir A kümesi ve bir bu küme üzerindeki bir f fonksiyonu için Turing makinesi (turing machine) üretilebiliyorsa (ya da bir kahin makinesi (oracle) ) hesaplanabilir bir kümemiz ve hesaplanabilir bir fonksiyonumuz bulunuyor demektir. Bu duruma A-hesaplanabilir (A-computable) veya f-hesaplanabilir (f-computable) isimleri de verilir.
SORU-58: Özyineli Küme (Recursive Set)
Hesaplanabilirlik teorisine (Computability Theory) göre bir doğal sayılardan oluşan bir kümedeki bütün elemanlar bir algoritmanın belirli bir zaman sonra sona ermesini sağlıyorsa bu kümeye özyineli küme ismi verilir. Şayet kümenin elemanlarından bir veya daha fazlası algoritmanın belirli bir zamanda bitmesini sağlamıyorsa bu kümeye hesaplanamaz (noncomputable) veya karar verilemez (undecidable) ismi verilir.
Özyineli küme terimi yerine hesaplanabilir küme (computable set) veya karar verilebilir küme (decidable set) terimleri de kullanılır.
Daha akademik bir tanımla şayet hesaplanabilir bir fonksiyonun aldığı parametereleri tutan bir S kümesi varsa ve bu fonksiyon kümedeki elemanlar için doğru ve kümede olmayan elemanlar için yanlış sonuç üretiyorsa (C dilinde 0 ve 1 olarak kabul edilebilir) bu kümeye özyineli küme (recursive set) ismi verilir.
f(si) = 1 si ∈ S
f(si) = 0 si ∉ S
şartlarını sağlıyorsa özyineli küme ismi verilir.
Yukarıdaki tanma göre boş küme, bütün doğal sayılar kümesi veya asal sayılar kümesi gibi kümeler özyineli küme olarak kabul edilebilirler.
Örnek Soru:
S isminde bir küme aşağıdaki şekilde tanımlanıyor olsun:
3 ∈ S
x ∈ S ve y ∈ S ise x + y ∈ S
Yukarıdaki S kümesinin 3′e tam bölünebilen pozitif tam sayılar kümesi olduğunu gösteriniz.
Çözüm:
Yukarıda tanımı yapılan kümenin 3′e tam bölünebilen pozitif tam sayı kümesi olduğunu göstermek için bu tanıma A kümesi diyelim. Yani S yukarıda tanımlanan küme ve A da olduğunu göstermemiz istenen küme olsun.
Bu durumda A = S olduğunu göstermemiz gerekiyor. Bunu göstermenin bir yolu daaynı anda
hem A ⊆ S hem de S ⊆ A olduğunu göstermemizdir. Yani şayet A, S’in alt kümesi ve aynı anda S de A’nın alt kümesiyse bu iki küme eşittir denilebilir.
Önce A’nın S’in alt kümesi olduğunu ispatlayalım. Bunun için matematiksel tümevarım teoreminden (mathematical induction principle) istifade edeceğiz.
başlangıç adımımız (basis step) : 3 olarak kabul edersek
istikra adımı olarak P(n) gibi bir fonksiyon tanımlayalım. P(n) sayının 3′e bölünebilme özelliğini kullanan fonksiyon olsun dolayısıyla
P(n) = 3n olsun. Görüldüğü üzere P(n) fonksiyonu her zaman 3′e tam bölünebilen sayılar üretir.
S kümesindeki herhangi bir k sayısını alalım. Bilindiği üzere k sayısı 3′e bölünebilen sayıdır (iddia gereği)
Dolayısıyla k = 3n şeklinde yazılabilir.
İstikra gereği (induction) bu serideki bir sonraki sayıyı bulmak için S kümesindeki en küçük eleman olan 3 ile sayımızı topluyoruz.
Öyleyse k+3 yada 3n+3 sayısı serideki k’dan sonra gelen sayı olur.
Bu gösterimi A kümesi için yazacak olsaydık
P(n) = 3n olacaktı ve bir sonraki sayı P(n+1) = 3n +3 olacaktı.
Dolayısıyla görülebileceği üzere
P(n) = k
P(n+1) = k+3
eşitlilklerinin ikisi de , yani isitkra (tümevarım, induction) adımlarımızın ikisi de sağlanmış oluyor.
Şimdi ispatın ikinci kısmına geçelim ve S’in A’nın alt kümesi olduğunu gösterelim. Bunun için S’in özyineli (recursive) tanımından faydalanabiliriz.
Öncelikle başlangıç adımı olan 3, S kümesinin elemanıdır. Ayrıca 3 = 3×1 şeklinde yazılabilir. Buradan S kümesindeki bütün elemanların 3xn şeklinde yazılabileceğini söyleyebiliriz. Bunu göstermek için S kümesinin tanımındaki ikinci parçayı yani x+y’nin A’nın bir üyesi olduğunu göstermeliyiz.
Şayet 3|x ve 3|y ise (yani x ve y ayrı ayrı 3′e tam olarak bölünebiliyorsa) o zaman 3|x+y’dir denilebilir (x+y, 3′e tam bölünebilir)
Yukarıdaki bu bölünebilirlik iddiasını ispatlamak için
3| x ve 3|y durumunda
x = 3s ve y = 3t olarak yazılabilir. Buradaki s ve t değerleri 3 sayısının herhangi bir çarpanıdır.
Dolayısıyla x + y = 3s + 3t olarak yazılabilir ve buradan 3(s+t) sonucuna ulaşılır ki s+t değeri her ne olursa olsun 3(s+t) değeri 3′e tam bölünebilir.
Yukarıdaki iki ispat sonucunda A=S olduğu söylenebilir ve özyineli kümenin bu özelliğinden faydalanarak ispat yapılmıştır.
SORU-59: Hesaplanabilirlik Teorisi (Computability Theory)
Bilgisayar bilimleri ve matematik açısından bir problemin sonucunun bulunup bulunamayacağı veya bir problemin sonucunun hesaplanabilir oldup olmadığı ile ilgilenen çalışma alanıdır.
Karmaşıklık teorisi ile sıkça karıştırıldığı için aralarındaki farkı söyleyerek başlamakta yarar var. Karmaşıklık teorisi (complexity theory) bir problemin çözümünün ne kadar karmaşık olduğunu ve ne kadar zaman ve yer istediği gibi konularla uğraşırken hesaplanabilirlik teorisi (computability theory) bir problemin çözümü olup olmadığı ile ilgilenir.
Bu anlamda örneğin durma problemi (halting problem) bu alandaki problemlerden sayılabilir.
SORU-60: Sıfır Bilgi İspatı (Zero-Knowledge proof)
Bilgisayar bilimlerinin bir çalışma alanı olan veri güvenliği konusunda kullanılan bir algoritmadır. Sıfır bilgi protokolü (zero knowledge protocol) ismi de verilir. Algoritmanın çözmeye çalıştığı problem aslında oldukça eski ve sık rastlanır bir problemdir.
“Bir bilgiyi bildiğimi, karşı tarafa bu bilgiyi vermeden nasıl ispat edebilirim?”
şeklindeki bir soruya cevap arar. Yani örneğin elimizde bir şekilde (graph) bulunan asgari tarama ağacı (minimum spanning tree) bilgisi olsun. Bu bilgiyi bildiğimizi ispat etmek istiyoruz ancak karşı tarafa da bu bilgiyi vermek istemiyoruz (Örneğin bu bilgiyi için ödeme alacağımız bir durum olabilir). Karşı taraf da kesin olarak bu bilgiyi bildiğimizden emin olmak istiyor (örneğin ödeme yapacak olsun). Bu durumda nasıl ispat yapılır?
Mağra Örneği
Problemin basit olarak anlaşılması için kullanılan mağra örneğinden yola çıkalım. A ve B kişilerinden A kişisi mağranın iki tarafını bağlayan kapının sihirli sözünü biliyor olsun (örneğin açıl susam açıl) ve bu bilgiyi B kişisine satmak istiyor olsun.
Yukarıdaki şekilde düşünülen mağra problemine göre A kişisi mağranın sağ veya sol tarafından girip diğer taraftan çıkabilmelidir.
Problemin çözümünde A kişisi mağraya girer ve kapının yanına geçer. Ardından B kişisi mağranın girişine girerek A kişisinin hangi yönden gelmesini istediğini bağırır. Şayet A kişisi doğru kelimeyi biliyorsa ve ters taraftaysa kapıyı açıp gelir. Şayet zaten istenen taraftaysa kapıyı açmasına ihtiyaç yoktur ve geri dönüp gelir.
Yukarıdaki örnekten de görüleceği üzere B kişisi %50 ihtimalle A kişisinin gizli kelimeyi bildiğinden emin olabilmektedir. Bu deney tekrarlanarak ihtimal arttırılabilir. Ancak hiçbir zaman %100 oranında emin olunması söz konusu olamaz (bilginin tamamı verilmeden).
Hamilton Döngüsü örneği
Sıfır bilgi ispatına konu olan ve üzerine düşünülen diğer bir problem de hamilton döngüsü (Hamiltonian Cycle) bilgisidir. Örneğin B kişisi şekli (graph) biliyor ancak bu şekil üzerindeki hamilton döngüsünü bilmiyor olsun. Bu bilgiyi bilen ve satmak isteyen A kişisi bu bilgiyi bildiğini nasıl ispat edebilir.
Sorunun çözümü için denkşekilli (isomoprhic , izomorfik) bir graf A kişisi tarafından oluşturulup bu şekil üzerinde bir hamilton döngüsü işaretlenir. (Tabi bu bilginin tamamı B kişisinden gizli tutuluyor)
Ardından B kişisi iki bilgiden birisini talep eder. Ya denkşekilli (isomorphic) olduğunun gösterilmesini ya da bu denkşekilli üzerinde hamilton döngüsünün gösterilmesini isteyebilir.
Sonuç
Kısacası A kişisi bilgiyi iki parçaya böler. Bu parçalar bir diğeri olmadan işe yaramaz ancak ikisi olduğunda bilginin bilindiğini ispatlayan parçalardır. Bilginin ispatlanacağı B kişisi bu bilgilerden birisini talep eder. A kişisi bu bilgilerden birisini verir ve şayet doğruysa %50 itimalle bildiğini ispatlamış olur.
Diğer bir deyişle şayet bilgiyi gerçekten bilmiyorsa %50 ihtimalle yanlış sonuç verir.
Yukarıdaki örnek bütün NP-Complete problemlere uygulanabilir.
SORU-61: Laplas Matrisi (Laplacian Matrix)
Bilgisayar bilimlerinin de içinde bulunduğu pekçok bilim ve mühendislik alanında kullanılan graf teorisi (graph theory) açısından önemli bir matristir. Laplas matrisinin özelliği her düğümün derecesini (node order) ve diğer düğümlerle olan komşuluk ilişkisini (adjacency list) tutmasıdır.
Laplas matrisine giriş matrisi (admittance matrix) veya Kirchhoff matirisi ( Kirchhoff matrix ) isimleri de verilmektedir. Kirchhoff teorsi ( Kirchhoff theory ) ile birlikte kullanılınca bir graftaki birbirinden farklı asgari tarama ağacı (minimum spanning tree) sayısını elde etmekte kullanılabilir.
Laplas matrisinin tanımı
Laplas matrisi bir grafta bulunan düğüm sayısı kadar boyutlara sahip kare matristir. Düğüm sayısı n olan bir graf için nxn boyutlarında bir matris aşağıdaki şartlara göre üretilir.
i=j -> düğümün derecesi (node order)
i≠j -> i. düğümün j. düğümle olan komşuluğu (komşuluk varsa -1 yoksa 0)
Yukarıdaki kurallardan anlaşılacağı üzere matrisin köşegeninde (diagon) düğüm dereceleri ve matrisin geri kalanında ise -1 ve 0′dan oluşan komşuluk matrisi bulunacaktır.
Laplas matrisine örnek
Örneğin aşağıdaki grafın laplas matrisini oluşturmak isteyelim:
Yukarıdaki şekilde 11 düğüm bulunmaktadır. Bu durumda 11×11 boyutlarında bir matris (masfuf) oluşturarak tanımımızda verilen kurallara uygun bir şekilde matrisin değerlerini dolduralım:
|
A |
B |
C |
D |
E |
F |
G |
H |
I |
J |
K |
|
|
A |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-1 |
|
B |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-1 |
|
C |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-1 |
|
D |
0 |
0 |
0 |
1 |
0 |
-1 |
0 |
0 |
0 |
0 |
0 |
|
E |
0 |
0 |
0 |
0 |
1 |
-1 |
0 |
0 |
0 |
0 |
0 |
|
F |
0 |
0 |
0 |
-1 |
-1 |
5 |
0 |
0 |
-1 |
0 |
-1 |
|
G |
0 |
0 |
0 |
0 |
0 |
-1 |
2 |
-1 |
0 |
0 |
0 |
|
H |
0 |
0 |
0 |
0 |
0 |
0 |
-1 |
1 |
0 |
0 |
0 |
|
I |
0 |
0 |
0 |
0 |
0 |
-1 |
0 |
0 |
2 |
-1 |
0 |
|
J |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-1 |
1 |
0 |
|
K |
-1 |
-1 |
-1 |
0 |
0 |
-1 |
0 |
0 |
0 |
0 |
4 |
Yukarıdaki tablodan açıkça görülebileceği üzere laplas matrisinin bir özelliğide satır bazında ve sütün bazında toplamların 0 olmasıdır.
Laplas matrisinin özellikleri
G ismindeki bir graf ve L ismindeki bir Laplas matrisinin λ0 ≤ λ1 ≤ λ2 ≤ … ≤ λn-1 koşullarını sağlayan özdeğerleri (eigen values) için aşağıaki özellikler sayılabilir:
Şayet laplas matrisinin bütün özdeğerleri (eigenvalues) sıfırdan büyükse bu matris için Hermitian matris denilebilir
λ0 her zaman 0′dır.
λ1 matematiksel bağlılığı (algebraic connectivity) gösterir
Özdeğerlerin 0 olma sayısı G grafındaki bağlı düğüm sayısını verir.
SORU-62: Denkşekillilik (Isomorphism)
Bilgisayar bilimleri de dahil olmak üzere pek çok bilim ve mühendislik alanında kullanılan graf teorisine (graph theory) göre iki şekil birbirinden farklı çizilmiş ancak işlev ve değer olarak aynı olabilir.
Tanım ve örnek
Örneğin aşağıdaki iki şekli ele alalım:
Yukarıda verilen graftaki düğümler (nodes) ik ifarklı küme oluşturmak için aşağıdaki şekilde yerlerinden hareket ettirilmiş olsun:
Yukarıdaki bu yeni şekil ve ilk şekil arasında denklik söz konusudur. Buradaki denklik komşuluk anlamında düşünülebilir. Yani iki şekilde de bütün düğümlerin komşuları aynıdır. Ancak fark edileceği üzere iki şekildeki elamanların yerleri değiştirilmiştir.
Bu durumda yukarıdaki iki şekil için denkşekilli (isomorphic, izomorfik) denilebilir.
Yukarıdaki tanım için aynı zamanda kenar korumalı (edge preserving) birebir ve örten dönüşüm (bijection) terimi kullanılabilir.
Denkeşliliğin özellikleri
Şayet iki graf denkeşli ise bu grafların birisndeki döngü (cycle) sayısı diğerine eşittir.
Şayet iki graf denkeşli ise bu graflardan birisindeki toplam düğüm dereceleri (node order) diğerine eşittir.
Denkeşlilik Problemi (Isomorphism problem)
Denkeşlilik ile ilgili önemli bir problem iki grafın birbirine denk olduğunun tespitinde yaşanır. Elimizde iki farklı graf olduğunu ve bunların denkeşli (isomoprhic) olup olmadığını öğrenmek istediğimizi düşünelim.
Herşeyden önce bu problemi ilginç yapan, problemin polinom zamanda çözülüp çözülemeyeceğidir. Bu problem bu anlamda ilginçtir çünkü problemi ne NP ne de P kümesine ait değildir denilebilir.
Aslında bu tartışma daha derinlerde NP ⊃ P iddiasının doğruluğuna dayanmaktadır. Yani şayet NP, P nin üst kümesiyse (superset) bu durumda ikisi arasında başka bir küme olamaz. Ancak bazı araştırmacılar (örneğin Uwe Schöning) bu iki küme arasında alt ve üst hiyerarşik seviyeler bulunduğunu iddia etmişler ve denkşekillik gibi bazı problemleri bu kümeye dahil etmişlerdir.
İddiaya göre P altta ve NP üstte iki seviye olarak düşünülürse düşük hiyerarşik seviyedeki problemler P’ye ve yüksek hiyerarşik seviyedeki problemler NP’ye daha yakın kabul edilecektir.
Bütün bu iddiaların sebebi denkşekillik gibi problemlerin karmaşıklığının (complexity) hala ispatlanamamış olmasıdır.
Bu problemin farklı bir halinin karmaşıklığı bulunabilmektedir. Örneğin problemimizi biraz değiştirerek alt grafların (subgraphs) denkşekilliğini sorgulayacak olursak bu durumda problemimiz NP-Complete olarak sınıflandırılabilir.
SORU-63: Öyler Yolu (Eulerian Path)
Bilgisayar mühendisliği de dahil olmak üzere pekçok bilim ve mühendislik alanında kullanılan graf teorisindeki özel bir yol (path) şeklidir. Bu yolun özelliği her kenardan (edge) bir kere (en az ve en çok) geçen yolu bulmaktır.
İçerik
Teorinin tarihi çıkışı
Teorinin tanımı
Öyler yollarının özellikleri
Bir graftaki farklı öyler yollarının sayısı
İlk bakışta hamilton yolu (hamiltonian path) ile karıştırılabilir ancak Hamilton yolundaki amaç her düğümden (node) bir kere geçen yolu bulmak iken Öyler yolunda her kenardan (edge) bir kere geçen yolu bulmak amaçlanır.
Öyler teorisinin tarihi çıkışı
Öylerin teorisini ortaya atmasında önemli rol oynayan tarihi problem Königsberg köprüsü problemidir.
Yukarıdaki şekilde pregel nehri etrafında kurulu (C ve B karaları) ve nehrin ortasında iki adası olan (A ve D adacıkları) kösigner şehrinin yukarıda görülen 7 köprüsü bulunmaktadır.
Problem bütün köprülerden bir kere geçilen bir yol olup olmayacağıdır.
Öyler bu soruyla uğraşırken yazımızın konusu olan öyler yolu teorisini bulmuştur ve cevap olarak böyle bir yolun bulunamayacağını istaplamıştır.
Öylerin iddiası bastir bir keşfe dayanmaktaydı. Şayet bir düğüme (node) bir kenar (edge) ile geliniyorsa bu düğümü terk etmek için farklı bir yola ihtiyaç duyulur.
Bu durumda her düğümün derecesini (node order) hesaplayan Öyler, bir düğüme giren çıkan yolların sayısına düğüm derecesi (node order) ismini verdi.
Buna göre şayet bir düğümün derecesi tekse, bu düğüm ya başlangıç ya da bitiş düğümü olmalıdır. Bunun dışındaki durumlarda (yol (path) üzerindeki herhangi bir düğüm olması durumunda) tek sayıdaki yolun sonuncusu ziyaret edilmiş olamaz.
Yukarıdaki şekilde köprü örneğinin graf ile gösterilmiş hali görülüyor. Burada dikkat edilirse her dört düğüm de tek sayıda dereceye sahiptir.
A 5
B C D ise 3
derecesine sahiptir. Bu durumda düğümlerden iki tanesi başlangıç ve bitiş olsa diğer iki tanesini birleştiren yollar kullanılamayacak ve bütün kenarlar gezilmiş olamayacaktır.
Öyler yolunun Tanımı
Öyler yolu (eulerian path) tam olarak şu şekilde tanımlanabilir:
Bir yönsüz grafta (undirected graph) şayet bütün düğümleri (nodes) dolaşan bir yol bulunabiliyorsa bu yola Öyler yolu( Eulerian Path, Eulerian Trail, Eulerian Walk) ismi verilir. Bu yolun bulunduğu grafa ise yarı öyler (semi-eulerian) veya dolaşılabilir (traversable) graf ismi verilir.
Şayet bu yolun başlangıç ve bitiş düğümleri (node) aynıysa bu durumda tam bir döngü (cycle) elde edilebiliyor demektir ve bulunan bu yola öyler döngüsü (eulerian cycle, eulerian circuit veya eulerian tour) ismi verilir. Bu yolu içeren grafa ise öyler grafı (eulerian graph veya unicursal) ismi verilir.
Yukarıdaki tanımı yönlü graflar (directed graphs) için de yapmak mümkündür. Ancak bu durumda yukarıdaki tanımda geçen yolları, yönlü yollar ve döngüleri, yönlü döngüler olarak değiştirmek gerekir.
Öyler yolunun özellikleri
- Bir yönsüz bağlı grafın bütün düğümlerinin derecesi çiftse bu graf öyler grafıdır (eulerian) [amcak ve ancak]
- Bir yönlü graf (directed graf) ancak ve ancak bütün düğümlerin giren ve çıkan derecelerinin toplamları eşitse öyler grafı (eulerian) olabilir.
- Bir yönsüz graf’ın öyler yolu bulunabilmesi için iki veya sıfır sayıda tek düğüm derecesine sahip üyesi olmalıdır.
Öyler Döngülerinin sayısı
Bir grafta öyler döngüleri bulunuyorsa, birden fazla olabilir. Yani birbirinden farklı döngüler elde edilebilir. Burada fark oluşturan faktör başlangıç ve bitiş düğümleridir.
Örneğin aşağıdaki şekil için
A-B-E-A-C-D-A döngüsü bir öyler döngüsüdür. Benzer şekilde
E-B-A-C-D-A-E döngüsü de bir öyler döngüsüdür.
Buradaki soru acaba bir grafta kaç farklı öyler döngüsü olabilir?
Bu soruya cevap BEST teoremi ismi verilen ve teoremi bulan kişilerin isimlerinin baş harflerinden oluşsan teorem ile verilir. BEST teoremine göre bir grafta bulunan öyler döngülerinin sayısı graftaki bütün düğümlerin derecelerinin bire eksiğinin faktöriyellerinin çarpımına eşittir.
∏ ( d(v)-1) !
olarak gösterilebilecek teoriye göre d() verilen düğümün (vertex) derecesi ve v ise graftaki bütün düğümlerdir.
Örneğin yukarıdaki graf için bu değeri hesaplayacak olursak önce düğümlerin derecelerini çıkarmamız gerekir:
A 4
B 2
C 2
D 2
E 2
Şimdi bu değerlerin birer eksiklerinin faktöriyellerini çarpalım
3! = 6
1! = 1
1! = 1
1! = 1
1! = 1
sonuç olarak 6x1x1x1x1 = 6 farklı öyler döngüsü bulunabilir diyebiliriz.
SORU-64: Markof Modeli (Markov Model)
Bilgisayar bilimleri de dahil olmak üzere pekçok bilim ve mühendislik alanında kullanılan markof modelleri aslında graf teorisinin (graph theory) bir uygulamasıdır.
Basitçe düğümleri (nodes) durumlardan oluşan ve bu durumlar arasında istatistiksel geçişi modelleyen kenarları (edges) bulunan graflardır.
Markof modellerine (markof zinciri (markov chain) ismi de kullanılmaktadır) göre bir durum belirli bir istatistiksel değere göre değişir veya değişmeden aynı kalır. Ayrıca geçmiş durumların mevcut durum üzerinde bir etkisi söz konusu değildir. Ancak şimdiki durum gelecek durumları etkileyebilir.
Markof modelinin tanımı
Markof modellerinin istatistiksel olma özelliğinden dolayı her bir stokastik olayın (Stochastic Event) olasılık değerini modelleyen bir gösterimi mümkündür.
Bu olasılıkların gösterildiği formül
Yukarıdaki formülde t+1 zamanındaki olayların t zamanına bağlı olması söz konusudur. Yukarıdaki bu zaman bağlamında bağımlılıktan dolayı markov modellerinin yönlü graf olmaları gerekir.
Yukarıdaki şekilde bu olaylar arası bağlantı yönlü graf (directed graph) olarak görülmektedir. Elbette yukarıdaki graf sadece bir örnek olup olayların doğrusal olarak bağlanmaları gerekemez ancak yukarıdaki graftan anlaşılması gereken her olayın bir sonraki olaya belirli bir olasılık değeriyle devam edebileceği veya aynı kalacağıdır. Örneğin t anında X1 olayı oluyor olsun. t+1 zamanında X1 devam edebilir veya X2 olayına geçilebilir.
Yukarıdaki bu gösterimlere ilave olarak markov zincileri birer masfuf (matrix) ile de gösterilebilir.
Örneğin yukarıdaki matriste 4 olay arasındaki geçiş değerleri gösterilmiştir. Matrisin tek yönlü olmasına dikkat edilebilir. Yani köşegen simetriği (diagonal symmetry) mutlaka 0 olmalıdır. Örneğin C’den B’ye olasılık değeri 0.2 iken B’den C’ye 0 olmaktadır. Ayrıca satır bazında ihtimallerin toplamları 1 olmalıdır. Yani satırın ifade ettiği stokastik olaydan farklı bir stokastik olaya geçişin veya aynı olayda kalmanın ihtimalleri toplamı 1 olmalıdır.
Markof zincirlerine hava durumu örneği
Markof zincirleri tahmin (forecasting) için oldukça kullanışlıdırlar. Örneğin hava durumu tahmini için markof zinciri kullanmak isteyelim ve iki olayımız olsun:
- bugünkü hava
- yarınki hava
şimdi elimizde bugünkü havanın durumu bulunmakta. Bu olaydan yola çıkarak yarınki hava olayını (tahminini) yapmaya çalışalım ve olayı markof modeli ile modelleyelim.
- Bugün yağmur yağıyorsa -> yarın yağmur yağma ihtimali = 0.4
- Bugün yağmur yağıyorsa -> yarın yağmur yağmama ihtimali = 0.6
- Bugün yağmur yağmıyorsa -> yarın yağmur yağma ihtimali = 0.2
- Bugün yağmur yağmıyorsa -> yarın yağmur yağmama ihtimali = 0.8
olarak verilmiş olsun. Bu bilgiyi geçmiş tecrübelerden edindiğimizi ve markof modeli ile modellemek istediğimizi düşünelim.
Sonuç olarak yukarıdaki şekilde bir olasılık matrisi elde edilir. Bu matrise, stokastik matris (stokhastic matrix) ismi de verilmektedir.
Markof zincirleri ile Kola ve Pepsi örneği
Markof zincirlerinin anlatımı sırasında kullanılan meşhur örneklerden birisi de kola ve pepsi örneğidir. Bu örnekte bir kişinin en son aldığı içeceğin kola ( coca cola ) olması veya pepsi olması durumuna göre bir sonraki içeceğinin tahmin edilmesine çalışılır.
Örneğin bu iki içecek arasındaki ilişki ve karar olasılıkları yukarıdaki şekilde verilmiş olsun. Yani pepsi alan bir kişinin bir sonraki içceğinin yine pepsi olma olasılığı 0.8 ve kola olma olasılığı 0.2, benzer şekilde kola içen birisinin bir sonraki içeceğinin yine kola olma olasılığı 0.9 ve pepsi olma olasılığı 0.1 olarak verilsin.
Şimdi sorumuzu soralım:
Pepsi içen bir kişinin ikinci alış verişinde kola alma olsaılığı nedir?
Bu sorunun cevabı için stokastik matrise başvurarak basit bir matris çarpım işlemi yapabiliriz:
Önce yukarıdaki şekilde stokastik matrisimizi çıkaralım ve olasılıkları yerleştirelim. Şimdi sorumuza dönecek olursak bize ikinci alış verişteki ihtimal sorulmuşt. Bu durumda matrisin karesini alalım (kendisi ile çarpalım)
Yukarıdaki p2 matrisinden anlaşılacağı üzere markof modelimizdeki bir olayın tekrar etme olasılığını bulmuş olduk. Kişinin ikinci alışverişinde pepsiden kolaya geçme olasılığı yukarıdaki şekilde 0.34 olarak bulunmuş olur.
Örneğin yukarıdaki bu bilgiler ışığında bize şöyle bir soru da sorulabilir di:
Mevcut durumda insanların %60′nın kola ve %40′ının pepsi içtiklerini düşünelim. Bu insanların haftalık olarak içecek aldıklarını düşünürsek üç hafta sonra insanların ne kadarı kola içiyor olacaktır?
Şimdi bu soruyu çözerken 3 satın alma işlemi yani 3 kere markof modelde değişim işlemi yapılacağını hatırlayalım. Ardından mevcut durumun etkisini de ekleyerek hesabımızı aşağıdaki şekilde yapalım:
Yukarıdaki hesapta öncelikle 3. satın alma işlemi sırasında stokastik matrisin değeri hesaplanmıştır. Yani P3 için matris 3 kere kendisi ile çarpılmıştır. Ayrıca matristen elde edilen katsayılar 0.6 ve 0.4 mevcut durum oranıyla çarpılmıştır. Sonuçta 0.6438 oranında kişinin kola içeceği bulunmuş olunur.
SORU-65: Hamilton Yolu (Hamiltonian Path,hamiltonian circuit)
Bilgisayar bilimlerinde veri modellemede kullanılan graf teorisinde (graph theory) kullanılan bir yol (path) biçimidir. Tanımı oldukça basittir. Buna göre bir yolun (path) Hamilton yolu olabilmesi için bir kere geçilen kenardan (edge) tekrar geçilmemesi gerekir ve ayrıca yolun bütün düğümleri (nodes) birer kere ziyaret etmesi gerekir.
Bir hamilton yolu başladığı yerde bitiyorsa yani tam bir döngü (cycle) tamamlıyorsa (yoldaki son düğümden ilk düğüme gidilmesi mümkünse) bu yollara hamilton döngüsü (hamiltonian cycle) ismi de verilebilir.
Hamiltonian yolları yönsüz graflarda (undirected graph) tanımlıdır ancak hamilton yollarının benzerleri yönlü graflar (directed graphs) için de uyarlanabilir.
Bir graftaki hamilton yollarının bulunması işlemi NP-Complete bir işlemdir.
Örnek
Örneğin aşağıdaki şekli (graph) ele alalım:
Yukarıdaki şekil için tanımımıza uygun bir yol (path) elde etmek istersek:
Yukarıdaki şekilde kırmızı olarak gösterilen yol bir hamiltonian yoludur. Ancak bu şekilde çıkarılabilecek tek yol yukarıdaki yol değildir.
Yukarıdaki bu yeni yol da tanıma uygun bir yoldur ve ilk çıkan yoldan farklıdır. Görüldüğü üzere bir şekilde tek bir hamilton yolu bulunmak zorunda değildir.
Hamilton yollarının özellikleri
Herhangi bir hamilton döngüsü (hamiltonian cycle) tek bir kenarın (edge) çıkarılmasıyla bir hamilton yoluna (hamiltonian path) dönüştürülebilir.
2 den fazla düğüme (node) sahip ve tam bağlı (strongly connected) turnuva çizimleri birer hamilton yoludur (hamiltonian path)
Tam bağlı graflar (strongly connected graphs) için birbirinden farlı hamilton yollarının (hamiltonian path) sayısı (n-1)!/2 tanedir.
Hamiltonian yollarının kullanım alanları
Seyyar satıcı problemi (travelling salesman problem) gibi pekçok graf teori problemin çözümünde kullanılabilirler.
Sıfır bilgi ispatı (zero-knowledge proof) gibi veri güvenliği (cryptography) problemlerinde kullanılabilirler.
SORU-66: Augmented Transition Network (ATN, Uzatılmış Geçiş Ağı)
Bilgisayar bilimlerinde özellikle de yapay zeka konusunda ve buna bağlı diğer alt dallarda (örneğin doğal dil işleme) kullanılan bir graf teori (graph theory) gösterimidir. Kelime anlamı olarak uzatılmış geçiş ağı (tehir-i intikal şebekesi) denilen ağların amacı toplanan bilgilere göre bir karar vermek ve karar verme işlemi sırasında da bir belirsizlik (ambiguity) bulunuyorsa, karar verme işlemini yeterli bilgi topalanana kadar tehir etmektir (geciktirmektir, augment). Ağın ismi de buradan gelmektedir.
Temel olarak sonlu otomatları (Finite state automats) baz alan ağlarda her düğüm (node) bir dilbilimsel (linguistic) bilgiyi içermektedir. Kirişler (edges, arcs) ise bu dilbilimsel üniteler arasındaki geçiş imkanlarını gösterir.
Örneğin yukarıdaki şekilde Türkçe cümleler ile randevüları tutan bir takvim programı uygulaması olan yüksek lisans tezimden alınma doğal dildeki kelime grupları arasındaki geçişleri gösteren bir ATN görülmektedir.(tezin detaylarına www.shedai.net/tusa adresinden erişilebilir)
Yukarıda ise bir önceki şekilde görülen ATN kullanılarak “on ocak ikibin ile on mart ikibiniki arasında haftada bir ali bey ile bahçeli okulda saat onda ikişer saatlik toplantılar var” cümlesinin parçalama ağacı (parse tree) gösterilmiştir.
SORU-67: Özyinelilerde Ana Teorem (Master Theorem)
Bilgisayar bilimlerinin en önemli konularından birisi olan algoritma analizinin vazgeçilmez teoremidir. Kısaca özyineli (recurrence) bir problemin çözümü için kullanılır. Üç farklı değerin bulunmasını sağlar. Bunlar sırasıyla
O (büyük O, big-oh) : En kötü durum analizidir (worst case analysis)
(büyük teta, Theta) : Ortalama durum analizi (Average case anlaysis)
(büyük omega) : En iyi durum analizi (Best case analysis)
Olarak sıralanabilir. Bu değerlerin hesaplanabilmesi için ve ana metodun (master method) uygulanabilmesi için öncelikle aşağıdaki şekilde bir denkleme ihtiyaç duyulur.
Burada önemli bir uyarı, ana metodun (master method) her denkleme uygulanamayacağıdır. Sadece yukarıdaki şekilde denklemlere uygulanabilir. Bu denklemde ayrıca , , ve fonksiyonu asimptotik olarak pozitif bir fonksiyon olmalıdır. Ayrıca gösterimi ile ya yada değeri kastedilmektedir. Başlangıç değeri olarak eşitliği birkaç terimde sağlanabilir. Yani denklem ilk terimler için kararsız çalışabilir ve ortalama olarak tek adımda sonuca erişebilir ancak unutulmaması gereken nokta özyineli denklemlerin çözümünde sonsuza giden analizden bahsedilmektedir. Yukarıdaki denklemin ayrıca bir özelliği de parçala ve fethet (divide and conquer) yaklaşımı olmasıdır. Dikkat edilirse problem b parçaya bölünmektedir. Yani her adımda a alt problem olarak tanımlanabilecek terim n/b boyutunda eleman içermektedir. fonkisyonu ise bu bölme ve birleştirme işlemlerinin maliyetini tutmaktadır.
Master Theorem
Yukarıdaki tanıma uygun olarak
, , asimptotik pozitif, ve olarak tanımlanmış bir denklemimiz olsun.
- öyle bazı , dolayısıyla .
- , dolayısıyla .
- öyle bazı , ve şayet öyle bazı vardır ki büyük n tanımlamak yeterlidir, ve sonuçta denilebilir.
Örnekler
- olarak verilmiş olsun. Çözüm için , ,olarak alınır ve olduğu bulunur. Ayrıca , eşitliği ana metoddan yazılabilir. Dolayısıyla (2). Duruma uygun olarak bulunmuş olunur.
- olarak verilmiş olsun. Çözüm için , , olarak alınır ve . olduğu bulunur. Ayrıca eşitliğinden , olduğu bulunur. . eşitliği ana metoddan yazılabilir. Dolayısıyla (1) Duruma uygun olarak bulunmuş olunur.
Birleştirme sıralamasının ana teorem ile karmaşılığının hesaplanması (Mahir Bey’in sorusu üzerine ekliyorum)
Öncelikle birleştirme sıralamasının (Merge sort) özyineli denklemini (recursive equation) yazmaya çalışalım. Bilindiği üzere sıralama algoritmamız üzerinde işlem yaptığı veri yapısını (örneğin bir dizinin (array) ) parçalara bölerek ilerler. Kodu aşağıda verilmiştir:
Yukarıdaki kodun 25. ve 26. satırlarında bölünen iki parça kendi içerisinde sıralanmaktadır. Bu parçalar problemin ikiye bölünmüş alt parçalarıdır.
Bölünen parçalar tek elemanlı olunca birleştirme işlemi başlar. Bu durumu aşağıdaki şekilde formülleyebiliriz:
Yukarıdaki formülde dikkat edileceği üzere eleman sayısının tek olması durumunda sıralama işleminin çalışma süresi 1 olmaktadır. Bunun dışında problem iki ayrı n/2 parçaya bölünmektedir. n değerinin çift sayı olmaması gibi durumlar düşünülecek olursa, bir elemanın ya sağda ya da soldaki kısma dahil olması dolayısıyla parçalardan birisi diğerine göre bir eleman fazla alabilir. Bunun için yukarıdaki denklemde parçalardan birisinin tavan değeri alınırken diğerinin taban değeri alınmıştır.
Sonuç olarak n>1 durumu için iki ayrı parçanın ayrı ayrı hesaplanan özyineli (recursive) denklemlerinin toplamına dn gibi sabit bir değer ilave edilmektedir. Bu değer birleştirme işleminin o anlık maliyetidir.
Yukarıdaki denklemin çözümünü kolaylaştırmak için n = 2k olarak kabul edelim. Bu durumda denklemi aşağıdaki şekilde açmak mümkün olabilir:
Bu denklemde bölme işlemini yapar ve değerleri toplarsak
Yukarıdaki bu denklemi açarak bir ve iki seviye daha ilerletecek olursak:
Denlemleri elde edilir. Bu denklemlerde dikkat edileceği üzere d2k değerinin çarpanı açılan seviye ile aynıdır. Dolayısıyla denklemin son haliyle ana teoremi uygulayabiliriz. f(n)=n için nlogba değeriolarak 1 yazılabilir. Bu durumda teoremimizde yazdığı üzere T(n) = Θ ( n log n) olarak fonksiyonun karmaşıklığı bulunmuş olunur.
Ana Teoremde 4. Kural
Gelen soru üzerine, aslında klasik olarak ana teoremde geçmeyen dördüncü kuralı da ekleme ihtiyacı doğmuştur. Yazının altında eklenen sorularda, f(n) fonksiyonunun logaritmik olması durumu verilmiştir. Bu durum klasik 3 duruma girmez. Klasik üç durumda da f(n) fonksiyonunun tam polinom olması beklenir. Oysaki logaritmik bir fonksiyon polinom değildir. Bu durumları içeren ve f(n) fonksiyonunun polilogaritmik olması halini kapsayan özel bir 4. durumu aşağıdaki şekilde tanımlayabiliriz.
Yukarıdaki şekilde bir f(n) fonksiyonu verilmesi durumunda T(n) fonksiyonunun asimptotik üyeliği gösterilmiştir. Burada k>=1 olması gerekliliği bulunur.
Örnek:
şeklinde verilen denklemi ele alalım.
Bu denklemin sonunda bulunan f(n) kısmı, logaritmadır. Bu durumda
Şeklinde değişkenler sıralanabilir. Yani verilen denklemde a=2 ve b = 2 olarak verilmiş, f(n) fonksiyonu ise nlogn olarak verilmiştir.
Bu durumda, sonuç yukarıdaki son satırda gösterildiği gibi logaritma kare olarak ifade edilir.
Gelelim, yazıya bu kısmı eklememize sebep olan soruya.
Bu denklemin sonucu, bir önceki denklemde olduğu gibi
Olarak bulunacaktır. Sebebi ise, denklemde bulunan 6/3 = 2 olması ve dolayısıyla k=1 için verilen ilk T(n) fonksiyonunun ana teoreme konulması sonucunda k+1 = 2 olarak bulunmasıdır.
Daha açık yazacak olursak:
Şeklinde yazılan denklemde değeri, asimptotik bağlantıdan dolayı orijinal denklemimizdeki 2 değeri olarak düşünülebilir. Bu durumda k=1 değeri bir arttırılarak
Sonucunu buluyoruz.
SORU-68: Floyd-Warshall Algoritması
Bilgisayar bilimlerinin önemli konularından olan algoritma analizi sırasında sıkça bahsi geçen bir algoritmadır. Algoritmanın ana amacı belirli bir graf üzerinde bir başlangıçtan(source) bir bitiş düğümüne (sink, end, target) en kısa yoldan (shortest path) ulaşmaktır.
Bu özelliğinden dolayı, maksimum akış (maximum flow) problemleri olarak bilinen, ve örneğin bir dağıtım şebekesinde bir kaynaktan bir hedefe gönderilebilecek azami miktarı sevk eden problemlerin çözümünde kullanılabilen bu algoritma, algoritmayı bulan iki kişinin ismi ile anılmaktadır.
İçerik
1. Algoritma
2. Örnek
3. Algoritmanın kullanım alanları
Bu algoritmanın ana amcı dinamik programlama (dynamic programming) konusunu daha iyi anlayabilmektir.
Algoritma
Algoritma basitçe bir grafta gidilebilecek düğümlerin komşuluk listesini (adjecency list) çıkararak bu düğümlere olan mesafeyi tutan bir masfuf (matris) üzerinden çalışır.
Algoritmanın her adımında matris üzerinde çeşitli işlemler yapılarak en kısa yol bulunmaya çalışılır.
Algoritma matris üzerinde çalışan ve düğümler arası mesafeleri her adımda güncelleyen bir algoritma olduğu için itartif (iterative, döngü ile) yazılabilir. Aşağıda müsvette kodlar (pseude codes) verilmiştir.
1 int yol[][]; 2 /* Sonucun içinde bulunacağı ve anlık olarak bir düğümden 3 diğerine ne kadar maliyet ile bulunduğunu tutan matris. 4 İlk olarak komşuluk listesini tutar ve doğrudan 5 gidilemeyen düğümlere olan mesafe sonsuz olarak tutulur.*/ 6 7 procedure FloydWarshall () 8 for k: = 1 to n 9 for each (i,j) in{1,..,n}210 yol[i][j] = min ( yol[i][j], yol[i][k]+yol[k][j] );
Yukarıdaki algoritmada görüldüğü üzere n adet düğümlü bir graf için iki boyutlu bir dizi oluşturulur. Bu dizinin içerisindeki değerlere ilk olarak komşuluk listesindeki değerler atanır ve doğrudan ulaşılamayan düğümler için sonsuz değeri doldurulur.
Ardından Matrisi dolaşan bir döngü ile (i ve j) yollar güncellenir. Bu matrisin taranması işlemi düğüm sayısı (n) kadar tekrar eder (yukarıdaki k döngü değişkeni tekrara yaramaktadır). Bu tekrar aslında üzerinden atlanma ihtimali olan düğümü belirtmektedir. Yani örneğin k = 3 için 3. düğüm marifetiyle ulaşılan düğümler belirlenir. (örneğin 1. düğümden 4. düğüme doğrudan erişim yoksa ama 1. düğümden 3. düğüme ve 3. düğümden de 4. düğüme erişim varsa )
Yukarıdaki algoritma daha basit bir şekilde yazılabilir:
for i = 1 to N for j = 1 to N if(i'den j'ye bir yol varsa) yol[0][i][j] = i ile j arasındaki mesafe else yol[0][i][j] = sonsuz for k = 1 to N for i = 1 to N for j = 1 to N yol[k][i][j] = min(yol[k-1][i][j], yol[k-1][i][k]+ yol[k-1][k][j])
Algoritmanın çalışmasına örnek
Algoritmanın çalışmasını daha iyi anlayabilmek için aşağıdaki örnek üzerinden adım adım algoritmayı kullanarak en kısa yolu bulalım:
Yukarıdaki şekil incelendiğinde A’dan E’ye giden birden çok yol bulunabilir:
Yol 1: A -> B -> E 20Yol 2: A -> D -> E 25Yol 3: A -> B -> D -> E 35Yol 4: A -> D -> B -> E 20
Yukarıdaki yollar çıkarıldıktan sonra en kısasının 20 uzunluğunda olduğu bulunabilir. Şimdi bu yollardan en kısasını Floyd-Warshall algoritmasının nasıl bulduğunu adım adım inceleyelim:
1. Adımda komşuluk listesine göre matris inşa edilir.
Yukarıdaki şekilde doğrudan ilişkisi bulunan düğümler ve ağırlıkları aşağıda verilmiştir:
A B C D EA 0 10 ∞ 5 ∞B 10 0 5 5 10C ∞ 5 0 ∞ ∞D 5 5 ∞ 0 20E ∞ 10 ∞ 20 0
Yukarıdaki grafta doğrudan ilişkisi bulunmayan düğümlerin değerleri ∞ olarak gösterilmektedir. Diğer değerler doğrudan ağırlıkları göstermektedir.
Şimdi algoritmanın 2. adımına geçerek yolların tutulduğu bu matrisi adım adım güncelleyelim:
A B C D EA 0 10 ∞ 5 ∞B 10 0 5 5 10C ∞ 5 0 ∞ ∞D 5 5 ∞ 0 20E ∞ 10 ∞ 20 0
B üzerinden atlanarak ulaşılan düğümleri güncelleyelim
A B C D EA 0 10 15 5 20B 10 0 5 5 10C 15 5 0 10 15D 5 5 10 0 15E 20 10 15 15 0
C üzerinden atlanan düğümler:
A B C D EA 0 10 15 5 20B 10 0 5 5 10C 15 5 0 10 15D 5 5 10 0 15E 20 10 15 15 0
D üzerinden atlanan düğümler:
A B C D EA 0 10 15 5 20B 10 0 5 5 10C 15 5 0 10 15D 5 5 10 0 15E 20 10 15 15 0
E üzerinden atlanan düğümler:
A B C D EA 0 10 15 5 20B 10 0 5 5 10C 15 5 0 10 15D 5 5 10 0 15E 20 10 15 15 0
Yukarıda son elde edilen bu matriste görüldüğü üzere herhangi bir düğümden diğer bütün düğümlere giden en kısa yollar çıkarılmıştır. Örneğin A düğümünden E’ye 20 uzunluğunda veya C düğümünden D’ye 10 uzunluğunda yolla gidilebilir.
Yukarıdaki matrislerde diyagona göre simetri bulunmasının sebebi grafın yönsüz graf (undirected graph) olmasıdır. Şayet graf yönlü graf (directed graph) olsaydı bu simetri bozulurdu (tabi yönlerin ağırlıklarının aynı olmaması durumunda).
Algoritmanın kullanım alanları
Floyd-Warshall algoritması aşağıdaki amaçlar için kullanılabilir:
- Yönlü graflarda en kısa yolun bulunması için. (Yukarıda bu durumu gösteren bir örnek bulunmakta)
- Bir düğümden seyahat edilebilecek diğer düğümlerin bulunmasında (transitive closure). Örneğin matrisin ilk halinde gidilemeyen düğümler sonsuz değerine sahiptir. Şayet matris işlendikten sonra hala sonsuz değerine sahip matris elemanı bulunursa bunun anlamı o satır ve sütündakı düğümler arasında gidişin aktarmalı da olsa mümkün olmadığıdır.
- Düzenli ifadelerde (regular expressions) herhangi bir tekrarlı olayın tespiti için kullanılabilir. Kleen yıldızı (kleen’s algorithm) şeklinde tekrar eden ifadelerin bulunmasına yarar. Şayet çıkan matriste bir düğümden diğer düğüme giden yol sonsuz değilse ve diyagona göre simetriği olan yol da sonsuz değilse bir kleen yıldızı üretilebilir.
- Ağ programlamasında özellikle yönlendirici algoritmalarında (routing algorithms) en kısa yolun tayin edilmesinde kullanılabilir.
- Yönsüz bir grafın (undirected graph), iki parçalı graf (bipartite graph) olup olmadığının bulunmasında kullanılabilir. Basitçe graftaki mesafelerin tamamını 1 uzunluğunda kabul edersek (veya mesafe olarak atlanan düğüm sayısını (hop count) kabul edersek) bu durumda bir düğümden gidilebilen bütün komşu düğümlerin mesafesi ya tek ya da çift olmalıdır. Bir düğümün hem tek hem de çift komşusu varsa bipartite değildir denilebilir.
SORU-69: Boyer Moore Dizgi Arama Algoritması (Boyer-Moore String Search)
Bir metin veya hedef dizgi (string) içerisinde bir başka dizginin (string) aranması sırasında kullanılan algoritmalardan birisidir. KMP (Knuth Morris Prat) algoritması ile birlikte en çok kullanılan arama algoritmalarındandır.
Bu algoritmadaki amaç bütün harfleri teker teker kontrol eden doğrusal aramadan (linear search) daha iyi bir sonuç elde etmektir.
Algoritmanın çalışması
BM algoritması basitçe aranan metni hedef metin ile eşleştirir. Bulduğu sonuca göre de atlama gerçekleştirir. Ayrıca aranan metne göre de bir atlama tablosu (jump table) tutarak işlemi hızlandırır.
Bu atlama işlemini bir örnek üzerinden anlamak daha kolay olacaktır.
Örnek çalışma
Örneğin hedef metin olarak:
XXXBCDŞADEFABŞADI
Aranan kelime olarak da :
ŞADI
kelimesini ele alalım. İlk karşılaştırılma işlemi sonucunda şayet Ş,A,D,I harflerinden birisi hedef metinde yer almıyorsa ilk 4 harfin kontrol edilmesi tamamlanmış ve 4 harf kaydırılabilir demektir.
Yani aşağıdaki kaydırma ve kontrol adımlarının tamamından tek seferde kurulunabilir:
XXXBCDŞADEFABŞADIŞADIXXXBCDŞADEFABŞADI ŞADIXXXBCDŞADEFABŞADI ŞADIXXXBCDŞADEFABŞADI ŞADIXXXBCDŞADEFABŞADI ŞADI
Yukarıdaki işlemlerin tek seferde anlaşılması mümkündür çünkü ilk karşılaştırma işlemi sırasında aranan metin boyutu (örnekte 4 harfli ŞADI kelimesi alınmıştır) kadar karşılaştırma yapılmış ve hiç Ş harfi bulunmamıştır. Dolayısıyla hedef metin içerisinde aranan metnin bulunma ihtimali yoktur.
Örnek ilkel atlama tablosu
Yukarıdaki örnekten yola çıkarak aşağıdaki tek harfli atlama tablosu oluşturulabilir.
|
I |
0 |
|
D |
1 |
|
A |
2 |
|
Ş |
3 |
|
Diğer bütün karakterler için |
4 |
Yukarıdaki tablodan da görüleceği üzere şayet karşılaştırma sonucunda yukarıdaki harflerden birisine rastlanırsa verilen karakter sayısı kadar kaydırma işlemi gerçekleştirilir. Örneğin aşağıdaki şekilde karşılaştırma yapıldığını varsayalım:
XXXBCDŞADEFABŞADI ŞADI
Yukarıda görüldüğü üzere D harfi aranan metin içerisinde tespit edilmiştir. bu durumda tek kaydırma işlemi yapılabilir ve aşağıdaki durum kontrol edilir
XXXBCDŞADEFABŞADI ŞADI
Benzer şekilde aşağıdaki durumda A harfine rastlandığı için 2 karakter kaydırma işlemi gerçekleştirilir.
XXXBCDŞADEFABŞADI ŞADI
Bu tablolama tek harf için çalışmaktadır ve örneklerden de anlaşılacağı üzere bir harfe konsantre olunduğunda zaman kazandırmakta ancak yine de gereksiz arama işlemiyle vakit kaybetmektedir. Örneğin yukarıdaki son örnekte A harflerini alt alta getirmek için aranan kelime 2 karakter kaydırılmıştır ancak burada aranan kelimenin olmadığı açıktır çünkü hedef metinde A harfinden hemen önce F harfi bulunmaktadır bu durum da aranan kelimenin olmasını engellemektedir.
Daha gelişmiş atlama tabloları
Yukarıdaki tek harfli atlama tablolarından anlaşılacağı üzere tek harfin kontrolü bazı durumlarda zaman kaybına sebep olmaktadır. Daha kesin bir çözüm alt kelime içeren tablolar hazırlanarak elde edilebilir.
Örneğin yukarıdaki örnek aranan metin için aşağıdaki tablo hazırlanabilir:
|
I |
0 |
|
DI |
1 |
|
ADI |
2 |
|
ŞADI |
3 |
|
Diğer bütün karakterler için |
4 |
Yukarıdaki tabloda yeni olan sondan bakılan harf sayısıdır. Bu sayılar metnin karşılaştırılması sırasında çeşitli durumlarda ne kadar atlanacağını vermektedir.
Algoritma performansı
Algoritma performansı doğrusal aramada aranan kelime uzunluğu (a) ile hedef kelime uzunluğu (h) çarpımıdır: ha
Ancak BM algoritaması burada devreye girerek aranan kelimenin bütün harflerinin kontrolünü her seferinde engellemektedir. Bu yüzden algoritma başarısı h olarak indirgenebilir. dolayısıyla doğrusal hıza sahip olunur ve O(n) ile ifade edilebilir.
SORU-70: Binom Ağaçları (Binom Trees, Çift Termili Ağaçlar)
Bilgisayar bilimlerinde kullanılan bir veri yapısı türüdür. Bu yapıda ağacın her elemanı binom dağılımındaki sayılar kadar çocuk düğüm (node) sahibi olur.
Daha basit bir tabirle her eklenen yeni düğüm ağacın o ana kadar olan bir kopyasıdır.
Yukarıdaki şekilde 5 farklı binom ağacı gösterilmiştir. İlk ağaçtan (B0) tek bir düğüm bulunmaktadır. İkinci ağaç olan B1 ağacının çocuğu, B0′ı içermelidir. Bu durumda B1 ağacı şekilde görüldüğü şekilde tek çocuklu olur.
B2 ağacında eklenen yeni çocuk ise B1′in tamamı olmalıdır.
Benzer şekilde B3 ağacında eklenen 3. çocuk B2 ağacının tamamı ve B4 olarak eklenen yeni ağaç ise B3′ün tamamından oluşmaktadır.
Bu özyineli (recursive) yapı aşağıdaki şekilde daha net görülmektedir:
Yukarıdaki şekilde kutu içerisine alınan alt ağaçlar (sub tree) bir önceki ağacın kopyası niteliğindedir.
Bu ağaca binom ağacı denmesinin sebebi ağaçtaki her seviyenin sayılarının binom dağılımı ile aynı olmasıdır.
Yukarıdaki şekilde bu durum net olarak görülmektedir. Yani B4 ağacı binom dağılımındaki 4. terimi yada diğer bir ifadeyle binom üçgeninin 4. satırını göstermektedir.
Bilgisayar bilimlerinde çeşitli amaçlar için kullanılan eşleştirme problemlerinin genel ismidir. Genellikle bir arz ile bir talebin eşleştirilmesi şeklinde olur. Örneğin bilgisayarın kaynaklarının, bu kaynakları talep eden işlemler ile eşleştirilmesi gibi. Ya da gerçek hayattan bir çalışanın uygun iş ile eşleştirilmesi veya evlilik problemleri veya iş akış diyagramları (işin doğru kaynak üzerinden akması) gibi problemler bu grupta sayılabilir.
İçerik
1. Eşleme Tipleri
a. Çoklu Eşleme (Maximal Matching)
b. Asgari Eşleme (Maximum Matching)
c. Mükemmel Eşleme (Perfect Matching)
d. Yaklaşık Mükemmel Eşleme (Near Perfect Matching)
2. Eşleme yolları
a. Dalgayı yol (Alternating Path)
b. Uzatılmış Yol (Augmented Path)
Eşleme problemleri genel olarak bir graf problemi olarak da düşünülebilir. Yani amaçlanan eşleme işlemi bir graf ile modellenebilir ve bu model üzerinde graf teorsinin bize sunduğu bütün imkanlar kullanılabilir.
Örneğin Petri Ağları (Petri Networks) bu konuda oldukça uygun bir çözüm ortamıdır. Benzer şekilde koşul programlama (constraint programming) başlığı altında da pek çok graf modellemesi ve çözümü (örneğin kiriş koşulu (arc constraint) ) kullanmak mümkündür.
Eşleme problemlerinde genellikle kenar bağımsız kümesi (edge independent set) bulunmaya çalışılır. Bu küme genellikle eşleşecek olan varlıkları ( grafta genellikle düğümler (nodes) ile gösterilir) eşlenmiş olarak modeller ve eşlenmeyen dışarıda kalan düğümlerin tespitini kolaylaştırır.
- Eşleşme Tipleri
Bir grafta bulunan bir düğüm (node , vertex) şayet bir kenara (edge) bağlıysa bu düğüm eşleşmiş kabul edilir (matched). Şayet bir kenara bağlı değilse, eşleşmemiş (unmatched) kabul edilir.
- Çoklu Eşleşme (Maximal Matching)
Bir grafta ortak düğümleri bulunmaksızın alınabilecek en fazla kenar sayısı o grafın çoklu eşlemesini veriri (maximal match). Örneğin aşağıda bu duruma örnek graflar bulunmaktadır:
Yukarıdaki grafta 3 kenar (edge) bulunmaktadır ve çoklu eşleşme bu kenarlardan sadece birisinin alınması ile olabilir. YAni A-K kenarı alındığı için B-K veya C-K kenarları alınamaz çünkü bu kenarlardan herhangi birisinin daha alınması durumunda K düğümü ile ortak kesişim düğümü bulunmuş olacaktır.
Alternatif olarak yukarıdaki grafta sadece B-K veya sadece C-K düğümleri alınabilir. Ancak yukarıdaki çoklu eşleme sonucunda 1 kenar alınabilmektedir.
Örneğin yukarıdaki grafta çoklu eşleşme sonucunda iki kenar alınabilmektedir. Yukarıdaki grafta bu kenarlar Ş-İ ve A-D olarak belirlenmiştir. Alternatif olarak Ş-A ve D-İ kenarları da olabilirdi ancak örneğin Ş-A ve A-D kenarları aynı anda alınamaz çünkü bu durumda A düğümü hem Ş hem de D düğümü ile eşleşmiş olur.
Amacımız olan eşleşmeye geri dönecek ve bu bilgiyi yorumlayacak olursak. Bir düğüm (bir kaynak, kişi ya da varlık) sadece bir düğüm ile eşleşebilir. Örneğin bir kişi sadece bir başka kişi ile evlenebilir veya bir kişinin sadece bir işi olabilir şeklinde düşünülebilir. Bu durumun dışındaki talepler graf teorisinde farklı şekillerde çözülür. Şimdilik bu eşleşme tanımı üzerinden farklı eşleşme türlerini tanıyalım.
- Azami Eşleme (Maximum Matching)
Azami eşleme, çoklu eşlemeden farklı olarak bir grafta bulunabilecek en fazla eşi arar. Örneğin aşağıdaki şekilde bu iki eşleme arasındaki farkı görebiliriz.
Örneğin yukarıdaki şekilde birbiri ile komşu olmadığı için alınan iki kenar (edge) görülüyor. Bu durumda yukarıdaki graf çoklu eşleme (maximal matching) için uygun bir graf iken azami eşeleme (maximum matching) kabul edilemez. Yukarıdaki grafın azami eşleme olması için aşağıdaki şekilde alınabilecek en fazla kenarı eşlemesi gerekir:
Yukarıdaki şekilde, bir önceki şekilden farklı olarak iki kenar yerine 3 kenar alınmıştır. Yukaırdaki şekilde 4 kenar eşlemenin imkanı bulunmadığı için azami eşleme (maximum matching) olarak kabul edilebilir.
- Mükemmel Eşleme (Perfect Matching)
Şayet bir graftaki bütün düğümler bir başka düğüm ile eşlendiyse ve eşleşmemiş bir düğüm kalmadıysa bu eşleme tipine mükemmel eşleme ismi verilir.
Örneğin aşağıdaki grafta yapılan eşleme mükemmel eşleme değildir:
Yukarıdaki graf mükemmel eşleme değildir çünkü E ve F düğümleri eşleme dışında bırakılmıştır.
Buna karşılık yukarıdaki eşleme mükemmel eşleme olarak kabul edilebilir çünkü bütün düğümler en az bir eşlemenin içine alınmıştır.
- Yaklaşık Mükemmel Eşleme (Near Perfect Matching)
Bu eşleme, mükemmel eşlemeden farklı olarak tek bir düğümün eşlenmemesi durumunu kabul eder. Yani grafta şayet tek sayıda düğüm varsa eşleme sonucunda bir düğümün dışarıda kalması zaten beklenen bir durumdur. İşet bu durumda diğer bütün düğümler eşleniyor ancak tek bir düğüm dışarıda kalıyorsa buna yaklaşık mükemmel eşleme ismi verilir.
- Eşleme sonucu yollar (Paths)
Bir eşleme (matching) işlemi sonucunda grafta elde edilen yollar için aşağıdaki tanımlar yapılabilir.
- Dalgalı Yol (Alternating Path)
Şayet grafta elde edilen yol bir eşlenmiş bir de eşlenmemiş şeklinde devam ediyorsa bu yola dalgalı yol ismi verilir.
Örneğin aşağıdaki grafta bu durum görülebilir:
Yukarıdaki grafta F-İ-Ş-A-D-E yolu veya F-İ-D-E yolu birer dalgalı yoldur çünkü yoldaki eşlemeler sonucunda geçilen kenarlar bir eşlenmiş bir de eşlenmemiş olarak sınıflandırlabilir.
- Uzatılmış Yol (Augmented Path)
Bir dalgalı yolun ilave bir düğüm ile başlaması durumudur. Yani şayet yola eşleşmemiş bir düğüm ile başlanıyorsa yada farklı bir ifadeyle başlanan düğüm herhangi bir eşleme içerisinde değilse bu yola uzatılmış yol ismi verilir.
Örneğin yukarıdaki şekilde C-K-A veya B-K-A yolları uzatılmış yollara örnektir. Başlana düğümler herhangi bir eşleme içerisinde değildir.
SORU-72: Petri Ağları (Petri Nets)
Bilgisayar bilimlerinde özellikle birbiri ile eş zamanda çalışan işlerin (concurrent jobs) modellenmesi ve çözülmesinde kullanılan özel grafiklerdir. Bu graflara Yer / Geçiş Ağları (Place / Transition Networks veya P/T Nets) ismi de verilir.
İçerik
1. Örnek Petri Ağları
2. Dairesel Petri Ağları
3. Paralel Petri Ağları
4. Koşullu Petri Ağları
Özellikle dağıtık sistemlerde (distributed systems), paralel programlamada (prallel programming) ve eş zamanlı çalışmada (concurrent processing) oldukça sık kullanılan bu ağlar yönlü iki parçalı ağaç (directed bipartite graph) olarak sınıflandırılabilir. Buna göre petri ağlarında (petri nets) bir olay ya da geçiş (transition) bir yer yada koşul (place) ve bir yön (ok, arc) bulunur. Örneğin iş akışını gösteren bir diyagramda bir işin bir noktadan başlayarak sonuca kadar izlediği rota çizilirken geçtiği yerler ve geçerken bazı koşullara göre farklı yerlere yönlendirilmesinden söz edilebilir. İşte iş akış grafikleri klasik birer petri ağıdır.
Örnek Petri Ağları
Örneğin aşağıda basit bir petri ağı gösterilmektedir:
Bir petri ağında daireler yerleri (places) ve kareler geçişleri (transitions) temsil eder. Yukarıdaki ağda a ve b birer geçiş (transition) iken 1 bir yerdir (place). Oklar ise yerler ve geçiler arasındaki akışı gösterir. Bu geçişleri birer zaman akışı olarak düşünmek de mümkündür. Örneğin iş akış şemasında bir işin akışının zamanda ilerlemesi gösterilir.
Yukarıdaki örnek petri ağı basit bir yaşamı göstermek için çizilmiştir. Bu durumda
a: doğum
1: hayatın yaşanması
b: ölüm
olarak yorumlanabilir. Yani bir canlının basit bir şekilde doğması yaşaması ve ölmesi olarak düşünülebilir.
Döngüsel Petri Ağları (Circular Petri Networks)
Benzer bir örneği mevsimlerden verebiliriz. Örneğin dört mevsim sürekli olarak birbirini kovalar ve ayrıca mevsimlerin geçişi söz konusudur.
Örneğin yukarıdaki petri ağında 1′den 4′e kadar olan daireler mevsimleri (yaz, bahar, kış, güz) ve a-d arasındaki harflerde mevsim dönüşlerini (ekinoks) göstermektedir.
Yukarıdaki şekilde bir döngü halinde mevsim dönüşü görülmektedir.
Paralel Petri Ağları
Petri ağlarının eş zamanlı (paralel) işlemler için kullanıldığını görmüştük. Şimdiye kadar olan örneklerde hep birbirini izleyen işler modellendi. Şimdi paralel olayların nasıl modellendiğine bakalım:
Örneğin yukarıdaki şekil iki paralel olayı (1 ve 2) modellemek için kullanılmıştır. Yukarıda 2007-2009 yılları arasında öğrenci olan ve aynı zamanda çalışan bir kişinin aynı anda yaptığı bu işler modellenmiş olsun.
Bu durumda a: 2007 , b ise 2009′u göstermektedir. Yani olayların başlangıç ve bitişleri gösterilir.
1 numara ile okuma eylemi ve 2 numara ile çalışma eylemi modellenmiş kabul edilirse bu iki eylemin başlangıcı ve bitişi aynı tarihlere tesadüf etmiştir.
Dolayısıyla yukarıdaki şekilden iki farklı eylemin aynı başlangıç ve bitişle paralel olduğunu söyleyebiliriz.
Koşullu Petri Ağları
Petri ağlarında ayrıca koşul bulundurmak ve bu koşula göre zamanın akışını şekillendirmek mümkündür.
SORU-73: İki Parçalı Graflar (Bipartite Graphs)
İçerik
1. İki parçalı graflara örnekler
2. İki parçalı grafın test edilmesi
3. İki parçalı grafların kullanım alanları
4. İki parçalı grafların özellikleri
Bilgisayar bilimlerinde veri modellemede sıkça kullanılan grafların (graph) özel bir durumudur. Buna göre bir graf’ı oluşturan düğümleri iki farklı kümeye ayırabiliyorsak ve bu iki kümenin elemanlarından küme içerisindeki bir elemana gidilmiyorsa. Yani bütün kenarlar (edges) kümeler arasındaki elemanlar arasındaysa, bu graflara iki parçalı graf (bipartite graph) ismi verilir.
İki parçalı graflara örnekler
Örneğin aşağıdaki şekilde gösterilen graf’ın iki parçalı olup olamayacağını incelemeye çalışalım:
Yukarıda örnek olarak verilen garfın düğümlerini koşulumuzu sağlayacak şekilde yani iki ayrı küme oluşturacak ve bu kümelerin içerisinde yol bulunmayacak şekilde yerleştirmeye çalışalım.
Yukarıdaki şekilde, ilk şekilden farklı olarak sadece düğümlerin (nodes) yerleri değiştirilmiştir. Yukarıdaki şekilde ayrıca iki ayrı küme oluşturulmuş ve ilk küme ile ikinci küme arasında bölünen düğümlerin tamamında kenarlar karşı kümeye işaret etmiştir. Yani aynı küme içerisinde hiçbir kenar bulunmamaktadır.
Bu durumu aşağıdaki şekilde de gösterebilirdik:
Yukarıdaki şekilde görüldüğü üzere komşu olmayan düğümler renklendirilmiştir. Yani komşu iki düğüm aynı renkle renklendirilmemiştir. Daha farklı bir deyimle bir düğümün komşuluk listesindeki (adjacency list) bütün düğümler aynı renkte boyanmıştır. Sonuçta aynı renkte boyanan iki komşu oluşmamaktadır.
Aşağıdaki iki parçalı olmayan grafta aynı şekilde yaklaşırsak problem orataya çıkacaktır.
Yukarıdaki şeki bipartite tree (iki parçalı ağaç) değildir. Çünkü graftaki D ve G aynı renktedir. Bu düğümlerden birisinin rengi değiştirildiğinde bu sefer de H ile aynı renkte olacaktır. Dolayısıyla hiçbir şekilde iki grup elde edilemez. Farklı bir ifadeyle:
Grafı yukarıdaki şekilde iki gruba bölmek istersek görüldüğü üzere aynı gruptaki iki düğüm arasında bir kenar bulunmakta bu durumda da iki parçalı ağaç olamamaktadır.
İki parçalı grafın test edilmesi
Bir grafın iki parçalı olup olmadığını (bipartite) test etmek için ne yazık ki yukarıda gösterildiği gibi graftaki bütün düğümlerin yerlerinin değiştirilmesi iki kümede toplanması ve sonunda da aralarında bir kenar olup olmadığına bakılması mümkün değildir.
Bir grafın iki parçalı olduğunu anlamak için grafın bütün elemanları arasında tek ve çift ayrımı yapılabilir. Örneğin rast gele seçilen bir düğümden başlanarak diğer bütün düğümlerin bu düğüme olan mesafesini yazmamız ve neticesinde tek ve çift düğümlerin komşuluğunu kontrol etmemiz yeterlidir. Yani çift veya tek iki düğüm birbirine komşu değilse bu grafa iki parçalı graf ismi verilebilir.
Yukarıdaki şekilde başlangıç için rast gele olarak 0 seçilmiş olsun. Bu grafta K düğümüne olan uzaklıklarına göre bütün düğümlere mesafeleri yazılmıştır. Örneğin İ düğümü K düğümüne 3 düğüm uzaklıktadır. Sonrada bütün düğümler tek ve çift olmasına göre renklendirilmiştir. Yani tek sayıdaki uzaklıkta olanlar sarı, çift sayıdaki uzaklıkta olanları ise mavi boyanmıştır. Bu boyama sadece görüntüleme için kullanılmıştır.
Programlama sırasında bilgisayar tek ve çift sayıdaki düğümleri kontrol edebilir. Örneğin G ve İ düğümleri komşudur. Benzer şekilde İ ve I düğümleri de komşudur. Şayet bu düğümler bir şekilde aynı şekilde olsaydı (ikiside çift veya ikisi de tek olsaydı) bu durumda iki parçalı bir grafik değildir sonucuna varılacaktı.
İki parçalı grafların kullanım alanları
Özellikle eşleştirme problemleri için oldukça kullanışlıdırlar. Eşleştirme problemleri (matching problems) genelde iş/işçi eşleştirmesi, evlilik, problem / çözüm eşleştirmesi gibi farklı unsurları birleştirmek için kullanılırlar.
Örneğin k kişisi i işi için uygunsa k kişisi ile i işi arasında bir kenar bulunuyor demektir. Bu iş ve kişilerden oluşan grafikte atama hatalarının olup olmadığının bulunması iki parçalı ağaç bulan algoritmalar için oldukça basittir.
Örneğin paralel işleme (eş zamanlı işleme) ve koşut zamanlı işleme (concurrent processing) gibi aynı anda birden fazla işin beraber yapıldığı işlerde kullanılan petri ağları (Petri nets, place / transition nets) gibi ağların modellenmesinde ve çalıştırılmasında önemli rol oynarlar.
iki parçalı grafların özellikleri
iki parçalı graflar için aşağıdaki çıkarımlar ve koşullar sıralanabilir:
- Bütün ağaçlar iki parçalı graftır.
- Şayet bir graf döngü (cycle) içermiyorsa iki parçalı graftır.
- Şayet bir graf tek sayıda düğümden oluşan bir döngü (cycle) içeriyorsa iki parçalı graf değildir.
- Şayet bir graf boyandığında iki renk veya daha az renkle boyana biliyorsa bu graf iki parçalı graftır.
SORU-74: Minimax Ağaçları (Minimax Tree)
Bilgisayar mühendisliğinde, yapay zeka konusunda kullanılan bir karar ağacı türüdür. Aslında minimax ağaçları bilgisayar bilimlerine işletme bilimindeki oyun teorisinden (game theory) girmiştir.
Temel olarak sıfır toplamlı bir oyunda (zero sum game), yani birisinin kaybının başka birisinin kazancı olduğu (veya tam tersi) oyunlarda karar vermek için kullanılışlıdırlar. Örneğin çoğu masa oyunu (satranç, othello, tictactoe gibi) veya çoğu finansal oyunlar (borsa gibi) veya çoğu kumar oyunları sıfır toplamlı oyunlar arasında sayılabilir (yani birisinin kaybı başka birisinin kazancıdır ve sonuçta toplam sıfır olur).
Yukarıda bahsedilen bu oyunlarda doğru karar verilmesini sağlayan minimax ağacı basitçe kaybı asgariye indirmeye (mimize etmeye) ve dolayısyıla kazancı azamiye çıkarmaya (maximize etmeye) çalışır.
Ağaç basitçe her düğümde (node) farklı alternatiflerin değerlerini hesaplar. Son düğümden (yapraklardan ,leaf) yukarıya doğru değerleri seçerek gelir ve en sonunda bütün ağaçtaki en doğru seçenek seçilmiş olur.
Örneğin Tic Tac Toe oyununu ele alalım. 3×3 boyutundaki kare bir tabloda sırasıyla X ve O harflerinin taraflarca yazıldığı bu oyunda, oyunun durumuna göre aşağıdakine benzer bir karar ağacı çıkması mümkündür:
Yukarıda ağacın kökünü (root) oluşturan ilk durumda oyunun mevcut hali görülmektedir. Ardından karar verecek taraf olan ve tahtaya X sembolü koyan oyuncu kendi oyununu analiz eder. X ‘in oynanabileceği 3 ayrı ihtimal bulunmaktadır ve bütün bu ihtimaller ağaçta farklı birer alt düğüm (node) olarak gösterilmiştir.
Ağacın daha alt seviyesinde ise X’in oyananabileceği her ihtimalde, O’nun oyananabileceği ihtimaller gösterilmiştir.
Bu durumda X oynayacak olan oyuncu bütün ihtimalleri görmüş olur ve ilave olarak kendisinin yapacağı son hamleleri aşağıdaki şekilde hesaplar:
Yukarıdaki şekile X’in yapacağı son hamleninde hesaplanışını görüyoruz. Bir minimax ağacının hesaplanması sırasında kaç seviye gidileceğine seviye (ply) ismi verilir. Örneğin yukarıdaki ilk şekildeki minimax ağacımı 2 seviye (ply) bir ağaçken, yukarıdaki son ağacımız 3 seviyeli bir ağaçtır.
Minimax ağacının seviyesinin artması sonucun daha kesin bulunması ve yapay zekamızın başarısını arttırır. Ancak bunun karşılığında bilgisayarın hafızasında (RAM, memory) daha fazla bilginin tutulması gibi bir bedel ödenir. Örneğin satranç veya GO gibi ihtimallerin çok yüksek olduğu oyunlarda seviyenin belirli bir limitin altında tutulması gerekir.
Bilgisayarın yukarıdaki ağaca bakarak bir karar vermesi çok güçtür. Kararın sayısal bir değere oturması için yukarıdaki her hamle durumunu puanlamamız ve bundan sonra bilgisayarın minimax yaklaşımı ile seçim yapmasını istememiz gerekir.
Örneğin bilgisayarın (Burada X olarak oynadığını düşünelim) kazanma durumlarına 1 ve kaybetme durumlarına 0 diyelim.
Bu durumda ihtimaller ve puanlar aşağıdaki şekilde olacaktır:
Yukarıdaki şekilden de anlaşılacağı üzere bilgisayar kazancının en fazla olduğu ihtimali seçmek ister. Basit bir hesapla bilgisayarın kazanç durumlarının toplamlarını ağacın bir üst seviyesine taşıyalım:
Yukarıda ağacın son seviyesinden karar vermemiz gereken pozisyona doğru bir seviye puanlar taşınmıştır. Yani kararın verilmesi gereken pozisyon ağacın kökündeki (en üst seviyesindeki) pozisyondur ve oyun sonu en altta gösterilmektedir. İşte bizde puanlarımızı karara bir seviye yaklaştırıyoruz.
Bu yaklaştırma işlemi sırasında dikkat edilmesi gereken X’in hamle sırasındaki azami (maximum) değerlerin taşınmasıdır çünkü X kendi hamlesinin en çok puan getirdiği durumu almak ister. Ancak yukarıdaki grafikteki son seviyede X’in tek hamlesi olduğu için mecburen (bir seçim yapılmaksızın) puanlar bir seviye yukarı taşınmıştır.
O’nun hamlesinin bulunduğu bir üst seviyede ise bir seçim yapılabilir. Çünkü O hamlesini iki farklı yere yapabilir. Bu durumda O’nun kendisi için en avantajlı yere oynayacağını düşünürsek bu seviyede asgari (minimum) değerlerin alınması gerekir.
Aşağıda O’nun hamlesi için alınmış asgari değerler bulunuyor :
Görüldüğü gibi ihtimallerin en düşük değerleri alınmıştır.
Taşınan bu son seviyeyle artık bilgisayar hamlesine karar verebilir çünkü mevcut durum için hangi hamlenin en kârlı olduğu (max) görülmektedir. Yukarıdaki ağaçta en sağdaki değer olan 1 puanındaki hamleyi yapması durumunda bilgisayar her ihtimalde oyunu kazanmaktadır.
Sonuç olarak alınan değerler aşağıdaki şekilde gösterilebilir:
Yani bir min bir max alınmaktadır ve bu işlem bu şekilde devam etmektedir. Ağacımızın ismi de zaten buradan türetilmiş minimax ağacı olarak geçmektedir. Aynı anlamda farklı başlangıç değerinde maximin ağacı da literatürde geçmektedir. Bu ağaç da tamamen aynıdır ancak kaygı kendi hamleleri üzerine değil rakibin hamleleri üzerine kuruludur.
SORU-75: Brent Algoritması (Brent's Algorithm)
Bilgisayar bilimlerinde özlelikle graf teorisinde (graph theory) kullanılan ve bir döngüyü (cycle) algılamaya yarayan algoritmadır. (cycle detection).
Basitçe tavşan ve kaplumbağa algoritmasından (hare and tortoise algoritm) esinlenmiştir. Floyd algoritması olarak da isimlendirilen tavşan ve kaplumbağa algoritmasından farklı olarak tavşan, kaplumbağanın iki misli değil 2 üzeri adımla hareket etmektedir.
Yani kaplumbağa, tavşan 2′nin bir kuvveti olan adım attığında hareket etmekte, bu zamana kadar beklemektedir.
Örneğin aşağıdaki dairenin Brent algoritması ile nasıl çözüldüğünü görmeye çalışalım:
Başlangıç değeri olarak A düğümünden iki göstericinin (pointer) başladığını kabul edelim. Bu durumda başlangıçtaki tavşan ve kaplumbağanın şekli aşağıdaki gibi olacaktır :
Kaplumbağa tavşanı beklemektedir. Tavşan birer birer hareket etmekte, şayet hareket ettiği değer 2 üzeri bir değer olursa kaplumbağa da hareket etmektedir. Aynı düğüme geldiklerinde daire bulunmuş demektir.
Yukarıdaki şekil için tavşan ve kaplumbağanın dolaştıkları düğümleri sıralayacak olursak:
K T
—
A B
A C (C tavşanın geldiği 2. düğüm olduğu için kaplumbağa hareket eder)
B D
B E (E tavşanın 4. adımda geldiği düğüm olduğu ve ikinin kuvveti olduğu için kaplumbağa hareket eder)
C F
C A
C B
C C (C. tavşanın 8. adımda geldiği düğüm olduğu için kaplumbağa hareket edecektir ancak daire bulunmuştur)
Yukarıdaki örnekte iki gösterici de C düğümünde buluşmaktadır dolayısıyla daire yakalanmıştır.
SORU-76: Tavşan Kaplumbağa Algoritması (Hare and Tortoise Algorithm)
Bilgisayar bilimlerinde veriyi modellemek için kullanılan graflarda bir döngü (cycle) olup olmadığını algılamaya yaramak için kullanılan algoritmadır. Floyd Döngü Yakalama Algoritması (Floyd’s Cycle Detection Algorithm) olarak da geçen bu algoritmaya göre bir yol üzerinde hareket eden iki farklı hızdaki gösterici (pointer) aynı değeri gösterebiliyorsa burada bir döngü bulunuyor demektir.
Tosbağa ve tavşan benzetmesinde ise tavşan, tobağadan iki misli hızlı gitmekte ve dolayısıyla bir zamandan şayet döngü bulunuyorsa mutlaka tosbağayı yakalaması beklenmektedir.
Örneğin aşağıdaki döngüyü ele alalım:
Yukarıdaki döngüyü kaplumbağa ve tavşanın dönüşünü inceleyelim. Başlangış düğümü olarak A’dan başlanıyor olsun.
Yukarıdaki örnekte kaplumbağa ve tavşanın başlangıç durumları ele alınmış. Tavşan kaplumbağadan iki misli hızlıl olduğu için daha ileride başlıyor ve birbirlerine eşit olana kadar sürekli hareket ediyorlar. Yani tavşan 2 kaplumbağa ise 1 hızında hareket ediyor.
Durumları aşağıdaki şekilde sıralanabilir:
T K---D BF CB DD EF F
Görüldüğü üzere tavşan her adımda ikişer atlarken kaplumbağa 1 adım ilerlemekte ve sonunda F düğümünde ikisi de aynı yeri göstermektedir. Buradan sonuç olarak bir döngü olduğuna varılabilir. Yani şayet 2 birim hızla giden ve 1 birim hızla giden iki gösterici (pointer) aynı yerde buluşuyorlarsa bunun anlamı ancak bir döngü olmasıdır.
Yukarıdaki graflar üzerinde kullanılan bu yaklaşım sayı teorisinde de (number theory) kullanılabilir. Örneğin dairesel grup (cyclic group) bulunması veya verilen bir sayı grubunun (dizisinin) dairesel olduğunun anlaşılması da mümkündür.
f(x) şeklinde dairesel olup olmadığı bilinmeyen bir fonksiyon düşünelim.
Kaplumbağa : f(x)
Tavşan : f(f(x))
şeklinde sayılar üretsinler. Bu durumda tavşan bir şekilde kaplumbağaya eşitse dairesel bir gruptan bahsediliyor demektir.
Örneğin
f(x) = 3x + 2 mod 7
olarak tanımlansın. Başlangıç değeri olarak
Kaplumbağa : f(1)
Tavşan : f(f(1))
değerleri ile başlıyor olsun. Bu durumda :
Kaplumbağa :5
Tavşan :3
değerini alacaktır.
Bulunan değerlerin tekrarlanması ile
Kaplumbağa : f(5)
Tavşan : f(f(3))
işlemini yapacak ve bu işlemlerin tekrarı aynı olana kadar devam edecektir.
K T---5 33 04 10 32 01 1
Yukarıda görüldüğü üzere kaplumbağa fonksiyonu bir kere işletirken tavşan iki kere işletmektedir. En nihayetinde aynı değerleri göstermesinden de anlaşılacağı üzere fonksiyonumuz dairesel bir fonksiyondur ve ürettiği sayıları tekrar eder.
Bu yaklaşım, hafızanın verimli kullanılmasını amaçlamaktadır. Yani bir dairenin yakalanması için geçilen bütün düğümler ve ya fonksiyon sonuçları bir yerde tutulup aynı değerden geçilip geçilmediğine bakılabilir. Ancak tahmin edileceği üzere bu işlem en kötü ihtimalde veri yapısında bulunan bütün sayıların ikinci bir yerde tutulması ile sonuçlanacak ve hafıza kullanımı artacaktır.
Tavşan ve kaplumbağa algoritmasında bunun tersine 2 gösterici (pointer) yeterlidir. Ancak bu sefer de işlem süresi uzamaktadır. Yukarıdaki örneklerde de görüleceği üzere aynı dairede birden fazla kere dönülmesi gerekebilmektedir.
Ayrıca karmaşık grafiklerde doğru yola karar verilmesi farklı bir problemdir. Örneğin bir ağaç üzerindeki dairenin yakalanması işleminde ağaçtaki yol ayrımlarının seçilmesi farklı bir problem halini alır.
SORU-77: Meşguliyet (Utilization, Kullanım)
Bilgisayar bilimlerinde sıra (queue) teorisinde, sıradaki bir varlığın ne kadar meşgul edildiğini ölçmeye ve bu ölçüme göre kararlar vermeye verilen isimdir.
İstatistiksel olarak ρ sembolü ile gösterilir ve şayet ρ değeri 1′den büyükse sıranın uzadığı, 1′e eşitse sıranın ne kısalıp ne de uzadığı ve şayet 1′den küçükse sıranın kısaldığı veya sırada kimse kalmadığı sonucu çıkarılır.
Burada ρ değerinin hesaplanması için aşağıdaki formül kullanılabilir:
ρ = λ / μ
Yukarıdaki formülde λ değeri sıraya gelen varlıkların oranını, μ değeri ise sistemin bu sıradaki varlıklara hizmet verme oranını göstermektedir. Yani basitçe sıraya girenler ve sıradan çıkanların oranı olarak düşünülebilir. Daha basit bir ifadeyle talep/arz oranı olarak düşünülebilir.
Basit bir örnek üzerinden anlatmak gerekirse örneğin bir bankada bulunan banka görevlileri ortalama olarak bir müşteriye 1 dakikada hizmet veriyor olsun. Saatte 50 müşterinin geldiği bir bankada meşguliyet aşağıdaki şekilde hesaplanabilir:
Öncelikle arz ve talebi aynı birimlere getirmek gerekir. Yani bankanın arz ettiği hizmet değeri müşteri/saat şeklindeyken, bankanın talep değeri müşteri/saat cinsinden. Bu durumda bankanın arz değerini de müşteri/saat cinsine çevirelim.
Basit bir hesapla 1 dakikada bir müşteriye hizmet verilen bir bankada saatte 60 müşteriye hizmet verilir. Öyleyse bu bankanın meşguliyet oranı (utilization):
ρ = λ / μ
λ = 50
μ = 60
için ρ = 50 / 60
= 0.83
olarak bulunur. Bu durumda bankanın müşterilerine zamanında hizmet verebileceğini ve müşterilerin sıra beklemeyeceğini veya bir sıra varsa zamanla azalacağını yorumlayabiliriz.
SORU-78: Knuth Morris Prat Algoritması (KMP Algorithm)
Knuth-Morris-Prat algoritması bir kelimenin (yada bir metin parçasının) bir metin içerisinde aranmasını sağlayan algoritmadır. Basitçe bu algoritmada bir kelimenin aranan metinde bakılması ve bakıldığı yerde bulunamaması durumunda nerede olabileceği ile ilgili bir bilginin elde edilmesi hedeflenir.
Algoritma aranan kelimenin, aranan metinde bulunmaması durumunda, kelimenin içerisindeki harflerden yola çıkarak birden fazla ihtimali elemektedir.
Klasik bir metin arama işleminde aranan kelime metindeki bütün ihtimallerde denenir. (örneğin doğrusal arama (linear search) bu şekilde çalışır). KMP algoritmasında ise aranan metindeki bütün ihtimaller denenmez. Bu durumu anlamak için bir örnek üzerinden algoritmayı inceleyelim:
KMP algoritmasının çalışması.
Örneğin metin olarak:
ŞABCDŞADEFABŞADI
Aranan kelime olarak da :
ŞADI
için KMP algoritmasının nasıl çalıştığını inceleyelim.
Öncelikle metin ve aranan kelime aşağıdaki şekilde ilk harflerinden karşılaştırılmaya başlanır:
ŞABCDŞADEFABŞADIŞADI
ilk iki harfin tutmasına karşılık 3. ve 4. harfler tutmamaktadır. Algoritma benzemeyen ilk harf olan 3. harfi bulunca geri kalanını benzetmeye çalışmaktan vaz geçer. Ayrıca bakmış olduğu harflerden hiçbirisi aradığı kelime olmadığı için metinde aradığı kelime boyu kadar kayarak yeniden arama yapar.
ŞABCDŞADEFABŞADI ŞADI
Yukarıdaki şekilde 4 harf kayarak D harfinden itibaren arama yapmaya başlar çünkü bir önceki aramada, ilk 4 harf arasında aranan kelimenin ilk harfi olan Ş harfinin bulunmadığını anlamış ve artık bu harflere bakmak gerekmediği sonucuna varmıştır.
- harf olan D harfi ile başlayan metinde, aranan kelime olan ŞADI karşılaştırılmış ve görülmüştür ki metin uymmamaktadır. Ancak aranan metnin bir parçası olan ŞAD kısmı buradaki aranan harfler arasındadır. dolayısıyla algoritma bu sefer 1 harf kaydırarak ve Ş harflerini alt alta getirerek deneme yapar.
ŞABCDŞADEFABŞADI ŞADI
Karşılaştırma işlemi sonucunda ilk 3 harf tutmasına karşılık son harfte problem vardır ve karşılaştırılan harflerden hiç birisi aradığımız kelimenin baş harfi olan Ş ile başlamamaktadır. Bu durumda metinde 4 harf daha hareket edilerek bir sonraki karşılaştırma işlemi yapılır:
ŞABCDŞADEFABŞADI ŞADI
Karşılaştırmada başarı olmamasına karşılık son harf Ş harfidir ve dolayısıyla bu harfi yakalamak için 3 harf daha kaydırılır:
ŞABCDŞADEFABŞADI ŞADI
Bu kez başarı elde edilmiş ve aranan kelime bulunmuştur.
Yukarıdaki örnekte dikkat edilirse toplam 5 karşılaştırma işlemi yapılmıştır. Bu işlem doğrusal arama algoritması ile yapılsaydı 12 karşılaştırma gerekcekti. Bu anlamda KMP algoritmasının daha başarılı olduğu söylenebilir.
Algoritmanın performansı olarak O(n) değeri bulunabilir (n metnin boyutu olarak düşünülürse). Doğrusal arama ile aynı sonucun alındığı algoritma performansı için en kötü durum analizi yapıldığı ve en kötü durumda doğrusal arama ile aynı olacağı unutulmamalıdır.
SORU-79: Aks-i Müfret (Palindrome)
Edebiyatta bir sanat olan aksi müfret, bir kelimenin ya da bir cümlenin baştan ve sondan okunuşunun aynı olması durumudur. Bazı kaynaklarda bedii sanatı olarak da zikredilmektedir.
Örneğin Yasin sûresi 40. ayette (Küllün fi felek) “Aya erişmek güneşe düşmez. Gece de gündüzü geçemez. Her biri bir yörüngede yüzerler.” (Yasin: 40)
Âyetteki harfler şöyledir: ( K L F Y F L K ) ve tersten okunuşu kendisi ile aynıdır.
Genellikle 3 harfli kısa kelimelerde kolaylıkla bulunabilecek olan duruma örnekler: mum, yay, asa, ata … şeklindedir ve baştan ve sondan okunduğunda aynı anlamı verir.
Bir yazının aksi müfret olduğunun anlaşılması için yığın (stack) yapısından faydalanılabilir. Buna göre yazının yarısı yığına konulduktan (push) sonra yazının geri kalanı ile yığından alınan (pop) değerler karşılaştırılır ve aynıysa bu yazı bir palindrome olarak değerlendirilir.
Algoritması:
String oku,
Stringin yarısına kadar karakter karakter push et,
Stringin yarısından sonraki her karakter için bir karakter pop edip karşılaştır,
karşılaştırma sonuna kadar tutuyorsa palindromedur
değilse palindrome değildir.
Palindrome Kontrolü Yapan Kod (Gizem Hanımına yazdığı bu kod için teşekkür ederim)
#include <stdio.h>#include <stdlib.h> void ispalindrome(char* s){ int i=0;int son; for (i=0;i<strlen(s);i++){ if (s[i]==s[strlen(s)-i-1]){ son=1;} else { son=0;} } if (son==1){ printf("ntrue");} else { printf("nfalse");} } int main(void){ ispalindrome("radar"); ispalindrome("kabak"); ispalindrome("yimirta"); getch();}
SORU-80: Atomluluk (Atomicity)
Latince bölünemez anlamına gelen atom kökünden üretilen bu kelime, bilgisayar bilimlerinde çeşitli alanlarda bir bilginin veya bir varlığın bölünemediğini ifade eder.
Örneğin programlama dillerinde bir dilin atomic (bölünemez) en küçük üyesi bu anlama gelmektedir. Mesela C dilinde her satır (statement) atomic (bölünemez) bir varlıktır.
Benzer şekilde bir verinin bölünemezliğini ifade etmek için de veri tabanı, veri güvenliği veya veri iletimi konularında kullanılabilir.
Örneğin veri tabanında bir işlemin (transaction) tamamlanmasının bölünemez olması gerekir. Yani basit bir örnekle bir para transferi bir hesabın değerinin artması ve diğer hesabın değerinin azalmasıdır (havale yapılan kaynak hesaptan havale yapılan hedef hesaba doğru paranın yer değiştirmesi) bu sıradaki işlemlerin bölünmeden tamamlanması (atomic olması) gerekir ve bir hesaptan para eksildikten sonra, diğer hesapa para eklenmeden araya başka işlem giremez.
Benzer şekilde işletim sistemi tasarımı, paralel programlama gibi konularda da bir işlemin atomic olması araya başka işlemlerin girmemesi anlamına gelir.
Örneğin sistem tasarımında kullanılan check and set fonksiyonu önce bir değişkeni kontrol edip sonra değerini değiştirmektedir. Bir değişkenin değeri kontrol edildikten sonra içerisine değer atanmadan farklı işlemler araya girerse bu sırada problem yaşanması mümkündür. Pekçok işlemci tasarımında buna benzer fonksiyonlar sunulmaktadır.
Genel olarak bölünemezlik (atomicity) geliştirilen ortamda daha düşük seviyeli kontroller ile sağlanır. Örneğin işletim sistemlerinde kullanılan semafor’lar (semaphores), kilitler (locks), koşullu değişkenler (conditional variables) ve monitörler (monitors) bunlar örnektir ve işletim sisteminde bir işlemin yapılması öncesinde bölünmezlik sağlayabilirler.
Kullanılan ortama göre farklı yöntemlerle benzer bölünmezlikler geliştirilebilir. Örneğin veritabanı programlama sırasında koşul (condition) veya kilit (lock) kullanımı bölünmezliği sağlayabilir.
SORU-81: İçerik Bağımsız Gramerler için Pompalama Önsavı (Pumping Lemma for Context Free Grammers)
Bilgisayar bilimlerinde bir dilin, içerik bağımsız gramer (context free grammer, CFG) ile gösterilemeyeceğini ispatlamaya yarar. Yani pompalama ön savı sayesinde bir dilin CFG olmadığı ispatlanabilir ancak olduğu ispatlanamaz. Şayet pompalama önsavını geçemyorsa CFG değildir denilebilir ancak geçmesi olmasını gerektirmez.
Pomplama önsavı (pumping lemma) kısaca bir dili aşağıdaki gramere uydurmaya çalışır:
s = uvxyz
Elimizde ispatı ile uğraştığımız L dili olsun ve yukarıdaki s kelimesini bu dilden bir kelime olarak üretelim. Ve bu kelime |vxy| ≤ p, |vy| ≥ 1 (p burada pompalama boyutudur) şartlarını sağlasın. Şimdi bu kelimeyi aşağıdaki şekilde pompayalım:
s = uv ixy iz
Şayet bu pompalama sırasında üretilen i ≥ 0 için kelimelerde L dilindense o halde bu dil içerik bağımsız gramer CFG ile ifade edilebilir, şayet oluşan kelimeler L dili tarafından kapsanmıyorsa bu durumda da L dili, CFG olarak ifade edilemez sonucuna varılabilir.
Örnek:
Pompalama önsavını (pumping lemma) kullanarak aşağıdaki dilin CFG olmadığını ispatlayalım:
L = {aibici | i > 0}
Yukarıdaki dili, tanımımızdaki
s = uvxyz
şekline getirmeye çalışalım ve bu sırada |vxy| ≤ p, |vy| ≥ 1 şartlarını sağlamaya çalışalım. Bu durumda ilk kelimemiz:
s = a1b1c1
olarak yazılacaktır. Bu yazımdan u ve z değerlerinin boş olabileceğini düşünürsek
v= a
x= b
y= c
olarak yazılabilir. Bunun dışındaki şartların hiç birisi s = uvxyz şartını ve s = aibici | i > 0 şartını aynı anda sağlamaz.
Şimdi ilk kelimemizi yazdığımıza göre, kelimemizi pompalayarak çıkan sonuçların yine bu dilde olup olmadığını kontrol edelim:
Yukarıdaki denemede i=1 için yazmıştık, şimdi i=2 için uvxyz değerlerini bulmaya çalışalım.
Böyle bir değer bulunamaz çünkü
s = a2b2c2
değerini ancak
s = uv ixy iz
pompalamasında i=2 yazarak sağlamaya çalışabiliriz ve bu durumda da x değeri olan b değeri 1 tane kalacağı için problem olacaktır. Daha basit bir ifadeyle CFG gösteriminde 3 değerin birden aynı sayıda olması garanti edilemez, ancak iki değer aynı sayıda olabilir. Çünkü yukarıdaki v ve y değerleri pompalandığında artarken x değeri tek değer olarak kalacaktır.
Benzer şekilde
u= a
v=b
y=c
olması durumunda u,
v=a
y=b
z=c
oması durumunda da z değerleri tek kalacak ve diğer harfler pompalanırken dengeyi bozacaktır.
Yukarıdaki durumda L = {aibici | i > 0} dilinin bir CFG gösterimi ile gösterilemeyeceği ispatlanmış olur.
SORU-82: Düzenli İfadelerde Pompalama Önsavı (Pumping Lemma for Regular Expressions)
Bir dilin Düzenli ifadele (Regular expression) olup olmadığının belirlenmesi için kullanılan pomplama önsavıdı (pumping lemma). Basitçe düzenli ifadede olup olmadığı sınanacak bir w dili için (yani L = w için)
w= xyz
şeklinde bir açılım sınanır. Buradaki sınama sırasında aşağıdaki koşulların sağlanması beklenir:
- |y| ≥ 1
- |xy| ≤ p
- bütün i ≥ 0 için, xyiz ∈ L
Yukarıdaki üç şartı da sağlayan bir dil için düzenli ifadedir denilebilir. Ayrıca bu dilin düzenli ifade olabilmesi için yukarıdaki y teriminin üstü olan i değeri istenildiği kadar arttırılabilmeli ve elde edilen sonuç yine düzenli ifade (Regular expression) olmalıdır.
Daha basit bir ifadeyle, bir dilden üretilen bir terim üç parçaya bölündüğünde ortasındaki parça boş olmamalı, istenildiği kadar tekrarlanabilmeli ve çıkan bütün sonuçlar yine aynı dilden olmalıdır.
Örnek:
Pompalama önsavı konusunda en çok verilen örneklerden birisi L = {anbn : n ≥ 0} dilinin bir düzenli ifade olmadığının ispatıdır. Bu örneği beraberce ispatlayalım:
Dilimizi pompalama önsavındaki kalıp olan w = xyz şekline benzetmeye çalışalım. Bu durumda dilimizdeki üretilebilen en kısa terim için w = apbp olduğu söylenebilir ve bu terimi kalıba benzetirken xy için a ve z için b olduğunu söyleyebiliriz. Çünkü y’nin sıfır boyutunda olamayacağını biliyoruz, bu durumda y değeri ya a ya da b olmalıdır. İki durumda birbirinin aynısıdır. Bu durumda
y=a
z=b
olarak kabul edelim.
Şimdi sonucumuzu pompalayalım ve bir sonraki beklentimiz olan xy2z terimini üretelim. Bu durumda çıktımız w = a2b olacaktır. Dikkat edilirse bu yeni terim, dil tanımımız olan L = {anbn : n ≥ 0} tanımı ile çelişmektedir ve bu dilin bir üyesi değildir.
Dolayısıyla L = {anbn : n ≥ 0} dilinin bir düzenli ifade olmadığı veya düzenli ifade olarak yazılamayacağı ispatlanmış olur.
Örnek Çözüm :
Mesut Aydın bey efendinin sorusuna istinaden aşağıdaki çözümü yazıyorum:
Sorunun tam metnini kendileri yorum olarak bu sayfada yazmışlar ancak bir kerede buraya alıntılıyorum:
L dili için (x ∈ (0,1)* ve W=XX^R) Pumping lemma ile regülerliğini araştırın.
Örneğin : x = 011 olsun x^R =110 olur ve w=011110 olur.
Zannediyorum x^R ile ifade edilmek istenen, x teriminin tersi oluyor.
Şimdi bakın böyle bir soruda doğrudan cevap olarak RE (Regular Expression) olamayacağını söyleyebilirim (biraz tecrübe ile biraz da olaya şu şekilde bakmanız gerekiyor) Bunun sebebi, basitçe herhangi bir RE için sadece bir pompalama noktasına izin verilmesidir. Diyelim ki x için değişim belirttiniz, bu durumda x^R ‘nin buna bağlı değişmesi soruda bir şart olarak tanımlanmış, oysaki biz sadece tek noktada pompalama yapabiliyoruz ve dolayısıyla x değişirken x^R şeklinde verilen ikinci kısma müdahale edemiyoruz.
Daha akademik olarak durumu ifade edecek olursak:
Yukarıda verilen W non-regular (düzenli olmayan bir ifadedir).
İspatı : L dilini (L tanımıyla üretilebilecek kelimeler kümesini) öncelikle düzenli ifade kabul edelim (regular expression)
Bu kabul doğruysa, n>1 uzunluğunda bir pompalama uzunluğu bulunmalıdır ki W∈L olsun ve her |w| ≥ n için x = klm yazılabilsin. (Burada klm üç harfini kullandım bunun sebebi aslında yukarıda anlatılan ve yazıda geçen xyz kullanırsak x harfinin sorudaki x ile karışması endişesidir. Soruda x harfi kullanıldığı için (L’den üretilebilen herhangi bir kelime olarak) bu harfi tekrar kullanmadım). Ayrıca bu klm üç harfi için aşağıdaki şartlar da sağlanmalıdır:
- |kl|≤n
- |l|>0
- son olarak klim ∈ L bütün i ≥ 0 değerleri için sağlanmalıdır.
Şimdi yukarıda yazdığımız ve pompalama önsavının tanımındana gelen durumları çürüten bir örnek bulursak, bu L dilinin düzenli ifade olmadığını, olmayana ergi (burhan-ı mütanakis, proof by contradiction) yöntemi ile ispat etmiş olacağız:
Örneğin X = 0n olarak verilmiş olsun. Bu durumda W = 0n 0n olacaktır çünkü 0n teriminin tersi yine kendisidir.
Yukarıdaki örnek W için W ∈ L rahatlıkla denilebilir. Çünkü soruda verilen tanımı sağlamaktadır. Ayrıca dikkat ediniz W terimi X ve X^R terimlerinden mürekkep olduğu için bu terimin boyu her zaman çifttir yani |W| her zaman çift bir sayıdır diyeibliriz. Çünkü X ile X^R kelimelerinin boyu eşittir. Yani bir kelimenin boyu ile tersinin boyu eşit olduğu için bu iki kelimeden oluşan W’nun boyutu tabii olarak çift sayı olacaktır.
Pompalama önsavından öğrendiğimiz üzere, W terimi n adet 0 ile başladığına göre, herhangi bir j sayısı, 0 ≤ j ≤ n şartını sağlamak üzere, pompalama önsavından gelen tanım itibariyle l = oj olur denilebilir. Yani diğer bir deyişle, klm dizilimindeki l teriminin pompalanacağını biliyoruz ve bu terim soruda verilen W ifadesini gösterebilmek için oj olarak seçilmeli ki pompalama neticesinde 0n terimine ulaşılsın (veya 0n terimine ulaşılana kadar pompalansın).
Örneğin klim ifadesi için i = 0 kabul edilirse kl0m = km = 0n-j 0n
olacaktır. Ayrıca bu terimin de bir W terimi olduğunu ve W’nun tanımında bulunan şartları sağladığını söyleyebilmeliyiz.
Ancak, n> n-j (j>0 olduğu için) olduğuna göre ve ayrıca j teriminin tek veya çift olması ile ilgili herhangi bir bilgi olmadığına göre. j teriminin tek olması halinde km teriminin eleman sayısının tek sayı olduğu görülür. Bu durumda tanım itibariyle çift sayıda eleman içermesi gereken W teriminin tek sayıda terim içerebileceği de görülmüş olur. (Bu durumda bir çelişki oluşuyor ve ispatını üzerine kurduğumuz üzere L dilinin RE olması kabulu çökmüş oluyor (contradiction)
Demek ki soruda verilen W terimi pompalama önsavına göre tek sayıda terim içerebilmektedir. Oysaki tanım itibariyle W bu şekilde bir terim asla olamaz.
Soruya bu şekilde cevap verdikten sonra, daha iyi anlaşılması açısından durumu şu şekilde izah edeyim:
Düzenli ifadelerde kullanabileceğimiz ve tekrar ifade eden tek işlemimiz (operator) ne yazık ki kleene yıldızı (kleene star) ismi verilen ve * sembolü ile gösterilen semboldür. Bu sembolün kaç kere tekrar edeceğini bilemeyiz.
Yukarıdaki soruda X için * kullanılması halinde X^R için yıldız kullanılamaz. İkisi için de kullanılırsa farklı miktarlarda tekrarlar olabilir. Bunun için iki veya daha fazla sayıdaki tekrar eden terimin birbirine eşit olması istenen durumlarda (ki bu örnek ve daha önce yazıda verdiğim örnekte de aynı durum söz konusu) RE olarak yazılamayacağını söyleyebiliriz çünkü kleen star kontrolsüz bir operatördür.
Buna karşılık tekrarlı terim sayısı 2 ise ve eşitlik isteniyorsa CFG olarak yazılabilir. Bunun için de CFG konusunda geçen pompalama önsavına (pumping lemma) bakabilirsiniz.
SORU-83: Pompalama Önsavı (Pumping Lemma)
Bilgisayar bilimlerinde dil tasarımı (language design, compiler design) konusunda önemli araçlardan birisidir. Bu önsava (lemma) göre şayet bir dil, bir herhangi bir gruba ( içerik bağımsız dil (context free language) veya düzenli ifadeler (Regular expression) yada farklı bir dil grubu ) dahil olarak kabul ediliyorsa, bu dil ne kadar pompalanırsa pompalansın yine aynı dil grubuna dahil olmalıdır.
Daha açık bir ifadeyle, bir dil sonsuz kümeyi kapsıyorsa (o dil kurallarıyla üretilebelecek sonuçların bir sınırı yoksa) bu dili ne kadar pompalarsak pompalayalım sonuçta yine aynı dil grubundan olmalıdır. Ve ancak bu sayede bu dilden üretilebilen bütün sonuçların aynı dil grubundan olduğu iddia edilebilir.
Yukarıdaki tanımlarda geçen pompalamak işlemi bir dil gramerinin bir kısmının şişirilmesi, arttırılması veya çoğaltılması olarak algılanabilir.
Pompalama önsavı genelde bir dilin iddia edildiği dil grubunda olmadığını ispatlamak için kullanılan bir çelişki ile ispat (proof by contradiction) yöntemidir.
SORU-84: İçerikten Bağımsız Gramer (context free grammer, CFG)
Bilgisayar bilimlerinde, dil tasarımı sırasında kullanılan bir gramer tipidir. Basitçe bir dilin kurallarını (dilbilgisini, grammer) tanımlamak için kullanılır.
Örneğin:
S -> a
Yukarıdaki dil tanımında bir büyük harfle gösterilen (S) bir de küçük harfle gösterilen (a) sembolleri bulunmaktadır. Bu satır, S devamlısının(nonterminal) a sonuncusuna(terminal) dönüştüğünü göstermektedir. Kısaca dildeki kuralları ifade etmek için büyük harfli semboller devamlıları (nonterminal) ve küçük harfli semboller sonucuları (terminal) ifade etmekte, -> ok işareti ise, işaretin solundaki devamlının (nonterminal), sağındaki sembolle gösterilebileceğini ifade etmektedir.
Bir dili içerikten bağımsız (context-free) yapan, o dilin bir belirsiz aşağı sürüklememli otomat (non deterministic pushdown automata) tarafından üretilebilir olmasıdır.
İçerikten bağımsız diller, programlama dilleri olarak sıklıkla kullanılmaktadır, bilinen çoğu programlama dili aslında birer içerikten bağımsız dil özelliğindedir ve bu dillerin kurallarının tanımlandığı gramerlerde içerikten bağımsız gramerlerdir (context free grammer).
Bu anlamda, YACC gibi programlama ortamlarında, bir dil tasarlamak ve içerikten bağımsız kurallar yazark dili tanımlamak mümkündür.
CFG gösterimi ne yazık ki doğal diller (natural languages) için kullanılamaz. Ya da kullanılsa bile bir doğal dilin tamamını kapsayacak bir CFG gösterimi çıkarılamaz. Örneğin doğal dillerde kelimebiliminin (lexicology) bir parçası olarak sıkça kullanılan uyum (agreement) veya atıf (reference) kullanımları CFG ile gösterilemeyen özelliklerdir. Yani daha net bir ifadeyle CFG gösterimi için dildeki anlamların belirli olması gerekir. Çeşitli durumlarda belirsizlik içeren doğal diller için ise bu durum imkansızdır.
CFG tanımı
Temel olarak bir içerik bağımsız gramer dört özellik içermelidir. Bunlar sonlular (terminals), devamlılar (nonterminals), bağlantılar (relation) ve başlangıç sembolu (starting symbol) olarak sıralanabilir. Bir gramerin tanımı sırasında kullanılan bu kümeler aşağıdaki şekilde yazılabilir:
G = ( V , ∑ , R , S)
Bu gösterimdeki grammer (G) , V devamlıları, ∑ sonluları, R bağlantıları, S ise başlangıç sembolünü göstermektedir.
Örneğin
S -> aSb | ab
şeklinde tanımlanan bir dilde:
G= ( {S} , {a,b} , {S -> aSb | ab} , S )
gösterimi kullanılabilir.
SORU-85: İçerikten bağımsız dil (Context Free Language, CFL)
Bilgisayar bilimlerinde bir dilin tasarımı sırasında, içerik bağımsız bir gramer ile oluşturulması durumudur. Basitçe bir aşağı sürüklemeli otomat (push down automata) tarafından kabul edilen dil çeşididir. Bazı kaynaklarda bağlamdan bağımsız dil olarak da geçmektedir.
Örneğin çok meşhur L= {anbn , n>0} dilini ele alalım. Bu dil örneğinin bu kadar meşhur olmasının ve önemli olmasının sebebi bir düzenli ifade (regular expression) ile yazılmasının imkansız oluşu ancak içerikten bağımsız dil ile yazılmasının mümkün olmasındandır.
Şimdi bu dilin aşağı sürüklemeli otomatını aşağıdaki şekilde çıkaraibiliyoruz:
δ(q0,a,z) = (q0,a)
δ(q0,a,a) = (q0,a)
δ(q0,b,a) = (q1,x)
δ(q1,b,a) = (q1,x)
δ(q1,b,z) = (qf,z)
δ(state1,read,pop) = (state2,push)
Yukarıdaki PDA (push down automaton) tasarıımnda dikkat edilirse iki durum (state) arasındaki geçişler ile yukarıdaki dili tasarlamak mümkündür. Bu sayede bu dilin bir içerik bağımsız dil olduğu söylenebilir.
Ayrıca yukarıdaki tanımda kullandığımız ” içerik bağımsız bir grammer ile oluşturulması durumu” ifadesini de açıklayarak buna da bir örnek verelim ve dilimizin ( L= {anbn , n>0} ) CFG (context free grammer, içerik bağımsız gramer) karşılığını aşağıda yazalım:
S -> aSb | ab
Yukarıdaki yazılışta, dilin sonucu ab veya aSb olarak çıkacaktır ancak S devamlısı (nonterminal) bitmek için bir sonuncuya (terminal) ihtiyaç duyacaktır bu değer de yine ab olacaktır.
Sonuçta yukarıdaki gramer ile istenilen uzunlukta sırasıyla a ve b lerden oluşsan dil tasarlanabilir ve üretilen bütün dillerde a’nın sayısı ile b’nin sayısı eşittir.
SORU-86: Dolaylı sıralama (Indirect Sort, Gayrimüstakim sıralama)
Bilgisayar bilimlerinde sıralama işleminin çok büyük veriler üzerinde yapılması durumunda tercih edilen bir sıralama yöntemidir. Basitçe sıralama işleminin doğrudan verilerin yerinin değiştirilemsi ile değil de daha çok bu verileri gösteren gösterici (pointer) veya nesne atıfları (object referrer) ile yapılmasıdır.
Örneğin sistemimizdeki öğrencileri sıralamak isteyelim. Her öğrenci bilgisi de sistemde 1MB kadar yer kaplıyor olsun (kişisel bilgileri ve bağlı olduğu bilgilerin tutulduğunu ve çok yer kapladığını düşünelim). Basit bir sıralama işleminde O(n logn) vakit harcanağını düşünürsek, ortalama 100 elemanlı bir listenin sıralanması için 700 civarı işlem gerekcektir. Bunun anlamı bilgisayarın hafızasında (RAM) 700 MB civarı bir bilginin hareket ettirilmesi demektir.
Bunun yerine her veriyi gösteren birer atıf (pointer, gösterici) bulunsa ve bu atıflar hareket ettirilse (tahminen her birisi öğrencilerin numarasını ve öğrencinin hafızadaki (RAM) yerini tutan yapılardan oluşacağı için birkaç byte’ı geçmeyecektir, kabaca 10byte kapladığını düşünürsek) yapılan işlem, hafızada 7KB civarında verinin hareket ettirilmesi demektir.
İlkine göre oldukça avantajlı olan ikinci durumda, hafızada ekstradan bir bilgi tutulmuştur. Bu ilk başta hafızanın verimsizleşmesi (fazladan veri tutulması) anlamına gelse de aslında yapılan işlemlerin süresini kısaltması ve kopyalama işlemi sırasında açılan ilave hafıza alanlarını azaltması açısından bir kazanımdır.
Sonuçta elde edilen ve gerçek nesneleri gösteren bu ilave listenin kullanılması ile gerçek verilerin hafızada sıralanması da mümkündür. Ancak buna gerek duyulmaz çünkü veriye erişmek için bu ilave liste kullanılabilir.
SORU-87: Harici Sıralama (External Sort)
Bir sıralama algoritmasının tamamının bilgisayarın hafızasına (Memory, RAM) yüklü olmaması durumudur. Yani klasik olarak bir dizi (array) veya bağlı liste (linked list) üzerinde yapılan sıralamaları dahili sıralama (internal sort) olarak isimlendirmek mümkündür.
Harici sıralama klasik sıralamalardan farklı olarak, verinin ancak bir kısmının RAM’de durması durumunda devreye girer. Örneğin hafızamızın 100MB alan ile sınırlı olduğunu ve 100GB veriyi sırlamamız gerektiğini düşünelim. Bu durumda verinin hafızaya sığması mümkün olmayacak ve verinin harici bir alanda (örneğin disk veya ağ üzerindeki bir kaynakta) durması gerekecek.
Harici sıralama algoritmaları verinin bir kısmını sıralayıp sonra hafızadaki verinin yerini değiştirip yeni veriyi sıralamak ve en nihayetinde tüm veriyi doğru sıraya sokmak gibi bir yol izlerler.
Örneğin en çok kullanılan harici sırlama algoritmalarından, harici birleştirme sıralaması (external merge sort) aynen yukarıda anlatıldığı gibi önce verileri parçalara böler, sonra her parçayı kendi içerisinde sıralar ve en sonunda da verileri birleştirir.
Elbette verilerin birleşmesi sırasında, verinin tamamının hafızaya sığmaması söz konusudur. Bu durumda verinin parça parça hafızaya yüklenmesi ve sıralanması gerekir.
Bu yöntem ayrıca paralel ve dağıtık sistemlerde de kullanılabilir.
SORU-88: A Yıldız Arama Algoritması (A Star Search Algorithm, A*)
Bilgisayar bilimlerinde en kısa yol bulmak için kullanılan algoritmalardan birisidir. Örneğin seyyar tüccar problemi (travelling salesman problem, TSP) gibi bir problemin çözümünde kullanılabilir. Benzer şekilde oyun programlamada, oyunda bulunan oyuncuların en kısa yolu bularak hedefe gitmeleri için de sıklıkla kullanılan algoritmadır.
Kısaca bir düğümden (node) hedef bir düğüme (target node) en kısa hangi düğümler üzerinden gidileceğini bulmaya yarayan “en iyi yerleştirme (best fit)” algoritmasıdır.
A* algoritması yapı olarak muteber sezgisel (admissable heuristic) bir algoritma olarak sınıflandırılabilir. Bunun sebebi algoritmasının mesafe hesaplamada kullandığı fonksiyondur:
f(n) = g(n) + h(n)
denklemindeki
f(n) = hesaplama yapan sezgisel (heuristic) fonksiyon.
g(n) = Başlangıç düğümünden mevcut düğüme kadar gelmenin maliyeti
h(n) = Mevcut düğümden hedef düğüme varmak için tahmin edilen mesafe.
Dikkat edileceği üzere f(n) fonksiyonunun sezgisel olma sebebi, bu fonksiyon içerisinde bulunan ve tahmine dayalı olan h(n) sezgisel fonksiyonudur.
Algoritmanın çalışması:
Algoritma yukarıdaki toplama işlemini kullanan oldukça basit bir yapıya sahiptir. Veri yapısı olarak bir öncelik sırası (priority queue) kullanan algoritmada en öncelikli olan düğüm f(n) değeri en düşük olan düğümdür.
- Algoritma her adımda en düşük değeri (Ve dolayısıyla en önemli) düğümü alır (yani bu düğüme gider) ve düğümü sıradan (queue) çıkarır.
- Gidilen bu düğüme göre komşu olan bütün düğümlerin değerleri güncellenir (artık bu düğüme gelmenin bir maliyeti vardır ve dikkat edilirse f(n) fonksiyonu içerisinde bu değer yer almaktadır.)
- Algoritma yukarıdaki adımları hedefe varana kadar (yani hedef düğümü öncelik sırasında (priority queue) en öne gelene kadar) veya sırada (queue) düğüm kalmayana kadar tekrarlar.
Örnek Çalışma:
Algoritmanın çalışması için aşağıdaki örneği ele alalım:
Yukarıdaki grafikte sol üst köşedeki düğümden, sağ alt köşedeki düğüme gitmek isteyelim. Bu durumda f(n) fonksiyonunu oluşturan mesafe değeri, grafik üzerindeki yol maliyetleri olarak hesaplanbilir. Sezgisel fonksiyon olarak, bir düğümün hedefe olan kuş uçuşu mesafesini alalım. Yani iki düğüm arasındaki mesafeyi çizilen yoldan değil de iki düğüm arasındaki cetvel ile ölçülen değer olarak kabul edelim.
Bu durumda düğümlerin sezgisel mesafelerini (heuristic) gösteren graf aşağıdaki şekildedir:
Yukarıdaki bu grafikte kırmızı renkle gösterilen değerler, o düğümden hedef düğüm olan sağ alt köşedeki düğüme olan kuş uçuşu mesafeyi göstermektedir. Bu değerleri kısaca h(n) olarak kabul edebiliriz.
Başlangıç düğümümüz olan sol üst köşedeki düğümden başlayarak iki düğümün değerini hesaplayalım. Başlangıcın altında bulunan düğüm için h(n) = 9 ve g(n) = 4 olmaktadır.
Başlangıcın sağında bulunan düğüm için ise h(n) = 8 ve g(n) = 3 olmaktadır. Buna göre:
f(aşağı) = 9+4 = 13
f(sağa) = 8 +3 = 11
olarak bulunmaktadır. Algoritmamız bu seçimler arasından küçük olan (ve önceliği yüksek olan) sağa gitmeyi tercih eder.
Bu seçimi aşağıdaki şekilde gösterelim.
Yukarıdaki bu seçimden sonra değerler tekrar hesaplanır. Yukarıda yapılan seçime göre düğümlerin değerleri tekrar f(n) = g(n) + h(n) olarak hesaplanırsa:
Yukarıdaki bu grafikte görüldüğü üzere sıramızda (queue) iki düğüm bulunmakta ve iki düğümün değerleri sırasıyla 13 ve 11 olmaktadır.
Bu durumda 11 olan düğümü seçip aşağı doğru hareket ediyoruz.
Yukarıdaki son durumda 13 ve 21 değerleri olan iki düğüm arasında seçim yapıldığında, kısa değer olarak 13 bulunduğu için A* arama algoritmamız bu aşamada geldiği alternatif yoldan vaz geçerek en kısa yolun bu yeni düğüm olabileceğini düşünür ve bu yola gider.
Yukarıdaki şekilde bu yeni seçim yeşil renk ile gösteirlmiştir. Yeni seçim yapıldıktan sonra ulaşılan komşu düğümler 14 ve 21 değerlerine sahiptir. Bu durumda 14 değerine sahip olan daha öncelikli düğüme hareket edilecektir.
Yeni halinde değerler hesaplandığında sonuç düğümüne gidilmesi mümkündür ve ulaşım değeri yukarıdaki dolaşmaya göre daha düşüktür.
Yukarıdaki son durumda hedefe ulaşılmış ve aşağıdaki yol kullanılmıştır. Sonuçta ulaşım için harcanan mesafe toplandığında 4+6+4 =14 mesafesi diğer alternatif olan 3+4+6+8 =21 değerine göre daha kısadır.
Tabi A* algoritması sezgisel bir algoritma olduğu için bu durumu bilmemektedir.
SORU-89: Sezgi Üstü Algoritmalar (Üstsezgisel Algoritmalar, Meta Heuristic Algorithms)
Bilgisayar bilimlerinde kullanılan algoritma tiplerinden birisi de sezgisel algoritmalardır. Temel olarak çalışmalarında kesinlik bulunmayan bu algoritmalar ya her zaman aynı performans ile çalışmaz ya da her zaman sonuç vermeyi garanti etmez ancak yine de problemi iyileştirme (optimisation) için kullanışlı algoritmalardır.
Üstsezgisel algoritmalar ise bu sezgisel algoritmalar üzerinde çalışan bir karar mekanizmasıdır. Yani bir problem için 3 farklı yöntem kullanabileceğimizi ve bu yöntemlerin hepsinin farklı açılardan avantajlı olan sezgisel algoritmalar olduğunu düşünelim. Bu sezgisel yöntemlerden hangilerinin seçileceğinin seçilmesine metaheuristic (sezgi üstü) algoritma ismi verilir.
Günümüzde pek çok alanda çözüm için birden fazla yöntem geliştirilmiş ve bu yöntemler probleme göre iyileştirilmiştir. Ancak bir problem için birden fazla olan çözüm yöntemleri arasında seçim yapma ihtiyacı bu yolla ortadan kaldırılmış olur. Basitçe bir sezgi üstü algoritma bu algoritmalar arasında seçim yapmakta ve en başarılı olanı çalıştırmaktadır.
Algoritma seçimine karar veren bu mekanizma ise çoğu zaman istatistiksel verilere dayanarak çalışmaktadır. Ancak bu algoritmanın da sezgisel olarak yazıldığı durumlar mevcuttur.
Meta Heuristic ve Hyper Heuristic Ayrımı
Mustafa Özgür Bey’in yorumu üzerine bu başlığı ekleme ihtiyacı duydum. Terim ve anlam olarak birbirine çok yakın olan meta heuristic (üst setgisel algoritmalar) ve hyper heuristic (hiper sezgisel algoritmalar) arasındaki ayrım aslında bir probleme çözüm aranılan uzaya göre farklılaşmaktadır. Yani iki yaklaşımda da bir probleme sezgisel olarak (heuristic) çözüm aranmakta fakat iki yaklaşımdaki çözüm aranan uzaylar farklı olmaktadır.
Basitçe sezgi üstü algoritmalarda (meta heuristic) arama işlemi problemin çözüm kümesinde (search space) yapılırken hiper sezgisel (hyper heuristic) algoritmalarda arama işlemi sezgisel uzayda (heuristic search space) yapılır.
Bu anlamda hiper sezgisel bir yaklaşımda problemin çözümünden daha çok hangi sezgisel algoritma ile çözüme daha verimli ulaşılabileceğine karar verilmeye çalışılır. Yani bir problemin birden fazla sezgisel çözümü varsa bunlardan hangisinin daha başarılı olacağına karar verilmesine hiper sezgisel (hyper heuristic) ismi verilir.
Buna karşılık sezgi üstü algoritmalar (meta heuristic algorithms) probleme getirilen sezgisel yaklaşımı bir kara kutu (black box) olarak kabul eder ve çözüm için geliştirilen bu algoritmanın detayıyla ilgilenmez. Tek yaptığı çözümde kullanılan bir yada daha fazla fonksiyonu en verimli şekle getirmeye çalışmaktır. Bu fonksiyonlara hedef fonksiyon (goal function , objective function) ismi verilir.
Bu ayrımı bir şekil ile ifade edecek olursak hiper sezgisel yaklaşım için:
Yukarıdaki şekilde görüldüğü üzere aynı problemde çözüme ulaşmak için kullanılan çok sayıdaki sezgisel algoritmadan hangisinin seçileceğine hiper sezgisel algoritma karar vermektedir. Benzer şekilde birden fazla sezgisel algoritmanın arka arkaya uygulanması gibi durumlarda da karar veren algoritmaya hiper sezgisel algoritma ismi verilir.
Üst sezgisel algoritma ise:
Yukarıdaki şekil çizilebilir. Şekilde görüldüğü üzere üst sezgisel algoritmaya konu olan sezgisel algoritma bir tanedir. Üst sezgisel algoritma bu sezgisel algoritmanın üzerinde çalışarak bu algoritmayı verimli hale getirmeye (optimisation) çalışır. Bunun için de genellikle algoritmanın bir fonksiyonunu (ki bu fonksiyona hedef fonksiyon (goal function, objective funtion) ismi verilir) iyileştirmeye çalışır.
Bu anlamda üst sezgisel algoritmanın konusu sanki giriş ve çıkış değerlerine göre bir kara kutuya (black box) benzetilebilir.
Meta-Hyper Heuristic ( Üst Hiper Sezgisel) algoritmalar
Yukarıdaki farkı araştırırken karşılaştığım bir yaklaşımda hem üst sezgisel hem de hiper sezgisel yaklaşımların birleştirilmiş olduğu çözüm modelleri oldu. Bu yaklaşımda hem kullanılan sezgisel algoritmaların verimlileştirilmesi için hedef fonksiyonlara ( goal function , objective function) müdahale edilmekte hem de bu algoritmalar arasından seçim yapılmaktadır.
SORU-90: En kötü durum analizi (Worst Case Analysis)
Bilgisayar bilimlerinde bir algoritmanın incelenmesi sırasında sıkça kullanılan bu terim çalışmakta olan algoritmanın en kötü ihtimalle ne kadar başarılı olacağını incelemeye yarar.
Bilindiği üzere bilgisayar bilimlerinde yargılamalar kesin ve net olmak zorundadır. Tahmini ve belirsiz karar verilmesi istenmeyen bir durumdur. Bir algoritmanın ne kadar başarılı olacağının belirlenmesi de bu kararların daha kesin olmasını sağlar. Algoritmanın başarısını ise çalıştığı en iyi duruma göre ölçmek yanıltıcı olabilir çünkü her zaman en iyi durumla karşılaşılmaz.
Algoritma analizinde kullanılan en önemli iki ölçü hafıza ve zaman kavramlarıdır. Yani bir algoritmanın ne kadar hızlı çalıştığı ve çalışırken ne kadar hafıza ihtiyacı olduğu, bu algoritmanın performansını belirleyen iki farklı boyuttur.
En iyi algoritma en hızlı ve en az hafıza ihtiyacı ile çalışan algoritmadır. İşte en kötü durum analizi olayın bu iki boyutu için de kullanılabilir. Yani en kötü durumdaki hafıza ihtiyacı ve en kötü durumdaki hızı şeklinde algoritma analiz edilebilir.
Limit teorisindeki master teoremde büyük O ile gösterilen (big-oh) değer de bu en kötü durumu analiz etmektedir. Bu yüzden en kötü durum analizine, büyük O gösterimi (Big-O notation) veya algoritmanın sonsuza giderken nasıl değiştiğini anlatmak amacıyla büyüme oranı (growth rate) isimleri verilmektedir.
Örnek
Bir çok terimli fonksiyonun (polynomial function) big-o değerini hesaplamaya çalışalım. Örnek olarak fonksiyonumuz aşağıdaki şekilde olsun:
f(x) = 3x4+2x2+5
Fonksiyonun üst asimtotik sınırı (asymptotic upper bound), her zaman için fonksiyona eşit veya daha yüksek değer veren ikinci bir fonksiyondur.
Bu durumda, yukarıdaki f(x) fonksiyonu için O(x4) denilmesinin anlamlı, herhangi bir sayı ile x4 değerinin çarpımının, f(x) fonksiyonuna eşit veya daha yüksek üreteceğidir. Bu durum aşağıdaki şekilde gösterilebilir:
f(x) ≤ cx4
Gerçekten de bu değer denenirse, c=4 için aşağıdaki çizim elde edilebilir:
Yukarıdaki mavi renkte görülen fonksiyon 4x4 ve kırmızı renkte görülen fonksiyon da f(x) fonksiyonudur. Görüldüğü üzre x>1 için 4x4 fonksiyonu, f(x) fonksiyonundan büyüktür. Dolayısıyla tanımımıza x>1 koşulu eklenebilir.
Kısaca f(x) = O (g(x))
tanımı, f(x) ≤ c.g(x)
şeklinde yorumlanabilir. Buradaki c değeri herhangi sabit bir sayıdır ve sonuçta elde edilen değer eşit veya daha büyüktür.
Büyük-O (Big-O) gösterimi ile kardeş olan bir gösterim de küçük-o (small-o) gösterimidir. Bu iki gösterim arasındaki temel fark küçük-o gösteriminde asimptotik üst sınır (asymptotic upper bound) fonksiyonunun tamamıncan büyük olmasıdır. Yani büyük-O gösterimindeki eşitlik durumu yoktur.
f(x) = og(x) için
f(x) < c g(x) şartı sağlanmalıdır.
Dolayısıyla f(x) = O (f(x)) tanımı doğru olurken f(x)=o(f(x)) tanımı hatalıdır.
Bazı örnekler aşağıda verilmiştir:
√x=o(x)
Yukarıda görüldüğü üzere x>1 için √x=o(x) doğrudur. Ancak x≥1 durumunda √x=O(x) yazılmalıdır.
log(x) = o(x)
Yukarıdaki şekillerde krımızı ile gösterilen f(x) fonksiyonlarının small-o fonksiyonları mavi renk ile çizilmiştir.
SORU-91: Sezgisel Algoritmalar (Buluşsal Algoritmalar, Heuristic Algorithms)
Bilgisayar bilimlerinde sezgisel (heuristics) bir yaklaşımın problem çözümüne uygulandığı algoritmalardır. Uygulanan yöntemin doğruluğunun ispat edilmesi gerekmez, tek istenen karmaşık bir problemi daha basit hale getirmesi veya algoritamanın tatmin edici bir sonuç bulabilmesidir.
Genel olarak bir problemin çözümü sırasında bilgisayar bilimlerinde iki amaçtan birisi güdülür. Ya problemin çözümü hızlı olmalı ve her zaman için bu çözüm elde edilebilmelidir. Bu sebepten dolayı en kötü durum (worst case) analizi sıkça yapılmaktadır.
Sezgisel algoritmalarda bu güdülen iki ihtimalden birisi göz ardı edilir. Yani ya probleme hızlı bir çözüm üretilir ama problemi her zaman çözeceği garanti edilemez ya da problemi makul bir zamanda çözer ama her zaman aynı hızda çözüleceği garanti edilmez.
Sezgisel algoritmalar gerçek hayatta hergün kullandığımız yaklaşımlardır. Örneğin bir yerden başka bir yere giderken yön duygumuza dayanarak ve yolun bizi nereye çıkaracağını hiç bilmeden hareket etmek ve yol ayrımlarında sezgisel olarak seçim yapmak böyle bir yaklaşımdır.
SORU-92: Kabuk Sıralama (Shell Sort)
Bilgisayar bilimlerinde kullanılan sıralama algoritmalarından birisi de kabuk sıralamadır (shell sort). İsmi Türkçeye kabuk sıralaması olarak çevrilsede aslında Donald Shell isimli algoritmayı ilk bulan kişinin isminden gelmektedir. Algoritma performansı O(n2)’dir. Çalışması aşağıdaki örnek üzerinde anlatılmıştır:
Sıralayacağımız sayılar:
5,7,2,9,6,1,3
olarak verilmiş olsun. Sıralama işlemi için öncelikle bir atlama miktarı belirlenir. Atlama miktarının belirlenmesi için çok çeşitli yollar bulunmasına karşılık en basit yöntem elimizdeki sayıların yarısından başlamaktır. Yani yukarıdaki örnekte elimizde 7 sayı olduğuna göre 3 atlama miktarı ile başlanabilir.
Sırasıyla her sayı kendinden 3 sonraki sayı ile karşılaştırılır ve bu sayılar kendi aralarında sıralanır. Bu sıralamayı daha rahat göstermek için aşağıdaki kolon gösterimi kullanılabilir:
5,7,2
9,6,1
3
Her kolon kendi içinde sıralanınca aşağıdaki sayılar elde edilir:
3,6,1
5,7,2
9
Yukarkıdaki örnekte de görüldüğü üzere sayıları sıralama işleminin 3te biri bitirilmiştir. Ardından atlama miktarı yarıya indirilir (bu örnek için 3/2 = 1 olur)
Bu durumda bütün sayılar tek bir doğrultuda sıralanır.
3,6,1,5,7,2,9
1,2,3,5,6,7,9
Yukarıdaki sıralamada örneğin kabarcık sıralaması (bubble sort) kullanılırsa dizinin orjinal haline göre (yani kabuk sıralamasının 3 atlamalı sıralama işlemi yapılmamış haline göre) çok daha başarılı olduğu görülür.
void shell_sort (int *p, int size){ int i, j, k, temp; for (k = size; k > 1; ) { k = (k < 5) ? 1 : ((k * 5 - 1) / 11); for (i = k - 1; ++i < size; ) { temp = p[i]; for (j = i; p[j - k] > temp; ) { p[j] = p[j - k]; if ((j -= k) < k) break; } p[j] = temp; } }}
Yukarıdaki örnek kodda bir dizinin (p) içerisindeki sayıları kabuk sıralamasına göre sıralayan fonksiyon verilmiştir. Fonksiyon girilen sayıları 5/11 oranında küçülen atlama miktarları ile kabarcık sıralaması kullanarak sıralamaktadır. Okuyucu döngünün içerisinde yapılan ve temp değişkeni kullanılan işlemin bir yer değiştirme (swap) işlemi olduğuna dikkat edebilir.
SORU-93: Sallayıcı Sıralaması (Shaker Sort)
Veri sıralama için kullanılan ve kabarcık sıralamasının (bubble sort) neredeyse aynısı olan sıralama algoritmasıdır (sort algorithm). Kabarcık sıralamasından tek farkı, kabarcık sıralaması tek yönlü olarak kabarcığı hareket ettirirken, sallayıcı sıralaması bir sağdan bir soldan iki yönden de sıralamaktadır. Bu sebeple çift yönlü kabarcık sıralaması (bidirectional bubble sort) ismi de verilmektedir.
Algoritmanın çalışması kısaca aşağıdaki örnek üzerinde anlatılmıştır:
Sıralanacak olan sayılarımız
5,7,2,9,6,1,3
olarak verilmiş olsun. Bu sayıların üzerinden bir kere geçiyoruz ve geçerken aldığımız ikilileri küçük-büyük sırasına sokarak ilerliyoruz:
1. adım : 5,7,2,9,6,1,32. adım : 5,2,7,9,6,1,33. adım : 5,2,7,9,6,1,34. adım : 5,2,7,6,9,1,35. adım : 5,2,7,6,1,9,36. adım : 5,2,7,6,1,3,9
Yukarıdaki 5 adımlık birinci geçişin normal bir kabarcık sıralamasından farkı yoktur ve normalde kabarcık sıralamasında bu işlem devam ederek sıralama işlemi bitene kadar tekrarlanır. Ancak sallama sıralamasında ikinci geçişte tekrar dizinin en solundaki 5 sayısından başlamak yerine, dizinin sonundaki 9 sayısından başlanır be büyükten küçüğe sıralanır:
1. adım : 5,2,7,6,1,3,92. adım : 5,2,7,6,1,3,93. adım : 5,2,7,1,6,3,94. adım : 5,2,1,7,6,3,95. adım : 5,1,2,7,6,3,96. adım : 1,5,2,7,6,3,9
Yukarıdaki geçişe dikkat edilirse ilk geçişin tersine sondan başa doğru kabarcık hareket ettirilmiştir. Özünde kabarcık sıralaması gibi çalışan sallayıcı sıralama algoritması sırasıyla bir soldan bir sağdan sıralama işlemine devam etmekte ve hiç sayı değişmeyene kadar işlemi tekrarlamaktadır. Algoritmanın performansı O(n2) olarak kabarcık sıralaması ile aynıdır. Kodu aşağıdaki şekilde yazılabilir:
do { exchange = 0; for(i = count - 1; i > 0; --i) { if(items[i - 1] > items[ i ]) { t = items[i - 1]; items[i - 1] = items[ i ]; items[ i ] = t; exchange = 1; } } for(i = 1; i < count; ++i) { if(items[i - 1] > items[ i ]) { t = items[i-1]; items[i - 1] = items[ i ]; items[ i ] = t; exchange = 1; } } } while(exchange);/* değişim olmayana kadar devam et */
SORU-94: Linear Programming (Doğrusal Programlama)
Problem çözümünde ve iyileştirmelerde (optimization) kullanılan yaklaşımlardan birisidir. Buradaki amaç bir problemi teşkil eden parametrelerin doğrusal bir formda olması ve problem uzayını doğrusal olarak alanlara bölmesidir.
Doğrusal bir fonksiyon aşağıdaki şekilde yazılabilir:
f(x1,x2,x3, …. , xn ) = c1x1 + c2x2 + c3x3 + … + cnxn
Yukarıdaki fonksiyonun doğrusal olmasının sebebi birinci dereceden olmasıdır. Aynı zamanda bu fonksiyon çok değişkenli (multi variable) bir fonksiyondur. (n adet farklı değişkeni bulunmaktadır ve fonksiyonda x değişkeni ile gösterilmiştir)
Yukarıdaki gösterimi matris haline çevirerek aşağıdaki şekilde de göstermek mümkündür:
cTx (matris çarpımı olduğu için çarpanları veren c matrisinin transpoz’u alınmıştır)
Amacımız bu ifadeyi azami hale getirmektir (maximisation)
Ayrıca bu iyileştirmeyi (optimization) engelleyen durumları (koşulları) da aşağıdaki şekilde ifade etmek mümkündür:
Ax ≤ b
Bu ifadedeki A matris çarpanları olurken b ise bir vektörel değerdir.
Sonuçta bu koşulların her birisi uzayda bir doğru ifade edecek ve bu doğrular ulaşılabilecek sınırları belirleyerek bu sınırlar içerisindeki alanda en iyi noktayı (optimum point) almak isteyeceğiz.
yukarıdaki şekilde 3 ayrı doğrusal denklem ve koordinat eksenleri arasında kalan ve problemimizin çözümü için olası alanı gösteren grafik verilmiştir.
Buna göre denklemimizdeki x değişkenleri 0′dan büyük olmak ve verilen doğrusal denklemlerden küçük olmak zorundadır.
Çözümü bulmak için geliştirilen algoritmalardan en tanınanı simplex algoritması ismi verilen algoritmadır.
SORU-95: Self Organizing Maps (Özdüzenleyici Haritalar)
Özdüzenleyici haritalar, yapay sinir ağlarının özel bir biçimidir ve eğitimleri sırasında gözetimsiz eğtim kullanılmaktadır.
İlk kez Kohonen ismindeki finlandiyalı bilim adamı tarafından geliştirildiği için kohonen haritası (kohonen map) ismi de verilen bu ağlar diğer bütün yapay sinir ağları gibi iki farklı şekilde çalışmaktadır.
İlk çalışma şeklinde sistem kendini eğitmektedir. Bu çalışma şeklindeyken rekabetçi öğrenme (competitive learning) kullanılır.
İkinci çalışma şekli olan haritalama merhalesinde ise ağ, gelen yeni girdiyi doğru hartialamak için çalışır.
Temel olarak çok boyutlu girdilerin (multi dimensional inputs) daha az boyuttaki çıktılara indirgenmesine dayanan çalışma mantığı problemin basitleştirilmesini amaçlayan bir boyut azaltma (dimension reduction) işlemidir.
Yapısal olarak ileri beslemeli ağlara (feed forward networks) örnek olabilecek olan SOM, çok küçük miktardaki nöronlar için k-ortalama (k-means) algoritmasına benzer davranmaktadır. Sayının artması ile SOM’un da farkı ortaya çıkmaktadır.
Algoritmanın çalışması aşağıda anlatılmıştır:
- Ağımızdaki nöronların ağırlık değerlerini rasgele olarak başlatıyoruz
- Giriş vektörlerini alıyoruz. (Sistemdeki hedef vektörlerimiz)
- Haritadaki bütün değerler dolaşılıyor ve :
- Giriş vektörü ile dolaşılmakta olan harita değeri arasındaki mesafe öklit mesafesi (euclid distance) olarak hesaplanır.
- En kısa mesafeye sahip olan düğüm alınır (bu yönteme en uygun (best matching unit , BMU) ismi verilir)
- Bu seçtiğimiz en uygun düğüme komşu olan bütün düğümler güncellenerek girş vektörüne yaklaştırılır. (Aşağıdaki formül kullanılır):
- Wv(t + 1) = Wv(t) + Θ(t)α(t)(D(t) – Wv(t))
- t < λ olduğu sürece 2. adıma dönülerek işlemler tekrar edilir.
Yukarıdaki formüldeki değerler kısaca aşağıda açıklanmıştır:
t = şimdiki adımλ = adım üzerindeki zaman limitiWv = şimdiki ağırlık vektörü (weight vector)D = hedeflenen giriş değeriΘ(t) = komşuluk fonksiyonu (en uygun komşudan ne kadar uzağa gidileceği)α(t) = zamana bağlı öğrenme limiti
Yukarıda verilen algoritmanın pekçok dilde uygulanması mümkündür. Algoritmadan da anlaşılacağı üzere yaşanan sorunlardan birisi zamandır. Algoritma n girdili bir sistem için vektörlerin oluşturulması ve bütün elemanların üzerinden her döngüde geçilmesi zaman problemi çıkarmaktadır. Ancak SOM’un boyut küçültücü özelliği göz ardı edilmemelidir. Bu özelliğin doğru kullanılması ile çok uzun süreler alacak işlemler kısa zamanda çözülebilir.
Ayrıca bu algoritmanın, müteharrik özdüzenleyici haritalar (dinamik özdüenleyici haritalar, dynamic SOM) ismi verilen bir yaklaşımla giriş değerlerinin sabit olmayıp sürekli değişim içinde olduğu örnekler üzerinde çalışan bir uygulaması da vardır.
SORU-96: Sokma Sıralaması (Ekleme Sıralaması, Insertion Sorting)
Sokma sıralaması, programlaması oldukça basit ancak performansı bölme sıralaması (merge sort), hızlı sıralama(quick sort) gibi sıralamalara göre nispeten yavaş bir sıralama algoritmasıdır. Çalışmasını aşağıdaki örnek üzerinden anlatmaya çalışalım:
Sıralanacak olan sayılarımız :
33 44 21 83 56 73 22 olsun. Bu sayıları sıralamaya ilk sayıdan başlıyoruz (yani 33).
ilk geçişte (pass) sadece 33 sayısı sıralanmış oluyor (yani hiçbirşey yapmıyoruz):
33| 44 21 83 56 73 22 ( | işareti o anda kadar sıraladığımız sayıları gösteriyor. Bu işaretin “|” solundaki sayılar sıralanmış kabul ediyoruz. Ve her geçişte bir sağındaki sayıyı alıyoruz)
ikinci geçişte sıralayacağımız sayı 44. 33 ile 44′ü karşılaştırıyoruz 33 küçük dolayısıyla yer değiştirmiyorlar:
33 44 | 21 83 56 73 22
üçüncü geçişte sıradaki sayı 21. 21 ile 44 karşılaştırılıyor ve 21 küçük olduğu için 44 ile yer değiştiriyorlar :
33 21 44 | 83 56 73 22 (geçişimiz henüz bitmedi çünkü 21, 33′ten de küçük:)
21 33 44 | 83 56 73 22 (şimdi 3. geçişi tamamladık ve bir sonraki sayı 83′ü alabiliriz:)
dördüncü geçişte 83 var:
21 33 44 83 | 56 73 22 ( bu geçiş çabuk bitti çünkü 83, 44′ten büyük ve sadece bunu görmemiz durmamız için yeterli sonuçta 56′ya kadar sıralamış olduk)
Beşinci geçişte 56′yı sıralayacağız:
21 33 44 56 83 | 73 22 ( burada 56 ile 83 karşılaştırıldı ve 56 sayısı 83′ün soluna kaydırıldı. Bunun sebebi 56′nın 83′ten küçük olması. Bu geçişte burada bitti çünkü 56, 44′ten büyüktür)
Altıncı geçişte sıra 73′te
21 33 44 56 73 83 | 22 ( sıralamamız yine tek adımda bitiyor çünkü 73, 83′ten küçük ve 56′dan büyük)
Yedinci geçişte 22 sayısını yerleştireceğiz ve adım adım 22′den küçük olan bir sayı görene kadar 22′yi dizide kaydırıyoruz:
21 33 44 56 73 22 83 |
21 33 44 56 22 73 83 |
21 33 44 22 56 73 83 |
21 33 22 44 56 73 83 |
21 22 33 44 56 73 83 |
Sonuçta işaretimiz “|” dizinin sonuna ulaştı ve dizimiz sıralanmış oldu.
Görüldüğü gibi sıralama algoritmasının ismi her seferinde sıradaki sayıyı sıralanmış dizideki uygun yere sokmaktan geliyor.
Sokma sıralamasının (insert sort) performansı O(n2)’dir. Bunun sebebi dizideki eleman sayısı kadar geçiş gerekmesi ve her geçişte en kötü ihtimalle elemsan sayısı kadar kaydırma gerekmesidir. Yani insertion sort’un en kötü durumu tersten sıralı bir listedir. Yukarıdaki örneği düşünecek olursak:
83 73 56 44 33 22 21 sıralamasındaki bir dizi en uzun sürede sıralanan dizidir. Buna karşılık sıralı bir dizi verildiğinde diziye 2n sayıda erişim erişim yeterlidir.
SORU-97: Backus Normal Form (BNF)
Bilgisayar bilimlerilnde genellikle bir dil tanımlamada ve bu dilin gramerini (Dil bilgisini) belirlemekte kullanılan gösterim biçimidir.
Basitçe dil bir dil tanımında başlayarak Terminal (sonuncu) ve Non-Terminal (Devamlı) terimler kullanarak tanılmanmaktadır.
Örneğin aşağıda basit bir örneği verilmiştir:
<dil> ::= <harf>|<imla>
<harf> ::= a|b|…|z
<imla> ::= .| |,|?
Yukarıda bir dil tanımı yapılmış ve bu dilde harf veya imlalar bulunduğu söylenmiştir. Buna göre dilimizdeki harfler a’dan z’ye kadar olup imla işaretleri de “. ,?” işaretleridir. Bir ifadenin (nonterminal veya terminal) alabileceği alternatifler | işareti ile ayrılır. Yani harf ya “a”, ya “b” ya… şeklinde sayılan alternatiflerden birisi olabilir demektir. (aralarında veya (or) bağlantısı varmış gibi düşünülebilir)
Yukarıda da görüldüğü üzere BNF gösteriminde nonterminal’ler (devamlılar) <> işaretleri arasında belirtilmektedir.
Yukarıdaki dilimize aşağıdakine benzer bir ek yapılırsa:
<dil> ::= <harf><dil> |<harf>|<imla>
<harf> ::= a|b|…|z
<imla> ::= .| |,|?
Yukarıda fark edilsin diye koyu gösterilen ek marifetiyle dilimizde bir dögü elde edilmiş ve bu sayede dilimizdeki harfler istenildiği kadar tekrar edilmiştir. Yani ilk örnekte dilimizde sadece tek bir harf veya tek bir imla işareti bulunabilirken yeni eklentimiz ile dilimizde istediğimiz kadar harf bulunudurabiliriz. Elbette bu durumun bir harf veya imla ile bitmesi gerekmektedir.
BNF ayrıca döngü miktarını belirlemek için de {}ij sembollerini kullanır. Bu gösterim i’den j’ye kadar tekralanacağı anlamındadır.
SORU-98: Buket Sıralaması (Bucket Sort)
Buket sıralması veya kutu sıralaması (bin sorting) isimleri verilen bu arama yöntemi öncelikle arama yapılacak olan sayıları (veya anahtarları) buketlere böler. Daha sonra her buketi kendi içinde sıralar (veya buket sıralama algoritmasını özyineli olarak (recursive) buketlerin üzerine uygulamaya devam eder)
Buketler sıralandıktan sonra birleştirme aşamasına geçilir ve buketler birleştirildiğinde sıralama işlemi tamamlanmış olur.
Bu sıralama çeşidine benzeyen ve aynı mantığa sahip olduğunu söyleyebileceğimiz sıralama algoritmaları:
- Taban sıralama (radix sort) : Her taban değeri bir buket olarak düşünülebilir
- Histogram sıralaması (histogram sort) : Her sayının frekansı ayrı bir buket olarak düşünülebilir
- Postacı Sıralaması (Postman sort): Bir postahanedeki mektupların sıralanması sırasında ilk harflerine göre mektupların sınıflandırılması mantığına dayanarak yapılan sıralamadır ve buradaki ilk harflerden oluşan kutular birer buket olarak düşünülebilir.
Buket sıralaması algoritmik olarak aşağıdaki şekilde adımlarla anlatılabilir:
- sıralanacak sayılar buketlere ayrılır
- buketler kendi içlerinde sıralanır
- buketler bir dizi üzerinde birleştirilirler
Bir algoritmada sıra bekleyen işlere bir türlü sıra gelmemesi durumudur. Teorik olarak sıradaki her işe birgün sıra gelecektir ancak fiiliyatta bu bir türlü gerçekleşmeyebilir.
Bu tip problemler genelde öncelik tanımlanmış olan algoritmalarda çıkar.
Şöyle bir örnek düşünelim, elimizde uzunlukları 4,5,6 olan işler olsun ve en kısa işi tercih eden bir algoritmamız olsun (Shortest Job First, SJF) . Algoritmamız en kısa olan 4 uzunluğundaki işten başlayacaktır. Ve diyelim ki 4 biter bitmez veya henüz bitmeden uzunluğu 3 olan başka bir iş gelsin. Algoritmamız 5 uzunluğundaki iş yerine daha kısa olan 3 uzunluğundaki işe öncelik verecektir.
Bu süreç böylece sonsuza kadar gidebilir. Yani henüz 5 ve 6 uzunluğundaki işlere sıra gelmeden hep daha kısa olan işlerin gelerek önceliği alması ve 5 ve 6 uzunluğundaki işlere hiçbir zaman sıra gelmemesi söz konusudur.
SORU-100: İlk Gelen Çalışır (First Come First Serve, FCFS, FIFO)
Bilgisayar bilimlerinin çeşitli alanlarında kullanılan bir yaklaşımdır. Bu yaklaşıma göre bir kaynak veya bir isıraya ilk gelenin ilk önce işini bitirerek çıkması hedeflenir.
Örneğin CPU Scheduling (İşlemci zamanlama) problemi sırasında işlemciye gelen işlemlerin hangi sıra ile çalışacağı bu algoritmaya göre belirlenirse ilk gelen iş bitmeden ikinci iş başlayamaz.
Bir sıra (queue) için benzeri durum düşünülürse sıraya ilk giren ilk çıkar (first in first out, FIFO fifo)
İşlemci zamanlama (CPU Scheduling) algoritması olarak kullanılmasını aşağıdaki örnek üzerinden anlamaya çalışalım:
Örneğin işlemciye aşağıdaki işlemler verilen sıra ile gelmiş olsunlar ve yanlarında verilen zaman kadar işlemcide çalışarak bitecek olsunlar:
|
İşlem |
CPU Zamanı |
|
A |
10 |
|
B |
15 |
|
C |
7 |
Yukarıdaki bu işlemlerin çalışma sırası ve zamanları aşağıda verildiği şekildedir:
|
Zaman |
İşlem |
Çalışma |
Kalan |
|
0 |
A |
10 |
0 |
|
10 |
B |
15 |
0 |
|
25 |
C |
7 |
0 |
Görüldüğü üzere kesintisiz (nonpreemptive) bir zamanlama algoritması olan FSFC algoritmasında bir işlem, işlemci tarafından kabul edildikten sonra bitene kadar başka bir işlem araya giremez.
Okuyucu bu çalışma tablosunu round robin algoritmasındaki tablo ile karşılaştırarak farkı daha iyi anlayabilir.
SORU-101: Sığ Öncelikli Arama (Breadth First Search , BFS)
Bir ağaç dolaşma algoritmasının (tree traverse algorithm, tree traversal) ilk önce aynı seviyede bulunan komşularını araması durumudur.
Örneğin aşağıdaki ağacı ele alalım:
Ağacı dolaşma sırlaması örneğin 5,7,8,3,2,1,9 şeklindeyse bu dolaşmaya sığ öncelikli arama ismi verilebilir.
Bu arama sıralamasında, dolaşma sıralaması aşağıdaki ihtimallerden birisi olabilir:
NLR : Node Left Right (Düğüm Sol Sağ)
NRL : Node Right Left (Düğüm Sağ Sol)
Yani öncelikle düğüm sonra altındaki üyelere hareket edilir.
Derin Öncelikli Arama (depth First Search) algoritma tipine göre ise :
LRN : Left Right Node (Sol Sağ Düğüm)
RLN : Right Left Node (Sağ Sol Düğüm)
RNL : Right Node Left (Sağ Düğüm Sol)
LNR : Left Node Right (Sol Düğüm Sağ)
ihtimallerinden birisi tercih edilebilir. Buradaki fark ilk bakılan düğümün, mevcut düğümün altında olan bir düğüm olmasıdır. Yani sığ öncelikli aramada olduğu gibi aynı seviyedeki düğümlerden önce alt seviyedeki düğümlere bakılır.
SORU-102: Durma Problemi (Halting Problem)
Problem kısaca bir programın bir zaman sonra durup durmayacağının belirsizliği üzerine tartışmadır.
Yani basitçe elimizde bir program ve bu programın parametresi olsun (programa verilebilen bir girdi). Programın bitip bitmeyeceğini bilemeyiz.
Peki bunu nasıl ispatlarız? Burada ispat için olmayana ergi (proof by contradiction) kullanılabilir. Önecelikle problemimizi modelleyelim:
D(P,G) : P, programının verilen G girdisi ile durup durmayacağı olsun.
Buna göre D(P,G) sonucunda ya durur ya da durmaz sonuçlarından birisi çıkacak.
Biliyoruz ki bir programın kendisi de bir girdidir. En azından programın kendisi bir bilgidir ve bu bilginin yazıldığı dilde bir metin olması ve bir veri şeklinde gösterilmesi mümkündür. Öyleyse D(P,P) gösterimi bir programa kendisinin girdi olarak verilmesi durumudur. Basitçe bir programı girdi alan bir program var ve bu program girdi olarak aldığı programın bitip bitmeyeceğine karar verecek şeklinde düşünülebilir. Çünkü yapmak istediğimiz tam da budur. Bir programın bitip bitmediğine karar veren bir program bulmak istiyoruz, öylesyse acaba bu bulmaya çalıştığımız ve bir programın bitip bitmediğine karar veren programın kendisinin bitip bitmeyeceğine aynı program karar verebilir mi?
D(P,P): kendisinin bitip bitmeyeceğine karar vermeye çalışan programımız olsun.
Şimdi olmayana ergi (proof by contradiction) kullanılacağı için D(P,P)’nin terisini almalıyız. Öyle bir T(P) tanımlayalım ki üzeirnde çalıştığımız P programı için D’nin tersi olsun ve D(P,P) durur dediğinde T(P) durmaz, D(P,P) durmaz dediğinde ise T(P) durur desin.
Şimdi artık çelişkimizi ortaya koyabiliriz. D(T,T) şayet sonuç olarak durmaz çıkarırsa tanımı itibariyle biliyoruz ki T(T) durur çıkarmalıdır. Tersi durumda D(T,T) şayet sonuç olarak durur çıkarırsa, T(T) durmaz çıkarmalıdır. Buradaki iki durumda da çelişki vardır çünkü T girdisi D(T,T) işleminde şayet durur çıkarıyorsa T(T) işleminde de durur çıkarmalıdır. (Tanımı itibariyle T ya durur ya durmaz, birisinde durur diğerinde durmaz çıkarması bir çelişkidir). Tersi durum olan D(T,T) için durmaz çıkarıyorsa T(T) için de durmaz çıkarmalıdır. Ancak görüldüğü üzere bu sonuçların tam tersi çıkarılmıştır. Bu durumda bir çelişkidir.
Sonuç olarak D her zaman doğru sonuçları veremez. Bunun anlamı durup durmayacağına karar verilebilen bir D yazılamaz demektir.
Basit Anlatımı
Çok saygı duyduğum bir hocam tarafından, burada yazılanların arasındaki bağlantıda problem olduğu söylendi . Bunun üzerine olayı çok daha basit bir şekilde anlatmaya çalışıyorum.
Durma problemi, bir programın durup durmayacağının asla bilinememesidir.
Yani hiçbir zaman bir program yazıp, bütün programların bitip bitmeyeceğine karar veremeyiz, böyle bir program yazılamaz.
Bunu anlamanın en kolay yolu, basit bir öz çelişkiden (self contradiction) geçer.
Örneğin bir program yazıp, programların bitip bitmeyeceğini (durup durmayacağını, halt) bulmayı hedeflediğimizi düşünelim. Ve bu programa T ismini verelim.
Dolayısıyla T(P) gibi bir yapı olacak ve T programımız, P programını parametre olarak alıp, bu programın bitip bitmeyeceğini söyleyecektir.
Şimdi öz çelişki (self contradiction) oluşturan duruma bakalım ve soralım acaba T(T) ne döndürür?
Yani programların bitip bitmeyeceğini söyleyen T programını kendisine parametre olarak verirsek ne olur?
Diğer bir deyişle, bir program yazıp, programların bitip bitmeyeceğini bulmak istiyoruz, peki bu program kendisinin bitip bitmeyeceğini söyleyebilir mi?
Elbetteki hayır.
Dolayısıyla bir programın bitip bitmeyeceğini bulan başka bir program yazılırsa, bu program en az kendisinin bitip bitmeyeceğini söyleyemeyecek ve dolayısıyla bütün programların bitip bitmeyeceğini söyleyebilecek bir program yazılamayacaktır.
SORU-103: Tersine Koyarak İspat (antitez, Contraposition)
Bilgisayar bilimlerinde de kullanılan ispat yöntemlerinden birisi bir önermenin tersini ispatlayarak önermenin doğruluğunu göstermektir.
Doğrudan ispat yöntemlerinde p -> q zinciri kullanılmaktadır. Bu yaklaşımda ise ¬p -> ¬q yaklaşımı ile iki önermenin de tersi alınır ve birbirini izlemesi gereken önermeler oldukları gösterilir.
Basit bir örnek ile durumu inceleyelim. Mesela n gibi bir tam sayı için şayet 3n + 2 işleminin sonuuc tek sayı ise n tek sayıdır denilebilir. Bu iddiayı tersine koyarak ispatlayalım.
Öncelikle doğrudan ispat yöntemi ile ispat yapmaya çalışalım. Biliyoruz ki 2k+1 gösterimi bir tek sayı gösterimidir. Şayet 3n+2 tek sayı ise 3n+2 = 2k +1 olduğu bir n ve k ikilisi bulunabilmelidir. Bu eşitlik basitleştirilince :
3n + 1= 2k gibi bir sonuca varılır ki bu eşitlikten bir yorum ve çıkarım yapmak mümkün değildir.
Doğrudan ispat yönteminin başarısızlığı üzerine dolaylı ispat yöntemlerinden tersine koyarak ispat yöntemini deneyelim:
Öncelikle önermenin tersini alalım. Buna göre 3n+2 tek sayı ise n de tek sayıdır ifadesinin tersini alıp n çifst sayıdır önermesi ile çıkalım. Çift sayı olmak demek n = 2k gibi bir ifadenin bulunması demektir.
3n + 2 denkleminde n yerine 2k koyulursa 3(2k) +2 = 6k +2 = 2 ( 3k + 1) bu sonuca göre 3n+2 değeri çift olmalıdır (çünkü 2 ile çarpılan bir değer çifttir). Oysaki bu durum olamaz çünkü başlangıçta bu değerin tek olduğunu kabul ederek başladık. Dolayısıyla ters durum ispatı göstermiş oldu ki 3n+2 tekse ve n çiftse bir çelişki olmaktadır, o halde 3n+2 tekse n de tektir.
SORU-104: Doğrudan İspat (Direct Proofing)
Bilgisayar bilimlerinin pekçok alanında da kullanılan ispat yöntemlerinden en basitidir. Bu yönteme göre ispatlanmak istenen durum genelde p->q şeklinde bir önermenin (kaziye) ispatının diğer bir önermeyi (kaziyeyi) gerektirdiği bir dizilimdir ve birisinin ispatı diğerini gerektirir.
Örneğin: n sayısı tek sayı ise n2‘nin de tek sayı olduğunu ispatlayalım.
Öncelikle biliyoruz ki bir sayının tek sayı olması 2k+1 şeklinde yazılabilmesidir. Yani 2k+1 formülündeki k yerine hangi tam sayı konulursa konulsun sonuçta elde edilen değer tek sayıdır.
Şimdi n tek sayı ise n için 2k+1 yazılabilir o halde n = 2k+1 -> n2 = (2k+1)2 = 4k2+4k+1 = 2(2k2+2k) + 1
biliyoruz ki 2k2 + 2k bir tam sayıdır ve diyelim ki bu tam sayı ş olsun, ş = 2k2 + 2k ise
n2 = 2(k2+2k)+1 değeri yerine 2(ş) +1 yazabiliriz. Buradaki ş bir tam sayı olduğuna göre elde edilen n2 değerinin tam sayı olduğu gösterilmiş olur çünkü 2ş+1 her zaman tam sayı verir.
Örnekler (ibrahim beyin talebi üzerine ekliyorum)
Yorum kısmında ibrahim bey örnek istemişler bunun üzerine bir iki klasik ve literatürde sık rastlanan örneği burada açıklıyorum. Öncelikle biraz daha kolay anlaşılabilir günlük örneklerle başlayalım. Aristo mantığının da temeli olan basit önermeleri ele alalım.
Örneğin “her sabah güneş doğar” gibi bir kaziyeyi (önermeyi) ele alarak başlıyalım. Bir kişi saatine bakıp 6.00 olduğunu görüyorsa. Saat 6.00 şayet sabah olarak kabul ediliyorsa, öyleyse güneş doğacaktır.
Şayet yukarıdaki zincirde ilk önerme yani “her sabah güneş doğar” önermesi doğruysa ve diğer zincirdeki bağlantılarda doğruysa gerçekten de güneş doğacaktır ve böylelikle güneşin doğacağı ispatlanmış olur.
Yukarıdaki örnekte de görüldüğü üzere doğrudan ispatlarda amaç p -> q , yani p doğruysa q da doğrudur şeklinde bir bağlantı bulabilmektir. Bu bağlantının ardından p ispatlanır yada doğru kabul edilir ve q bu vesileyle ispatlanmış olunur.
Farklı bir örneği ele alalım. Birinci önermemizde (kaziye) iki açının toplamının 90 derece olduğunu söyleyelim (buna p diyelim). İkinci kaziyede ise bu iki açının mütemmim (bütünleyen açılar, tümleyen açılar, complementary) olduğunu söyleyelim. Bu ikinci kaziyeye de q diyelim.
Yukarıdaki sistemde şayet ilk kaziye doğruysa ikincisinin de doğru olması gerekir. Yani doğrudan ispatın gereği olarak p -> q bağlantısı bulunur. Burada genelde ilk kaziyeye şart (condition) ikinci kaziyeye ise faraziye (hipotez, varsayım, hypothesis) ismi verilir. Yani tam anlamıyla ikinci kaziyenin doğruluğunu farz etmek için birinci kaziye şarttır.
Yine ibrahim beyin talebine dönecek ve farklı bir örnek çözecek olursak, bu sefer örnek olarak aşağıdaki ispatı ele alalım:
r gibi bir sayının, bir çok terimlinin (polinom) kökü olarak kabul edilebilmesi için p(r) = 0 olması gerekir.
Şimdi ispatını yapmak istediğimiz faraziyeyi ortaya atalım:
Şayet r1 ve r2, p(x) = x2 + bx +c çok terimlisinin (polynom) ayrı iki köküyse, r1 +r2 = -b ve r1 r2 = c olduğu söylenebilir.
İspatı:
Öncelikle p(x) çok terimlisini çarpanlarına ayıralım (r1 ve r2 birer kök olduğuna göre)
p(x) = (x – r1) (x – r2)
sağ tarafta işlem yaparsak
p(x) = x2 – (r1 + r2) x + r1 r2
olarak bulunur. Bu çok terimliyi ilk yazdığımız haldeki çok terimli ile taraf tarafa eşitlersek sonuçta ispatlamak istediğimiz durumun doğruluğu ortaya çıkar
x2 – (r1 + r2) x + r1 r2 = x2 + bx +c
dolayısıyla r1 +r2 = -b ve r1 r2 = c ‘dir denilebilir.
Yuakrıda görüldüğü üzere ispatın yapılabilemesi için r1 ve r2, kök olmalıdır. Yani ikinci kaziyenin ispatı için ilki şarttır.
SORU-105: Taban Sıralaması (Radix Sort)
Hane sıralaması, radiks sıralaması veya kök sıralaması isimleri de verilebilen sıralama algoritmasına göre sıralanacak olan değerler hanelerine (digits) göre sıralanır. En değersiz haneden (Least significant digit) en değerli haneye (most significant digit) doğru sıralama işlemi yapılır. Yani örneğin en fazla 4 haneli sayıların bulunduğu bir sayı kümesinde en değersiz hane 1′ler hanesi en değerli hane ise binler (1000) hanesidir.
Taban sıralama algoritmasının en basit hali (iyileştirilmiş (optimized) halleri ileride anlatılacaktır) aşağıdaki örnekte gösterilmektedir:
- Sıralanmamış sayılarımız:170, 45, 75, 90, 2, 24, 802, 66
- İlk haneye göre (1′ler hanesi) sıralanmış hali:170, 90, 2, 802, 24, 45, 75, 66Yukarıdaki sıralama ile ilgili önemli bir nokta aynı 1′ler değerine sahip olan sayıların ilk listedeki sıralarına göre kalmış olmalarıdır. Yani 1. adımdaki listede 802, 2′den önce geliyor olsaydı 2. adımda da bu sıralama korunacaktı.
- Sıradaki hane olan 10′lar hanesine göre sıralanmış hali:2, 802, 24, 45, 66, 170, 75, 90
- Son haneye (100′ler hanesine göre) sıralanmış hali :2, 24, 45, 66, 75, 90, 170, 802
Yukarıdaki bu sıralama işleminde her aşamada bütün sayı kümesi üzerinden bir kere geçilmesi gerekmektedir. (yani n sayılık bir küme için her aşam n adım gerektirir). Bu işlemin ilave bir hafıza kullanılması ile daha da hızlı çalışması mümkündür.
Örneğin 10′luk sayı tabanında her sayıdan birer tane bulunması halinde her hane için 10 ihtimal bulunur. Bu her ihtimalin ayrı bir hafıza bölümünde (örneğin bir dizi veya bağlı liste) tutulması durumunda sıralama işlemi en büyük hane sayısı * n olmaktadır.
Örneğin 3 haneli sayılar için O(3n) ~ O(n) olmaktadır. Bu değer zaman verimliliği (time efficiency) arttırırken hafıza verimliliğini (memory efficiency) azaltmaktadır.
SORU-106: Doğrusal Arama (Linear Search)
Sequential Search (Sıralı arama) ismi de verilen bu arama tam anlamıya bir veriyi, arananlara teker teker bakarak aramaktır.
Yani aşağıdaki sayılar arasında 15 sayısını aramak için:
4 6 12 8 5 15 25 34
Teker teker bütün sayılara bakılır. Örneğin baştan başlanarak 4 aranan sayı mı? değil 6 aranan sayı mı değil 12 aranan sayı mı değil şeklinde bütün sayılar alınır ve aranan sayı bulunana kadar devam eder.
Örnekten de anlaşılacağı üzere n sayılık bir kümede arama işlemi yapılırken, aranan sayının bu kümede hiç bulunmaması durumu ancak n sayının tamamına bakılarak olabilir. Dolayısıyla algoritmanın karmaşıklığı (complexity, growth rate, big-oh) O(n) olmaktadır.
SORU-107: Yerleştirme Algoritmaları (Fitting Algorithms)
Bilgisayar bilimlerinde kısıtlı bir alanın verimli kullanılması için geliştirilmiş algoritmalardır. Örneğin sınırlı bir hafıza ( RAM ) içerisine en verimli şekilde programları yerleştirmek, işletim sistemleri için bir problemdir. Benzer problemlerle gerçek haytta da sıkça karşılaşılmaktadır. Örneğin bir deponun verimli kullanılması veya bir kamyonun verimli yüklenmesi veya haftalık bir ders programına derslerin verimli yerleştirilmesi gibi.
Bu problemlerin çözümü için genelde bin packing ismi verilen bir algoritma kullanılır. Bu yazının amacı bin packing algoritması da dahil olmak üzere genel olarak izlenen yerleştirme stratejilerini açıklamaktır.
Temel olarak bir yerleştirme işleminde şu 3 yöntemden birisi izlenebilir
- İlk bulunan yere yerleştirme ( First fitting )
- En iyi yerleştirme ( best fitting )
- En kötü yerleştirme ( worst fitting )
Bu yöntemler isimlerinden de anlaşılacağı üzere kısaca:
Yerleştirmek istediğimiz şeyi ( hafızaya yerleştirilecek bir program, depodaki bir kutu gibi ) ilk bulduğumuz boş yere yerleştiriyorsak buna first fitting, en az boş yer kalacak şekilde bir yer arıyor ve yerleştiriyorsak buna best fitting şayet yerleştirdikten sonra en fazla boş yer kalacak yere yerleştiriyorsak buna da worst fitting ismi verilir.
Veri iletişimi sırasında veriye erişmek için yapılan her bir erişim işlemine verilen isimdir. Örneğin veri diskte veya hafızada duruyor olsun, veriye erişmek için yapılan her bir hafıza veya disk erişimine sonda ismi verilir.
Basit bir dizide veriyi aradığımızı düşünelim. Örneğin dizimiz:
int a[] = { 2 ,3 ,8 ,7};
olarak verilmiş olsun. Örneğin doğrusal arama (linear search) kullanarak dizinin ilk elemanından son elemanına kadar diziyi taradığımızı düşünelim. 8 sayısını bulmak için sırasıyla 2,3 ve 8 sayıları diziden okunmalıdır. Bu durumda toplam 3 sonda işlemi (probing) yapılmış olur.
SORU-109: Brent Yöntemi (Brent's Method)
Bilgisayar bilimlerinde dosya yönetiminde özetleme (hashing) için kullanılan bir yöntemdir. Bu yönteme göre ekleme sırasında bazı değişiklikler ile yerleştirilen kayıtların arama hızını arttırmak ön plandadır. Özet tabloya (hash table) yerleştirilen bir kayıdın çeşitli durumlarda yeri değiştirilerek okuma zamanının arttırılması hedeflenir. İki ayrı zincir tutmaktadır.
- Birincil sonda zincirinde (primary probe chain) sayının aranması ve yerleştirilmesi için gereken sıra tutulur.
- İkincil sonda zincirinde (secondary probe chain) ise sayının yer değiştirmesi için gereken sıra tutulur.
Çalışmasını daha iyi anlamak için aşağıdaki örnek üzerinden yöntemi anlamaya çalışalım:
Eklenmek istenen sayılar : 27, 18, 29, 28, 39, 13, 16 olsun.
Ekleme sırasında kullanılacak olan öze fonksiyonlarımız ( hashing function):
H1(anahtar) = anahtar mod 11 (anahtarın 11′e bölümünden kalan değer)
H2(anahtar) = bölüm ( anahtar/11 ) mod 11 (anahtarın 11′e bölüm değerinin tekrar 11′e bölümünden kalan değer)
İlk 3 sayı herhangi bir çakışma olmadan özet tablomuza koyulabilirler. Bu sayılardan sonra tablomuz:
Yukarıdaki şekilde olur. Burada koyu renkle gösterilen anahtarlar değiştirilmiştir.
Örneğin ilk durumda 27, 18 ve 29 sayıları problemsiz bir şekilde koyulur. Ardından gelen 28 ve 39 sayısı çakışır (collision). İkisi de 6 adresine konulmalıyken sırasıyla önce 28 daha sonra da 39 bu adrese konulur. En son durumda 6. adreste 39 bulunur. 28 değeri ise ikinci özetleme fonksiyonuna (hashing function sokulur. H2(28) = 2 olduğu için 6+2 = 8. adrese 28 değeri taşınır.
Ardından gelen 13 problemsiz bir şekilde 2. adrese koyulurken, 16 sayısı için yine çakışma söz konusudur.
16 sayısı 5. adree koyulduktan sonra bu adreste bulunan 27 sayısı taşınmaya başlanır. H2(27) = 2 olduğu için 2şer atlanarak uygun yer aranır. Önce 5+2 = 7 kontrol edilir, dolu olduğu için 5+2+2 = 9 kontrol edilir dolu olduğu için 5+2+2+2 = 11 mod 11 = 0 kontrol edilir ve boş bulunduğu için bu adrese sayı yerleştirilir.
Görüldüğü üzere brent metodunda (brents method) her zaman son gelen değer adreste tutulmakta ve adreste bulunan eski değer hareket ettirilmektedir. Bu anlamda dinamik metot olarak sınıflandırılabilecek brent yönteminde, EISCH yöntemi, LISCH yöntemi veya doğrusal sondalama (linear probing) yöntemlerinden farklı olarak sayılar ilk konuldukları yerden farklı yerlere taşınabilirler.
SORU-110: Ara Değer Araması (Interpolation Search)
Bilgisayar bilimlerinde sıralı bir veri kümesi üzerinde arama yapmak için kullanılan yöntemlerden birisidir. Buna göre arama sırasında aşağıdaki formül kullanılarak orta nokta bulunur:
orta = sol + ((x-a[sol]) * (sag – sol) ) / ( a[sag] – a[sol])
Yukarıdaki sol ve sag ile ifade edilen değerler arama yapılan alandaki aralığın başlangış ve bitiş indisleridir. a dizisi ise arama yapılan dizinin ismidir. x değeri ise aranan değer olarak kabul edilir. Buna göre aşağıdaki sayısal örnek üzerinden arama algoritmasının çalışmasını inceleyelim:
a dizisi : 1 3 4 5 6 8 9 11 12
indisler: 0 1 2 3 4 5 6 7 8
Yukarıdaki a dizisindeki elemanlardan örneğin 6 sayılı değeri aradğımızı düşünelim. Arama işlemimiz dizinin tamamında yapılacağına göre sol : 0 ve sag : 8 olarak aramaya başlıyoruz. Öncelikle orta değeri yukarıda verilen formül ile bulalım:
sol = 0 // arama alanının başlangıcı
sag = 8 // arama alanının sonu
a[sol] = 1 // dizideki sag değeri
a[sag] = 12 //dizideki sol değeri
x = 6 // aranan değer
orta = 0 + (( 6- 1) * (8 – 1)) / (12-1)
orta = 35 /11 ~ 3 olarak bulunur. (sonuç yuvarlanır)
dolayısıyla a[3] değeri ara değerimiz olmaktadır ve bu değerin aradığımız değer olup olmadığı kontrol edilir. Buna göre a[3] = 5′tir ve aradğımız değerden küçüktür.
Şayet bulunan orta değeri aranan değerden küçükse aradığımız değer daha sağda olduğu için orta değer, sol olarak kabul edilip işlem tekrarlanır.
Şayet bulunan orta değeri aranan değerden büyükse aradığımız değer daha solda olduğu için orta değer, sağ olarak kabul eidlip işlem tekrarlanır.
Buna göre orta değer yeni sol değerimiz olacak ve formül tekrarlanacaktır:
sol = 3 // arama alanının başlangıcı
sag = 8 // arama alanının sonu
a[sol] = 5 // dizideki sag değeri
a[sag] = 12 //dizideki sol değeri
x = 6 // aranan değer
orta = 3 + (( 6- 5) * (8 – 3)) / (12-5)
orta = 3 + 5 /7 ~ 4 olarak bulunur. (sonuç yuvarlanır)
Sonuçta a[4]=6 değeri bulunmuştur ve işlem biter.
SORU-111: Dış Yol Uzunluğu (External Path Length)
Bir ağacın dış düğümlerine ayrı ayrı ulaşılması için geçilmesi gereken yol miktarıdır. Örneğin aşağıdaki ağaç için bu değeri hesaplayalım:
yukarıdaki ağaçta kırmızı renkli düğümler iç düğümdür. Siyah renk ile gösterilen düğümleri ise dış düğümlerdir.
Buna göre kökten başlandığında ağacın sol tarafında 1 adet dış düğüm vardır ve erişim 1 yolla yapılır. Ağacın sağında 3 adet dış düğüm vardır. Bunlardan en soldakine 3 sağındakine 3 en sağdakine ise 2 yol ile ulaşılır. Dolayısıyla bu ağacın dış düğüm sayısı 1+3+3+2 = 9 olarak bulunur.
Bir ağacın iç yol uzunluğu (internal path length) biliniyorsa dış yol uzunluğu aşağıdaki şekilde hesaplanabilir:
E = I + 2n
I: iç yol uzunluğu
E: dış yol uzunluğu
Yukarıdaki örnekte iç yol uzunluğu 3′tür. İç düğüm sayısı da 3 tür. Dolayısıyla E = 3 + 2×3 = 9 bulunabilir.
SORU-112: İç Yol Uzunluğu (Internal Path Length)
Bir ağacın iç düğümlerinin ayrı ayrı ulaşılması için geçilmesi gereken yol miktarıdır. Örneğin aşağıdaki ağaç için bu değeri hesaplayalım:
yukarıdaki ağaçta kırmızı renkli düğümler iç düğümdür. Buna göre kökten başlandığında ağacın sol tarafında iç düğüm olmadığı için buraya giden yolların maliyeti 0 olur. Ağacın sağında 2 adet iç düğüm vardır. Yukarıdaki iç düğüm için 1 aşağıdaki iç düğüm için ise 2 yol gitmek gerekir. Dolayısıyla bu ağaç için iç yol uzunluğu 0+ 1 + 2 + 0 = 3 olarak hesaplanabilir.
SORU-113: İç düğüm (Internal Nodes)
Bir ağacın, uçlarında olmayan düğümleridir. Örneğin aşağıdaki grafikte iç düğümler kırmızı, dış düğümler ise siyah renk ile gösterilmiştir:
SORU-114: Akış Diyagramı (Flow Chart)
Bilgisayar algoritmalarında, algoritmanın görsel olarak tasvir edilebilmesi için geliştirilmiş bir çizim yöntemidir. Buna göre yapısal programlamaya uygun olarak geliştirilmiş bir programlama dili aşağıdaki üç temel özelliği içerir:
- Bir alt programa dallanmak (fork)
- Bir alt programı tekrarlamak (loop)
- Bir alt programı icra etmek (execute)
İşte temel olarak bu 3 işlemi yapabilen her programlama dili yapısal programlamaya (structured programming) uygun olarak kabul edilir. Bu işlemleri tanımlayabilen bir akış diyagramı ise basitçe yapısal programlaya uygun dilleri modellemek için kullanılabilir.
Yukarıdaki her işlemin çizim karşılığı aşağıda verilmiştir:
Dallanma (fork):
Yukarıdaki şekilde de gösterildiği üzere akış diyagramında (flow chart) gelmekte olan akış bir koşula bağlı olarak sola veya sağa doğru dallanmaktadır.
Yukarıda görüntülenen tasvirde gelen akış bir seçime göre sola veya aşağı dallanmaktadır. Bu dallanma işlemi bittiktensonra koşula geri dönerek sorgu tekrarlanmakta ve şart sağlandığı sürece aynı alt grup (burada siyah kareler ile gösterilmiştir) tekrarlanmaktadır. Bu şart sağlanmadığı takdirde sola dallanarak döngüden çıkılır.
Çalıştırma işlemi bir programın en basit anlamda yaptığı her satırdır (statement). Basitçe bir alt program (su bprogram, subroutine) olarak kabul de edilebilir.
SORU-115: Seyyar Tüccar Problemi (Traveling Salesman Problem)
Bu problemin amacı pekçok farklı yer gezen bir tüccarın en az yol katederek bütün gezeceği yerleri nasıl tamamlayacağının hesaplanmasıdır. (gezgin satıcı problemi) Örneğin aşağıda verilen türkiye haritasında farklı iller ve bu illerin coğrafi konumları işaretlenmiştir. Kolaylık olsun diye iller arasındaki mesafeleri kuş uçuşu gitmek istersek bu durumda bütün illeri dolaşan en kısa yol aşağıdaki şekilde olur.
Dikkat edilirse bu haritada bütün noktaları dolaşan en kısa yol bulunmuştur. Bu işlemi yaparken kullanılabilecek bir iki yol aşağıda açıklanmıştır:
Açgözülü yaklaşımı (greedy approach): Bu yaklaşıma göre başlangıç olarak seçilen herhangi bir şehire en yakın mesafedeki diğer şehir seçilir ve gezi listesine eklenir. Bu şekilde bütün şehirler gezi listesine eklenene kadar hep en son eklenen şehre en yakın şehir seçilir.
Bu yaklaşım klasik açgözlü yaklaşımının hata yapma ihtimalini barındırmakla birlikte uygulanması en kolay yöntemlerden birisidir.
Enküçük artış yöntemi (Smallest increase): By yöntemde ise toplam gezilecek mesafe her seferinde yeniden hesaplanmakta ve alternatiflerden hangisi eklenirse toplam gezi mesafesi en az olur diye sorglunarak yeni şehirler eklenmektedir.
Örneğin başlangıçtan alınan 3 şehir gezi listesinde bulunurken 4. şehri seçecek olalım. Bu durumda bütün ihtimaller teker teker listeye eklenerek en az mesafe hangisinde bulunuyorsa bu şehir seçilir.
SORU-116: Devamsal Geçiş Tarzı (Continuation-passing style, CPS)
Fonksiyonel programlamada kullanılan fonksiyon tarzlarından birisidir. Buna göre bu tarzda yazılmış olan bir fonksiyon doğrudan değer döndürmek yerine, ilave bir parametre ile fonksiyondaki hesaplamaları taşır.
Devamsal geçiş tarzında yazılan bir fonksiyon çağrıldığı zaman ilave olarak bir prosedür verilerek bu fonksiyonun dönüş değerine yazılır. Doğrudan çağırmada dahili olark yapılan bazı işlemler devamsal geçiş tarzında harici olarak yapılmaktadır. Örneğin fonksiyonların dönüşleri, ara değerleri, parametrelerin çalıştırılma dereceleri ve kuyruk özyinelemeler (tail recursion) gibi.
Doğrudan çağırmalı bir fonksiyonun devamsal geçiş tarzına çevrilmesi otomatik olarak yapılabilmektedir. Bu sayede derleyiciler (compilers) bu özellikten faydalanarak iyileştirme (optimisation) yapabilmektedirler. İşin aslında bu özelliğinden dolayı devamsal geçiş tarzı programcılardan çok derleyiciler tarafından kullanılan bir tarzdır.
Scheme dilinde bazı örnek çevrimler aşağıda verilmiştir:
|
||||
|
||||
|
Yukarıda verilen iki farklı faktöriyel hesabından ilki (yani yukarıdaki 3 fonksiyondan ikincisi) doğrudan çağırmanın çevrilmiş haliyken ikincisi (yani yukarıdaki 3 fonksiyondan sonuncusu) yardımcı bir fonksiyon (f-aux) kullanarak birikimsel tarzda (accumulation style) yazılmış bir fonksiyonun devamsal geçiş tarzına dönüştürülmesini göstermektedir.
Ayrıca tembel programlama (lazy programming) kullanılarak bir fonksiyonun körilenmesi (curry) gerekirse faktöriyel hesabı aşağıdaki şekilde yazılmalıdır:
(define (factorial n k) (if (= n 0) (k 1) (factorial (- n 1) (compose k (curry * n))))) >(factorial 5 (lambda (x) x))>120
Yukarıda verilen bu fonksiyonun temel farkı k parametresi ile biriktirilen (Accumulate) değerlerin birer fonksiyon olmasıdır. Yani basitçe her çağırmada gelen fonksiyon bir önceki fonksiyon ile birleştirilmiş (compose) ve faktöriyelin başlangıç durumu (initial state) olan n = 0 durumuna gelindiğinde (n = 0 olunca 0! = 1 olduğunu hatırlayalım) bu fonksiyon birleşimlerine 1 parametresi verilerek çağrılmıştır. Bu durum daha basit şekilde aşağıdaki matematiksel gösterimde yer almıştır:
factorial(factorial(factorial(factorial(factorial(1))))
yani basitçe factorial fonksiyonları n kadar birleştirilmiştir. n = 0 olunca birleştirme işlemi durmuş bunun yerine bu birleştirilmiş fonksiyonlara 1 parametresi verilmiştir.
SORU-117: Kuyruk Özyinelemesi (Tail Recursion, Birikimsel Tarz, Accumulation Style)
Fonksiyonel programlamada kullanılan bir fonksiyon tipidir. Buna göre bir özyinelemeli (recursive) fonksiyon kendisini her çağırmada mevcut işlenmiş değeri geçirir. Bu sayede derleyici (compiler) özyineleme yığınını (recursion stack) hafızada tutmak yerine basit bir parametre değiştirme işlemi ile sonucu hesaplayabilir.
Örneğin aşağıda iki farklı faktöriyel hesabı yapan fonksiyon verilmiştir:
Klasik özyineleme fonksiyonu tarzında faktöriyel:
int faktoriyel(int n){ if(n<=0) return 1; return n * faktoriyel(n-1);}
Yukarıda verilen bu fonksiyon örneğin 3! hesaplamak için çalıştırıldığı zaman aşağıdakine benzer bir çağırma yığını (call stack) oluşur:
call faktoriyel (3) call 3 * faktoriyel (2) call 2 * faktoriyel (1) call 1 * faktoriyel (0) return 1 return 1 return 2 return 6
Görüldüğü üzere öncelikle fonksiyon özyinelemedeki başlangıç durumuna (initial state) kadar indirilmiş ve sonuç buradan çağrıldığı yere kadar geri hesaplanmıştır. Bu işlem sırasında kaç fonksiyon çağırma işlemi yapıldıysa bütün bu fonksiyonlar hafızayı işgal etmiştir. Aynı hesaplama işlemi basit bir değişiklikle daha verimli tutulabilir.
Kuyruk özyinelemeli (birikimsel tarzda) faktöriyel hesabı:
int faktoriyel(int n, int birikim){ if(n<= 0) return birikim; return faktoriyel(n-1,birikim*n);}
Yukarıdaki fonksiyon faktoriyel(3,1) olarak çalıştırıldığında hafızadaki durum aşağıdaki şekildedir:
replace arguments with (3 1), jump to "faktoriyel" replace arguments with (2 3), jump to "faktoriyel" replace arguments with (1 6), jump to "faktoriyel" replace arguments with (0 6), jump to "faktoriyel" return 6
Görüldüğü üzere fonksiyon (3 1) parametreleri ile çalıştırıldığında yeni çağrılacak olan fonksiyonda ilk parametre 1 azalıp 2 olurken ikinci parametre 3 * 1 ile 3 olmaktadır. Bu şekilde ilk parametre her çağrımda 1 azalırken ikinci parametre kendisi ile ilk parametrenin çarpımı olmakta ve en nihayetinde ilk parametre 0 olduğunda ikinci parametrede sonuç birikmiş olmaktadır. Bu durumda sonuç ikinci parametrenin döndürülmesiyle kolayca hesaplanır.
Bu yeni yöntemde dikkat edilirse ilk yönteme göre daha az hafıza kullanılmıştır çünkü fonksiyonların birbirini beklemesi söz konusu değildir. Ayrıca zaman olarak da verimlidir çünkü başlangıç durumu (initial state) ulaşıldıktan sonra hesaplamalar için fonksiyonların geri yüklenmesi gerekmemektedir.
SORU-118: Sıralama Algoritmaları (Sorting Algorithms)
Bilgisayar bilimlerinde verilmiş olan bir grup sayının küçükten büyüğe (veya tersi) sıralanması işlemini yapan algoritmalara verilen isimdir. Örneğin aşağıdaki düzensiz sayıları ele alalım:
5 9 2 3 7 11 -4 6
Bu sayıların sıralanmış hali
-4 2 3 5 6 7 11
olacaktır. Bu sıralama işlemini yapmanın çok farklı yolları vardır ancak bilgisayar mühendisliğinin temel olarak üzerine oturduğu iki performans kriteri buradaki sonuçları değerlendirmede önemli rol oynar.
- Hafıza verimliliği (memory efficiency)
- Zaman verimliliği (Time efficiency)
Temel olarak algoritma analizindeki iki önemli kriter bunlardır. Bir algoritmanın hızlı çalışması demek daha çok hafızaya ihtiyaç duyması demektir. Tersi durumda da bir algoritmanın daha az yere ihtiyaç duyması daha yavaş çalışması demektir. Ancak bir algoritma hem zaman hem de hafıza olarak verimliyse bu durumda diğer algoritmalardan başarılı sayılabilir.
Genellikle verinin hafızada saklanması sırasında veriyi tutan bir berlirleyici özelliğinin olması istenir. Veritabanı teorisinde birincil anahtar (primary key) ismi de verilen bu özellik kullanılarak hafızada bulunan veriye erişilebilir. Bu erişme sırasında şayet berlileyici alan sıralı ise erişimin logaritmik zamanda olması mümkündür. Şayet veri sıralı değilse erişim süresi doğrusal (linear) zamanda olmaktadır.
SORU-119: Seçerek Sıralama (Selection Sort)
Verinin hafızada sıralı tutulması için geliştirilen sıralama algoritmalarından (sorting algorithms) bir tanesidir. Basitçe her adımda dizideki en küçük sayının nerede olduğu bulunur. Bu sayı ile dizinin başındaki sayı yer değiştirilerek en küçük sayılar seçilerek başa atılmış olur.
Sıralanmak istenen verimiz:
5,7,2,9,6,1,3,7
olsun. Bu verilerin bir oluşumun(composition) belirleyici alanları olduğunu düşünebiliriz. Yani örneğin vatandaşlık numarası veya öğrenci numarası gibi. Dolayısıyla örneğin öğrencilerin numaralarına göre sıralanması durumunda kullanılabilir.
Seçerek sıralamanın çalışması yukarıdaki bu örnek dizi üzerinde adım adım gösterilmiştir.
- adım: başlangıç adımı i=0 olarak ata.
- adım: dizideki i. sırasından sonraki en küçük sayının yerini bul. Bu dizideki en küçük sayı 1′dir ve 5. sıradadır.
- adım dizinin i. sırasındaki sayıyı bu en küçük sayı ile yer değiştir: 1,7,2,9,6,5,3,7
- adım : i’yi bir arttır ve 1. adıma git.
Dolayısıyla 3. adımdan sonra i=1 olacak ve sonra ilk sayı olan 1 atlanaraka kalan sayılar olan 7,2,9,6,5,3,7 sayıları arasından en küçük sayı bulunur. Bu sayı 2′dir ve sırası da 2dir. sıradaki sayı olan yerdeki sayı ile yer değiştirir ve sonuç : 1,2,7,9,6,5,3,7 olarak bulunur.
Artık i değeri 2dir ve bu sayıdan itibaren en küçük sayı 7,9,6,5,3,7 arasında aranır. bulunan 3′ün sırası 6′dır. Bu sayı da sıradaki yerini alır ve sonuç : 1,2,3,9,6,5,7,7 olur. Bu işlem böylece devam eder ve dizinin değişimi aşağıda gösterilmiştir:
1,2,3,5,9,6,7,7
1,2,3,5,6,9,7,7
1,2,3,5,6,7,9,7
1,2,3,5,6,7,7,9
olarak bulunur.
Bu işlemi yapan örnek bir JAVA kodu aşağıda verilmiştir:
?View Code JAVA
| 1234567891011121314151617181920212223 | public static int [] selectionsort(int [] A,int n) { int tmp; int min; for(int i=0; i < n-1; i++) { min=i; for(int j=i; j < n; j++) { if (A[j] < A[min]){ min=j; } } tmp=A[i]; A[i]=A[min]; A[min]=tmp; } return A; } |
Yukarıdaki örnek kodda iki döngü iç içe yazılarak içteki döngüde en küçük sayı bulunmuş, dıştaki döngüde ise bu işlemin her seferinde yenilenmesi sağlanmıştır.
Bu algoritmanın karmaşıklığı O(n2) ‘dir çünkü her adımda n sayı işlenmekte ve bu işlem n kere tekrar edilmektedir.
SORU-120: Hızlı Sıralama Algoritması (Quick Sort Algorithm)
Verinin hafızada sıralı tutulması için geliştirilen sıralama algoritmalarından (sorting algorithms) bir tanesidir. Basitçe sıralanacak olan dizideki orta noktada (mean) bulunan bir sayıyı seçerek diğer bütün sayıları bu orta sayıdan büyük veya küçük diye sınıflayarak sıralama yapmayı hedeflemektedir. Bu açıdan bir parçala fethet (divide and conquere) yaklaşımıdır. Ayrıca bu seçilen orta noktaya eksen (pivot) adı da verilir çünkü bütün diğer sayılar bu sayının ekseninde sıralanacaktır.
[flashvideo file=http://www.bilgisayarkavramlari.com/wp-content/uploads/video/quicksort.flv /]
Sıralanmak istenen verimiz:
5,7,2,9,6,1,4,7 (düzeltildi, Erol Bey'e teşekkürler)
olsun. Bu verilerin bir oluşumun(composition) belirleyici alanları olduğunu düşünebiliriz. Yani örneğin vatandaşlık numarası veya öğrenci numarası gibi. Dolayısıyla örneğin öğrencilerin numaralarına göre sıralanması durumunda kullanılabilir.
Yukarıda verilen bu örnek dizinin sıralanması adım adım aşağıda anlatılmıştır:
- adım, dizinin ortasındaki sayı bulunur. Bu örnekte 8 sayı olduğu için ortadaki sayı 4. elemandır. Bu elemanın değeri de 9′dur. Bu durum aslında biraz bahtsız bir durumdur çünkü tesadüfen dizideki en büyük sayıdır. Bu mesele 2. adımda ortaya çıkacaktır.
2.adım: Diziden 1.adımda seçilen sayıya göre dizideki bütün elemanları küçük veya büyük diye sınıflandır. Tabi 1. adımda şanssız bir şekilde en büyük sayı seçildiği için bütün sayılar 9′dan küçük olarak sınıflandırılacaktır.
5,7,2,6,1,4,7 (9)
- adım: Sınıflandırılana küçük ve büyük dizileri tekrar hızlı sıralamaya ver. Yani 5,7,2,6,1,4,7 dizisini aynı adımlarla tekrar sıralıyoruz.
- adım: eleman sayımız 7 ve ortadaki eleman 3. eleman olan 6 olur. Dizideki sayılar 6′dan büyük ve 6′dan küçük diye sınıflandırılırsa:
5,2,1,4 (6) 7,7
olarak iki grup elde edilir. Bu grupları da sıralamak üzere tekrar hızlı sıralama algoritmasına veririz. Dolayısıyla 5,2,1,4 sayıları ayrı ve 7,7 sayıları ayrı ayrı sıralanacaktır.
- adım 5,2,1,4 sayılarının orta değeri 2′dir ve sınıflandırılırsa:
1 (2) 4,5 bulunur. Aynı zamanda diğer dizi olan 7,7 sıralanırsa sonuç değişmez ve 7,7 bulunur.
- adım 1 sıralanırsa 1 ve 4,5 sıralanırsa 4,5 bulunur.
7.adım. Bu adımdan sonra artık birleştirme işlemine geçilebilir. Buna göre 6. adımda sıralanan değerleri birleştirirsek :
1,2,4,5 değerleri elde edilir.
8.adım: 4. adımdaki sayılar birleştirilirse 1,2,4,5,(6),7,7 sayıları elde edilir.
9.adım: 2. adımdaki sayılar birleştirilirse 1,2,4,5,6,7,7 (9) olarak dizinin sıralanmış hali elde edilir.
Bu algoritmanın JAVA dilindeki karşılığı aşağıdaki şekilde yazılabilir:
public static void quickSort(int A[],int p, int r){ int q; if(p<r) { q=partition(A,p, r); quickSort(A,p, q-1); quickSort(A,q+1, r); }}
Görüldüğü üzere yukarıdaki kod özyineli (recursive) bir koddur ve kendi içerisinde orta değeri bulmak için partition adı verile harici bir fonksiyonu çağırmıştır. Bu fonksiyonun kodu da aşağıda verilmiştir:
public static int partition(int A[],int p, int r){ int tmp; int x = A[r]; int i = p-1; for(int j=p; j<=r-1; j++) { if(A[j]<=x) { i++; tmp=A[i]; A[i]=A[j]; A[j]=tmp; } } tmp=A[i+1]; A[i+1]=A[r]; A[r]=tmp; return i+1;}
Yukarıdaki bu orta nokta bulmaya yarayan fonksiyonun en büyük özelliği tek bir dizi kullanarak bu dizi içerisindeki indisi döndürmeye çalışmasıdır. Bu yüzden hangi parça üzerinde orta nokta aradığını alt ve üst limitleri parametre alarak bulur. Bu parametreler koddaki p ve r değişkenleridir.
Hızlı sıralamanın algoritma karmaşıklığına bakıldığında O(nlogn) olarak bulunur çünkü üzerinde çalışılan dizi her adımda en fazla 2ye bölünmüştür (Big-O hesaplanırken en kötü ihtimalin hesaplandığını hatırlayınız) böylece sonuç dizisi olan 2şer elemanlı dizilere log2n adımda ulaşılabilir. Daha sonra her n eleman için sıralama yapıldığı ve her n eleman üzerinden geçildiği için bu değer çarpan olarak gelmekte ve sonuç nlog2n olarak bulunmaktadır.
Örnek görsel çalışma
Yapılan yorumlardan sonra hızlı sıralama algoritmasının çalışmasını görsel bir örnek üzerinden göstermeye karar verdim.
Sıralayacağımız sayılar 88,31,52,25,98,14,30,62,23,79 olarak verilsin
Yukarıdaki bu sayıları sıralamak için öncelikle ortadaki değer (pivot) bulunur. Bu değer sayılar arasından rast gele seçilebileceği gibi dizinin ilk elemanı, son elemanı gibi belirli bir yerdeki sayı olarak da atanabilir. Bu değer yukarıdaki kodda verilen p değişkeni ile gösterilen değerdir. Biz örneğimizde 52 olarak seçtiğimizi düşünelim:
Yukarıdaki şekilde gösterildiği üzere sayı rast gele olarak seçildikten sonra, diğer sayılar, bu seçilen rast gele sayıdan (52) büyük ve küçük olarak sınıflandırılır.
Görüldüğü üzere problem iki parçaya bölünmüş ve burada parçala fethet (divide and conquere) yaklaşımı kullanılmıştır. Artık problemin her iki alt kümesi ayrı ayrı yeniden hızlı sıralama algoritmasına verilecek ve bir önceki adımda olduğu gibi sayılar arasından rast gele bir sayı seçilerek problemi alt parçalara bölecektir:
Yukarıdaki son şekilde görüldüğü üzere problem bir önceki adımda bulunan parçaların alt parçalara bölünmesi ile daha küçük hale getirilmiştir. Burada rast gele olarak seçilen pivot sayıları ilk grup için 25 ve ikinci grup için 79 olmuştur.
Problem son kez alt adımlara bölünerek çözülür
Görüldüğü üzere son adımda son kümeler için yine pivot seçimi yapılmış ve 23, 31, 98 sayıları pivot olarak seçilmiştir. Problem bu aşamadan sonra çözülmüştür çünkü bu sayıların alt grupları tek elemanlı kümeler haline gelmiştir.
Ardından her küme kendi pivotunun solunda veya sağında olmasına göre yukarı doğru toplanır. Bu toplama işlemi yukarıkida şeklin en altında gösterilmiştir.
SORU-121: Birleştirme Sıralaması (Merge Sort)
Verinin hafızada sıralı tutulması için geliştirilen sıralama algoritmalarından (sorting algorithms) bir tanesidir. Basitçe sıralanacak olan diziyi ikişer elemanı kalan parçalara inene kadar sürekli olarak ikiye böler. Sonra bu parçaları kendi içlerinde sıralayarak birleştirir. Sonuçta elde edilen dizi sıralı dizinin kendisidir. Bu açıdan bir parçala fethet (divide and conquere) yaklaşımıdır.
[flashvideo file=http://www.bilgisayarkavramlari.com/wp-content/uploads/video/mergesort.flv /]
Sıralanmak istenen verimiz:
5,7,2,9,6,1,3,7
olsun. Bu verilerin bir oluşumun(composition) belirleyici alanları olduğunu düşünebiliriz. Yani örneğin vatandaşlık numarası veya öğrenci numarası gibi. Dolayısıyla örneğin öğrencilerin numaralarına göre sıralanması durumunda kullanılabilir.
Birleştirme sıralamasının çalışması yukarıdaki bu örnek dizi üzerinde adım adım gösterilmiştir. Öncelikle parçalama adımları gelir. Bu adımlar aşağıdadır.
- adım diziyi ikiye böl:
5,7,2,9 ve 6,1,3,7
- adım çıkan bu dizileri de ikiye böl:
5,7 ; 2,9 ; 6,1 ; 3,7
- adım elde edilen parçalar 2 veya daha küçük eleman sayısına ulaştığı için dur (aksi durumda bölme işlemi devam edecekti)
- adım her parçayı kendi içinde sırala
5,7 ; 2,9 ; 1,6 ; 3,7
- Her bölünmüş parçayı birleştir ve birleştirirken sıraya dikkat ederek birleştir (1. ve 2. parçalar ile 3. ve 4. parçalar aynı gruptan bölünmüştü)
2,5,7,9 ve 1,3,6,7
6. adım, tek bir bütün parça olmadığı için birleştirmeye devam et
1,2,3,5,6,7,7,9
- adım sonuçta bir bütün birleşmiş parça olduğu için dur. İşte bu sonuç dizisi ilk dizinin sıralanmış halidir.
Birleştirme Sıralamasının JAVA dilinde yazılmış bir örnek kodu aşağıda verilmiştir:
Öncelikle birleştirme sıralamasının ana fonksiyonu:
public int [] mergesort(int [] m) { int x=0; int y=0; int middle=m.length/2; int left[] =new int [middle]; int right[] =new int [middle]; int result[] =new int[(m.length)]; if(m.length<= 1){ return m; } for(int i=0; i<middle; i++) { left[x]=m[i]; x++; } for(int i=middle; i<m.length; i++) { right[y]=m[i]; y++; } left=mergesort(left); right=mergesort(right); result=merge(left,right); return result; }
Yukarıdaki bu fonksiyon dikkat edilirse özyinelemeli (recursive) bir kod olup paramatre olarak aldığı dizinin yanında bu dizi boyutunun yarısı uzunluğunda iki ilave dizi kullanmış ve bu dizilere iki parçayı ayrı ayrı koyarak yine sıralaması için kendi fonksiyonuna parametre olarak geçirmiştir. Sonda ise bu iki parçayı birleştiren bir merge() fonksiyonu çağırmıştır. İşte bu birleştirme fonksiyonunun kodu aşağıda verilmiştir:
public int [] merge(int []left,int []right) { int result[] =new int [left.length + right.length]; int x=0; int y=0; int k=0; while(left.length>x && right.length>y) { if(left[x] <= right[y]) { result[k]=left[x]; x++; k++; } else { result[k]=right[y]; y++; k++; } } if(left.length>x) { while(x < left.length) { result[k]=left[x]; x++; k++; } } if(right.length>y) { while(y < right.length) { result[k]=right[y]; y++; k++; } } return result; }
Yukarıdaki koda dikkat edilecek olursa 3 ayrı durum için birleştirme kodu yazılmıştır:
Birinci durumda iki dizide de eleman bulunmaktadır. Bu durumda iki dizideki en baştaki sayılar karşılaştırılarak küçük olan sonuç dizisine kopyalanmaktadır. İkinci ve üçüncü durumlarda ise dizilerden birisinde eleman kalmamıştır. Bu durumlarda eleman kalan dizideki elemanlar doğrudan sonuç dizisine kopyalanabilir.
Birleştirme Sıralamasının algoritma karmaşıklığına bakıldığında O(nlogn) olarak bulunur çünkü üzerinde çalışılan dizi her adımda 2ye bölünmüştür böylece sonuç dizisi olan 2şer elemanlı dizilere log2n adımda ulaşılabilir. Daha sonra her n eleman için sıralama yapıldığı ve her n eleman üzerinden geçildiği için bu değer çarpan olarak gelmekte ve sonuç nlog2n olarak bulunmaktadır.
Yukarıdaki kodun bir problemi, sıralama işlemi sırasında 2 ve üzeri sayıdaki elemanlı dizileri kabul ediyor olmasıdır. Örneğin 2,4,8,16 gibi eleman sayılarındaki diziyi kabul eder. Bunun sebebi, sıralama fonksiyonuna gelen dizinin her durumda iki eşit parçaya bölünmesidir. Oysaki tek eleman sayısına sahip dizilerde, bölünme sonucunda sorun çıkar. Örneğin 3 elemanlı bir diziyi iki parçaya böldüğümüzde biri 2 diğeri 1 elemanlı iki dizi elde etmemiz gerekir. Bu durumda bölme işlemi sonucunda tek elemana inen eleman sayıları için de geçerlidir. Örneğin 12 elemalı bir dizi önce 6+6 şeklinde eşit iki parçaya sonra 6 elemalı diziler 3+3 şeklinde eşit parçalara ve nihayet 3 elemalı dizi eşit olmayan iki parçaya bölünecektir. Bu durumda sorunun çözümü için yukarıdaki koda, ilgili düzeltmeyi yapmamız gerekir.
JAVA dili için yukarıdaki kodu düzenleyerek tekrar yazdım ve kod aşağıdaki şekildedir:
public class MergeSort { private int[] list; // siralancak listeyi alan inşa fonksiyonu public MergeSort(int[] listToSort) { list = listToSort; } // listeyi döndüren kapsülleme fonksiyonu public int[] getList() { return list; } // dışarıdan çağırılan sıralama fonksiyonu public void sort() { list = sort(list); } // Özyineli olarak çalışan ve her parça için kullanılan sıralama fonksiyonu private int[] sort(int[] whole) { if (whole.length == 1) { return whole; } else { // diziyi ikiye bölüyoruz ve solu oluşturuyoruz int[] left = new int[whole.length/2]; System.arraycopy(whole, 0, left, 0, left.length); //dizinin sağını oluşturuyoruz ancak tek sayı ihtimali var int[] right = new int[whole.length-left.length]; System.arraycopy(whole, left.length, right, 0, right.length); // her iki tarafı ayrı ayrı sıralıyoruz left = sort(left); right = sort(right); // Sıralanmış dizileri birleştiriyoruz merge(left, right, whole); return whole; } } // birleştirme fonksiyonu private void merge(int[] left, int[] right, int[] result) { int x = 0; int y = 0; int k = 0; // iki dizide de eleman varken while (x < left.length && y < right.length) { if (left[x] < right[y]) { result[k] = left[x]; x++; } else { result[k] = right[y]; y++; } k++; } int[] rest; int restIndex; if (x >= left.length) { rest = right; restIndex = y; } else { rest = left; restIndex = x; } for (int i=restIndex; i<rest.length; i++) { result[k] = rest[i]; k++; } } public static void main(String[] args) { int[] arrayToSort = {15, 19, 4, 3, 18, 6, 2, 12, 7, 9, 11, 16}; System.out.println("Unsorted:"); for(int i = 0;i< arrayToSort.length ; i++){ System.out.println(arrayToSort[i] + " "); } MergeSort sortObj = new MergeSort(arrayToSort); sortObj.sort(); System.out.println("Sorted:"); int [] sirali = sortObj.getList(); for(int i = 0;i< sirali.length ; i++){ System.out.println(sirali[i] + " "); } }}
SORU-122: Yığınlama Sıralaması (Heap Sort)
Verinin hafızada sıralı tutulması için geliştirilen sıralama algoritmalarından (sorting algorithms) bir tanesidir. Yıpınlama sıralaması, arka planda bir yığın ağacı(heap) oluşturur ve bu ağacın en üstündeki sayıyı alarak sıralama işlemi yapar. (Lütfen yığın ağacındaki en büyük sayının her zaman en üstte duracağını hatırlayınız. )
Sıralanmak istenen verimiz:
5,12,20,18,4,3
olsun. Bu verilerin bir oluşumun(composition) belirleyici alanları olduğunu düşünebiliriz. Yani örneğin vatandaşlık numarası veya öğrenci numarası gibi. Dolayısıyla örneğin öğrencilerin numaralarına göre sıralanması durumunda kullanılabilir.
Yukarıdaki bu sayılardan bir yığın ağacı oluşturulduğunda aşağıdaki şekilde bir sonuç elde edilir:
Bu ağacın en büyük değeri en üstte (kökte) durmaktadır öyleyse bu değer alınarak sonuç dizisinin son elemanı yapılırsa ve sonra geriye kalan sayılar tekrar yığınlanırsa (heapify) ve bu işlem eleman kalmayana kadar tekrarlanırsa sonuç dizisindeki veriler sıralanmış olarak elde edilir. Bu işlemler adım adım aşağıda gösterilmiştir:
Sonuç dizisi : {20}
20 sayısı yığın ağacından silindikten sonra kalan sayılar yığınlandıktan sonra yukarıdaki ağaç elde edilir. Bu ağacın en üstündeki sayı alınırsa sonuç dizisi : {20,18} olarak bulunur ve 18 alındıktan sonra kalan sayılar yığınlandığında ise aşağıdaki ağaç elde edilir.
Bu ağacında en üstünde bulunan sayı 12′dir ve bu sayı sonuç dizisine eklenir. Sonuç dizisi : {20,18,12} olarak bulunur ve kalan sayılar bir kere daha yığınlanır:
Sonuçta kalan sayıların yığınlanmasıyla yukarıdaki ağaç elde edilir ve bir kere daha en üstteki sayı alınarak sonuç dizisi : {20,18,12,5} olarak bulunur.
Aynı işlem kalan sayılara tekrar tekrar uygulanarak {20,18,12,5,4,3} sayıları elde edilir. Dikkat edilirse bu sayılar ilk başta verilen sayıların sıralanmış halidir. Şayet sıralama işlemi küçükten büyüğe yapılsın isteniyorsa bu durumda sayılar dizinin başından değil sonundan yazılarak kaydedilebilir veya çıkan sonuç tersten sıralanabilir.
Bu işlemi yapan bir yığın sıralaması (heap sort) kodu java dilinde aşağıdaki şekilde yazılabilir :
public void heapsort(int[]A){ int tmp; BuildHeap(A); for(int i = A.length-1; i>=0; i--) { tmp=A[0]; A[0]=A[i]; A[i]=tmp; heapsize = heapsize -1 ; heapify(A,0); } }
Yığın ağacı bilgisayar bilimlerinde özellikle sıralama amacıyla çokca kullanılan bir veri yapısıdır. Bu veri yapısı üst düğümün (atasının) alt düğümlerden (çocuklarından) her zaman büyük olduğu bir ikili ağaç (binary tree) şeklinde düşünülebilir.
Aşağıda örnek bir yığın ağacı verilmiştir:
Yukarıdaki ikili ağaçta dikkat edilirse ağaç dengeli bir şekilde sırayla doldurulmuştur. Buna göre yeni bir eleman daha eklenmesi durumunda ağacın eleman sayısı 7′ye yükselecek ve sayıların yeri değişmekle birlikte yeni eleman şu andaki 18 sayısını içeren düğümün sağına gelecektir.
Yığın ağaçları her zaman sırayla dolar ve sırayla boşalır.
Yukarıdaki ağacın bir dizi ile tutulması da mümkündür.
Ayrıca herhangi bir ağacı yığın ağacına çevirmek (yığınlamak, heapify) de mümkündür ve bu işlem bir yığın ağacı oluşturma işleminin temelini oluşturur. Basitçe yığın ağacına yapılan her ekleme ve her çıkarma işleminden sonra bu yığınlama (heapify) işlemi yapılarak yığın ağacının formunu koruması sağlanmalıdır.
Aşağıda java dilinde bir yığınlama (heapify) fonksiyonu verilmiştir:
public void heapify(int[]A,int i) { int largest = 0; int le=left(i); int ri=right(i); if((le<=heapsize) && (A[le]>A[i])) largest = le; else largest = i; if((ri<=heapsize) && A[ri]>A[largest]) largest = ri; if (largest != i) { int tmp = A[i]; A[i]= A[largest]; A[largest] = tmp; heapify(A, largest); } } public static int left(int i ){ return 2*(i+1)-1; } public static int right(int i){ return 2*(i+1); }
Dolayısıyla yukarıdaki yığınlama (heapify) fonksiyonu kullanarak bir yığın ağacı oluşturmak aşağıdaki şekildem mümkün olabilir:
public void BuildHeap(int[]A){ heapsize=A.length-1; for(int i=0; i<Math.floor(A.length/2); i++) { heapify(A,i); }}
SORU-124: Dizi üzerinde ağaç kodlaması
Ağaçlar bilindiği üzere gösterici kullanan veri yapılarıdır. Ancak verinin dizi(array) üzerinde saklanması durumunda ağacın bu gösterici özelliğinin kullanılması ne yazık ki mümkün olamamaktadır. Bunun yerine dizinin indis numaralarını kullanan bir matematiksel fonksiyon ile benzer bir yapı elde edilebilir.
Yukarıda verilen ikili ağacı ve her düğümün köşesinde yazan düğüm numarasını dikkate alırsak yapının aynısı aşağıdaki gibi bir dizide saklanabilir.
A[6] = {20,12,18,5,3,4}
dolayısıyla örneğin A[3] = 5 olarak hem ağaçta hem de dizide aynı değere sahip olmaktadır.
bu durumu veren bir matematiksel fonksiyon aşağıdaki şekilde yazılabilir:
Ağaçtaki herhangi bir düğümün (bu düğümün numarası i ise) ,
solunu veren formül : 2*(i+1)-1;
sağını veren formül : 2*(i+1);
şeklinde hesaplanabilir. Örneğin 2 numaralı düğümün solundaki düğüm için 2 * (2 +1 ) – 1 = 5 değeri doğru olarak hesaplanır.
Yukarıda da gösterildiği üzere bir ağacı bir dizi üzerinde sadece indisler üzerinde matematiksel işlemler yaparak saklamak mümkündür. Ancak bu yönteminde bir zararı bulunmaktadır. Örneğin ağaç yukarıda gösterildiği gibi dengeli ve dolu bir ağaç değilse ve ağacın bazı düğümleri boşsa bu durumda dizide hiç kullanılmayan boş değerler tutulacak demektir.
Diğer bir mesele ise ağacın ilgili düğümünün çocuğunun olup olmadığının bulunmasıdır. Örneğin ağacın boyutu bilinmiyorsa dizinin boyutu birer çocukları daha varmış gibi uzatılacak ve buralara nöbetçi (sentinel) konularak ağaç üzerinde istenilmeyen yerlere gidilmesi engellenebilir.
Dolayısıyla bu yöntemin yığın ağaçları (heap) gibi boş eleman içermeyen ve verinin boyutu bilindiği durumlarda kullanılması daha verimlidir.
SORU-125: Sayarak Sıralama (Counting Sort)
Verinin hafızada sıralı tutulması için geliştirilen sıralama algoritmalarından (sorting algorithms) bir tanesidir. Basitçe sıralanacak olan dizideki her sayının kaç tane olduğunu farklı bir dizide sayar. Daha sonra bu sayıların bulunduğu dizinin üzerinde bir işlemle sıralanmış olan diziyi elde eder.
Sıralanmak istenen verimiz:
5,7,2,9,6,1,3,7
olsun. Bu verilerin bir oluşumun(composition) belirleyici alanları olduğunu düşünebiliriz. Yani örneğin vatandaşlık numarası veya öğrenci numarası gibi. Dolayısıyla örneğin öğrencilerin numaralarına göre sıralanması durumunda kullanılabilir.
Bu dizi üzerinden bir kere geçilerek aşağıdaki sayma dizisi elde edilir:
|
Dizi indisi: |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
|
Değeri (sayma): |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
2 |
0 |
1 |
Yukarıdaki tabloda da gösterildiği üzere dizide bulunan en büyük eleman sayısı kadar eleman içeren bir sayma dizisi oluşturulmuş ve bu dizinin her elemanına, sıralanmak istenen dizideki her elemanın sayısı girilmiştir.
Bu sayma işleminden sonra sıralı dizinin üretilmesi yapılabilir. Bu işlem de dizinin üzerinden tek bir geçişle her eleman için kaç tekrar olduğu yazılarak yapılabilir. buna göre örneğin sıralanmış dizide 0 hiç olmayacak 1′den 1 tane, 2′den 1 tane olacak şeklinde devam eder ve sonuç:
1,2,3,5,6,7,7,9
şeklinde elde edilir.
Bu sıralama algoritmasının JAVA dilindeki kodu aşağıda verilmiştir:
public static void countingsort(int[]A, int[]B, int k){ int C[]=new int[k]; // sayma dizisi oluşturuluyor int i; int j; for(i=0; i<k; i++) { C[i]=0; } for(j=0; j<A.length; j++) { C[A[j]]=C[A[j]]+1 ; } for(i=1; i<k; i++) { C[i]=C[i]+C[i-1]; } for(j=0; j<A.length; j++) { B[C[A[j]]]=A[j]; C[A[j]]=C[A[j]]-1; }}
Yukarıdaki fonksiyon bir adet sıralanacak dizi, bir adet sıralanmış hali geri döndürecek atıf ile çağırma (call by reference ile) boş dizi ve dizideki en büyük sayının değerini alır. Sonuç ikinci parametre olan boş diziye döner.
Bu sıralama algoritmasının karmaşıklığı (complexity) hesaplarnısa. Dizideki her elemanın üzerinden bir kere geçilerek sayıları hesaplanır. Bu geçiş n elemanlı dizi için n zaman alır. Ayrıca dizideki en büyük elemanlı sayı kadar (bu örnekte k diyelim) büyük olan ikinci bir sayma dizisinin üzerinden de bir kere geçilir bu işlem de k zaman alır. Dolayısıyla toplam zaman n+k kadardır. Bu durumda zaman karmaşıklığı O(n) olur.
Hafıza karmaşıklığına baklırsa (memory complexity) hafızada mevcut verilerin saklandığı bir dizi (yukarıdaki örnek kodda A dizisi). Sonuçların saklandığı ikinci bir dizi (yukarıdaki örnekte B dizisi) ve her elemanın kaçar tane olduğunun durduğu bir dizi (yukarıdaki örnekte C dizisi) tutulmaktadır. Bu durumda A ve B dizileri n, C dizisi ise k boyutundadır ve toplam hafıza ihtiyacı 2n+k kadardır.
SORU-126: Kabarcık Sıralaması (Baloncuk sıralaması, Bubble Sort)
Verinin hafızada sıralı tutulması için geliştirilen sıralama algoritmalarından (sorting algorithms) bir tanesidir. Basitçe ardışık duran iki hafıza bloğunun birbirine nispetle sıralanması ve bu işlemin sürekli tekrarlanması sayesinde sıralar. Ardışık iki hafıza bloğuna bakmasından dolayı baloncuk ismini almıştır. Çünkü bu bakma işlemi bir baloncuğun (buble) hareket etmesi gibi sayıların üzerinde hareket etmektedir.
[flashvideo file=http://www.bilgisayarkavramlari.com/wp-content/uploads/video/bubblesort.flv /]
Aşağıda bir örnek verilmiştir:
Sıralanmak istenen verimiz:
5,7,2,9,6,1,3
olsun. Bu verilerin bir oluşumun(composition) belirleyici alanları olduğunu düşünebiliriz. Yani örneğin vatandaşlık numarası veya öğrenci numarası gibi. Dolayısıyla örneğin öğrencilerin numaralarına göre sıralanması durumunda kullanılabilir.
Kabarcık sıralama algoritmasının çalışması adım adım gösterilmiştir:
1. adım : 5,2,7,6,1,3,92. adım: 2,5,6,1,3,7,93. adım: 2,5,1,3,6,7,94. adım: 2,1,3,5,6,7,95. adım:1,2,3,5,6,7,96. adım:1,2,3,5,6,7,9
Yukarıda her adımda yapılan işlemi gösteren baloncuk sıralamasında algoritmanın çalışması aşağıdaki şekilde anlatılabilir:
1. ilk iki sayıyı al2. aldığın iki sayıyı karşılaştır3. küçük olanı yaz diğerini aklında tut4. dizinin sonuna geldiysen aklındaki sayıyı diziye yazarak bitir5. dizinin sonu değilse yeni bir sayı al.6. 2. adıma geri git.
Yukarıdaki algoritma kabarcık sıralamasının bir geçişini (pass) göstermektedir. Bu geçişin dizide bulunan eleman sayısının bir küçüğü kadar tekrar etmesi durumunda dizi sıralı olur.
Ancak bazı durumlarda dizinin sıralanması yukarıda geçen sayı (n elemanlı bir dizi için n geçiş) kadar tekrarı gerektirmez. Bu durum sadece dizi tersten sıralıysa gerekir. Ayrıca dizi zaten sıralı ise ilk geçişten sonra sıralamanın durmasını bekleriz.
Bir diğer iyileştirme ise yukarıdaki örneğe dikkatle bakıldığında fark edilebilir. Kabarcık sıralamasında dizi üzerindeki her geçişten sonra dizinin en büyük elemanı dizinin sonuna atılmaktadır. Örneğin ilk geçişten sonra en büyük eleman, 2. geçişten sonra en büyük iki eleman 20. geçişten sonra en büyük 20 eleman gibi dizinin sonunda yerleşmekte ve bir daha hiç değişmemektedir. bu durumda örneğin 20. geçişte son 20 elemana bakmanın bir anlamı yoktur. Bu iyileştirme (optmisation) ile de hız kazanmak mümkündür.
Yukarıda anlatılan algoritmanın java dilinde ve c dilinde kodları aşağıda verilmiştir.
JAVA dilindeki karşılığı:
public void bubblesort(int [] A) // bir diziyi parametre alan fonksiyon { int tmp; for(int i=0; i<A.length; i++) { // for(int j=1; j<A.length-i+1; j++) şeklinde de döngü yazılabilir for(int j=A.length-1 ; j>i;j--) //i'ye kadar olan kısmı sabitlendiği için tekrar geçişlerde kontrolü gerekmemektedir. { if(A[j-1]>A[j]) { tmp=A[j-1]; A[j-1]=A[j]; A[j]=tmp; } } } }
Algoritmanın karmaşıklığına bakıldığında her geçişin bütün elemanlardan en az bir kere geçmesi gerektiği görülür. bu durumda her geçiş n kadar işlem gerektirmekte toplam n-1 geçiş gerektiği için de n x (n-1) geçiş gerektirir. Sonuçta O(n2) zaman karmaşıklığına sahiptir. Hafızadaki ihtiyacına bakıldığında ise mevcut veri kadar yer tutması yeterlidir. Bu durumda hafıza karmaşıklığı O(n) olarak hesaplanabilir.
Kabarcık Sıralamasında İyileştirmeler (Hasan Bey’in yorumu üzerine ekliyorum)
Kabarcık sıralaması yukarıdaki şekilde yazılabileceği gibi iki önemli iyileştirme (optimization) yapılabilir. Bunlardan birincisi, Hasan Bey’in de belirttiği üzere zaten sıralı bir dizinin daha fazla sıralanması içn döngünün dönmemesidir.
Örneğin yukarıdaki kodu ele alırsak ve zaten sıralı bir diziyi döngüye verirsek, döngü zaten sıralı dizi için, dizinin boyutu kadar dönecektir. Bunun yerine dizinin sıralandıktan sonra dönmesini engelleyebiliriz.
Peki dizinin sıralandığını nereden anlayacağız?
Dizi şayet sıralıysa elemanların yer değiştirmesini gerektiren if koşuluna hiç girilmez. Bu durumda kodumuzda bulunan if’e hiç girmiyorsak dizi sıralıdır ve daha fazla döngünün dönmesine gerek yoktur.
Kodu aşağıdaki şekilde iyileştirelim:
public void bubblesort(int [] A) // bir diziyi parametre alan fonksiyon { int tmp; for(int i=0; i<A.length; i++) { int sirali=1; for(int j=A.length-1 ; j>0;j--) { if(A[j-1]>A[j]) //şayet buraya girmiyorsak dizi sıralı demektir { sirali=0; //şayet giriyorsak sıralı değil demektir tmp=A[j-1]; A[j-1]=A[j]; A[j]=tmp; } } if(sirali)//şayet dizinin üstünden geçtiğimiz halde //hiç bir değer yer değiştirmiyorsa // dizi sıralıdır döngüden çıkılabilir break; } }
Yukarıdaki iyileştirmenin yanında ilave bir iyileştirme daha yapabiliriz. Bu ise dizideki sabit değerlerdir.
Yukarıdaki döngü dikkat edilirse n boyutundaki bir dizi için n x n = n2 kere döner. Aslında buna gerek yoktur. Sebebi dizinin her geçişinden sonra (pass) belirli değerlerin sabit şekilde sona atılması ve bu değerlerin üzerinden tekrar geçilmesine gerek olmamasıdır.
Bu durumu örnek bir sıralama üzerinden anlamaya çalışalım. Sıralamak istediğimiz dizi aşağıdaki şekilde olsun:
4,5,2,9,6,1,8,7
Şimdi kabarcık sıralaması algoritması gereği dizinin üzerinden bir kere geçelim:
4,2,5,6,1,8,7,9
ilk geçişten sonra görüleceği üzeren en büyük sayı en sona atılmıştır. Sıralamaya devam edelim ve bir geçiş daha yapalım:
2,4,5,1,6,7,8,9
ikinci geçişten sonra sondaki iki sayı her zaman ve her durum için dizideki en büyük iki sayıdır.
Bu gerçeği farkettikten sonra artık diyebiliriz ki her geçişte bütün diziye bakılmasına gerek yoktur. Çünkü dizinin sonundaki elemanlar zaten yer değiştirmeyecektir.
Bu durumda dizinin üzerinden geçen içteki döngümüzü dizi boyutunda değil, dizi boyutundan eleman sayısı noksan değere kadar sıralamalıdır. Sebebi örneğin 3. geçişte sondaki 3 elemana bakılmasına gerek olmamasıdır.
Kodumuzu yeniden düzenleyelim:
public void bubblesort(int [] A) // bir diziyi parametre alan fonksiyon { int tmp; for(int i=0; i<A.length; i++) { int sirali=1; for(int j=A.length-1 ; j>i;j--) //i. geçiş için i eksik dönüş { if(A[j-1]>A[j]) //şayet buraya girmiyorsak dizi sıralı demektir { sirali=0; //şayet giriyorsak sıralı değil demektir tmp=A[j-1]; A[j-1]=A[j]; A[j]=tmp; } } if(sirali)//şayet dizinin üstünden geçtiğimiz halde //hiç bir değer yer değiştirmiyorsa // dizi sıralıdır döngüden çıkılabilir break; } }
Yukarıdaki yeni kodda, içteki döngü dikkat edilirse dıştaki döngü değişkeni kadar eksik dönmektedir. Dolayısıyla 0. geçişte 0, 1. geçişte 1 ve n. geçişte n eleman eksik dönecektir. Çünkü n. geçişte sondaki n eleman zaten sıralı olacak ve kontrol edilmesine gerek olmayacaktır.
Bu durumda kabarcık sıralamamızın algoritma performansını tekrar analiz edecek olursak
En iyi durumda (best case analysis) : n olur çünkü bütün dizi zaten sıralıysa dizideki her elemana birer kere bakarak döngü kırılır ve bir daha dizinin üzerinden geçilmez.
En kötü durumda (worst case analysis): n2 olur. Sebebini şu şekilde açıklayalım. Her geçişte geçiş sayısı kadar elemana bakılması gerekir. Örneğin eleman sayısı n olan bir dizi için k. geçişte n-k elemana bakılır. Dolayısıyla 0. geçişkte n elemana 1. geçişte n-1 elemana son geçişte ise n-n yani 0 elemana bakılır. Bu durumda toplam sayı 1′den n’ya kadar olan sayılrın toplamıdır ve
n x (n+1) / 2
elemana bakılması gerekir. Bu durumda O(n2) değeri bulunur (upper bound olduğu için).
SORU-127: Matris Mod (Masfuf Hali,Matrix Mod)
Bir matriksin modulosunu alma işlemidir. Basitçe bir dizide bulunan sayılardan en fazla tekrarlı olanını döndürür. Şayet sayılardan hiçbirisi tekrar etmiyorsa o zaman bu matriksin mod’u yoktur. Şayet aynı sayıda tekrar eden iki farklı sayı bulunuyorsa o zaman matriksin mod’u bu sayıların tamamını içeren bir dizidir.
Örnekler:
int[] a = {4,5,7,3,2,5,6,8}
mode(a) = {5}
int[] b = {4,6,3,4,7,8,2,7}
mode(b) = {4,7}
SORU-128: Hamming Mesafesi (Hamming Distance)
Hamming mesafesi bilgisayar bilimlerinde aynı uzunluktaki iki dizgi (string) arasında, birbirine dönüşmesi için gerekli olan yer değiştirme sayısını verir. Yani basitçe bir dizginin diğer dizgiden ne kadar farklı olduğunu gösterir.
Örneğin aşağıda bazı dizgiler arasındaki hamming mesafesi verilmiştir:
100011101 <-> 100101101 = 2
düğün <-> düşün = 1
Bu durum bir küp şeklinde düşünülebilir. Örneğin 3 ikillik (bit) bir sayısının hamming mesafesi aşağıdaki küpte gösterilmiştir:
Yukarıdaki şekilde dikkat edilirse küpün 8 köşesine 3 ikillik(bit) sayının alabileceği her ihtimal yazılmıştır. Bu ihtimaller arasında geçiş için gereken değiştirme miktarı hep 1′dir dolayısıyla bu küp hamming mesafesini gösteren bir küp olarak düşünülebilir.
Örneğin 010 sayısının 111 sayısına dönüşmesi küp üzerinde iki mesafe gerektirmektedir ve iki ayrı yoldan (011 veya 110) üzerinden uaşılabilmektedir.
SORU-129: Fibonacci Arama Algoritması (Fibonacci Search Algorithm)
Bu arama algoritması, özyineli (recursive) bir seri olan fibonacci sayılarını kullanarak sıralı bir dizi üzerinde arama yapmaktadır.
Çalışma mantığı arama yapılacak olan sıralı diziyi fibonacci sayılarını kullanarak parçalara bölmektir. Örneğin arama yapılacak olan alanın en fazla 2147483647 değerine sahip olabildiği integer alan olsun. Bu durumda bu sayıya ulaşana kadar olan fibonacci sayılarına ihtiyaç duyulacaktır:
{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, INT_MAX};
Bu sayılar arama alanını parçalara bölmektedir. Öncelikle aranan sayının, en büyük fibonacci sayısından büyük mü küçük mü olduğu kontrol edilir. Buna göre ilk kontrolde 1836311903 sayısından küçük ise bu sefer 1134903170 sayısından küçük olup olmadığı kontrol edilir.
Şayet aranan sayı büyük ise bu sefer serinin 2 önceki sayısı mevcut sayıya eklenir. Örneğin sayımızı bulmak için 196418 sayısından büyük olup olmadığını kontrol ederken sayının büyük olduğu görüldü bu durumda aradığımız sayının 196418 indeksinden sağda olduğunu biliyoruz. Ancak bir önceki sayı olan 317811′den de küçük olduğu biliniyor bu durumda 196418 + 75025 = 271443 sayısından büyük olup olmadığı kontrol edilir ve bu işlem böylece gider.
Yani özetle fibonacci sayılarının arasındaki aralık serideki her sayıda artmaktadır. Fibonacci araması ise bu işi terse çevirerek en büyük aralıktan en küçüğe doğru aralığı daraltarak arama işlemi yapar. En sonunda aralık 1′e inince sayı bulunur (ya da sayı seride yer almıyorsa bulunmadığı anlaşılır).
Bu arama algoritması bir parçala fethet (divide and conquere) yaklaşımıdır. Benzer bir arama yöntemi olan ikili arama (binary search) ile aynı algoritma karmaşıklığına sahiptir O(log n) ancak yapılan matematiksel çalışmalarda fibonacci aramasının daha hızlı olduğu gösterilmiştir.
SORU-130: Özyineli Fonksiyonlar (Recursive Functions)
Fonksiyonlar tekrarlama yapılarına göre temel olarak iki türlü düşünülebilir. Buna göre bir fonksiyonun içinde yine kendisinden bir parça bulunuyorsa bu fonksiyonlara özyineli (recursive) fonksiyon denilirken, fonksiyonun kendisini tekrar etmemesi durumunda döngülü (iterative) fonksiyon ismi verilir.
Teorik olarak bütün döngülü (iterative) fonksiyonlar özyineli (recursive) fonksiyon olarak yazılabilir (tersi de doğrudur).
Öreğin 1′den verilen sayıya kadar olan sayıları toplayan bir fonksiyonu hem özyineli hem de döngülü olarak yazalım:
//özyineli olarak:int topla(int a){ if(a==1) return 1; return a+ topla(a-1);}//döngülü olarakint topla(int a){ int toplam= 0; for(int i = 0 ;i toplam= toplam+a; } return toplam;}
Dikkat edilirse ilk foksiyonun içerisinde kendisini çağıran bir satır bulunmaktayken ikinci fonksiyonda çözüm bir döngü yardımı ile (for döngüsü) yapılmıştır.
Ekrana 1′den 10′a kadar olan sayıları bastıran kod (Fatih Bey’in sualine cevaben)
Örneğin ekrana 1′den 10′a kadar olan sayıları bastıran bir kod yazmak isteyelim. Kodu özyineli (recursive) bir fonksiyonla yazdıralım ve kullanıcı aslında ekrana kaça kadar basılmak istediğini fonksiyona parametre olarak geçirsin (bu soruda 10 olacak)
Aşağıdaki kodu ele alalım:
Yukarıdaki kodun çalışan hali aşağıda verilmiştir:
Görüldüğü üzere kod ekrana 1′den 10′a kadar olan sayıları bastırmıştır.
Burada ekrana bastırma işlemini yapan fonksiyon koddaki 4-8 satırlar arasında tanımlı olan f() fonksiyonudur. Fonksiyon bir n değerinden başlayarak ekrana anlık olarak n değerini bastırmış ve her adımda n-1 değeri ile fonksiyonu tekrar çağırmıştır.
Şayet yukarıdaki şekilde bir if kontrolü ile çağırmayı engellemek yerine her adımda kendisini tekrar çağırmasını isteseydik aşağıdaki şekilde kodu yazabilirdik:
Kodda görüldüğü üzere özyineli olarak fonksiyonun kendisini çağırması işlemi ve ekrana printf fonksiyonu marfietiyle yazma işlemi aynı sırada verilmiştir. Ancak fonksiyonun bitişini belirten if kontrolü, n değerinin 0 olması durumunda return 0; yaparak işlemi durdurmuştur.
Burada return 0 yapılmasının veya return 1234 yapılmasının bir önemi yoktur, fonksiyon herhangi birşey return ederek artık bittiğini belirtmektedir.
Yukarıdaki kod benzer şekilde return void yapacak halde yani integer bir değer döndürmeyecek halde yeniden yazılabilir:
Yukarıdaki kodun yeni halinde fonksiyonun döndürdüğü değer tipi int yerine void haline gelmiştir. Bunun sebebi aslında fonksiyonun geri değer döndürmüyor oluşudur. Ayrıca fonksiyondaki 6. satırda return teriminden sonra bir değer bulunmaz, yani sadece return edilir ve fonksiyon nihayete erer.
Yukarıdaki bu özyineli fonksiyonlarda 3 temel unsur her zaman dikkate alınmalıdır:
- başlangıç değeri
- bitiş değeri
- adım değeri
Yukarıdaki basit 1′den 10′a kadar olan sayıları basan kodda başlangıç değeri 10 olarak kullanıcı tarafından veriliyor. Ayrıca özyineli devam işlemi 0′a ulaşınca bitiyor. Bunu kontrol eden bir if kodun ilk satırında bulunuyor. Kodun her adımında n-1 değeri fonksiyona yeniden veriliyor. Bu değer de adım değerini oluşturuyor.
Örneğin yukarıdaki fonksiyonu yine özyineli bir görüntü altında iteratif olarak yazabiliriz. Bu yazılış şekline geçiş tarzı şekli (continuation by passing style) ismi verilir:
Yukarıdaki kodun yeni halinde, 12. satırd bulunan f fonksiyonuna dikkat edilirse, 10′a kadar olan sayılar 1′den başlanarak basılsın diye iki ayrı parametre verilmiştir. Fonksiyonumuz i değerini her adımda arttırmakta ve n değerine eşit olunca durmaktadır. Durma işlemi yine bir önceki fonksiyonda olduğu üzere void değer return edilerek olmaktadır.
Yukarıdaki kod aslında iteratif bir koddur, bunun sebebi fonksiyonlardan herhangi birisinin çalışması için kendi içerisinden çağırdığı f(n,i+1) fonksiyonun çalışması beklenmemektedir. Ancak döngü ile yazılmış bir fonksiyondan da farkldır.
Ekrana Yıldızlar kullanılarak X harfi basan kod (Duygu Hanımın talebi üzerine ekliyorum)
Amacımız ekrana aşağıdaki şekilde bir X harfi basmak olsun:
* * * * * * ** *
Yukarıda görülen şekil kullanıcıdan 5 girişi için çizilen şekildir. Bizim amacımız bu şekli çizen bir kodu özyineli (recursive) fonksiyonlar marifetiyle ekrana çizdirmek.
Yukarıdaki şekil 2 boyutlu bir şekil olduğu için hem satır hemde sütun boyutlarında işlem yapmamız gerekiyor. Öncelikle satır boyutunda çözüm üretip sonra bu satır işlemini tekrarlayan bir işlemle şekli çizdirebiliriz.
Satırlara dikkat edilirse her satırda 2 yıldız ve 3 boşluk bulunuyor. Bu durum nxn boyutunda bir x harfi için n-2 boşluk ve 2 yıldız olarak formüllenebilir.
Yukarıdaki X harfinde dikkat edilirse bir karenin iki köşegeni * diğer değerler ise boşluk olarak çizilmiştir. Bu durumda karenin köşegenlerini nasıl bulacağımızı düşünürsek, satırları tutan bir i değişkeni ve sütunları tutan bir j değişkeni için i==j ve i==n-j durumlarında köşegen değerleri bulunduğu görülür.
Bu durumu aşağıdaki i ve j indislerinin yazılı olduğu tabloda görebiliriz:
|
i,j |
1 |
2 |
3 |
4 |
5 |
|
1 |
1,1 |
1,2 |
1,3 |
1,4 |
1,5 |
|
2 |
2,1 |
2,2 |
2,3 |
2,4 |
2,5 |
|
3 |
3,1 |
3,2 |
3,3 |
3,4 |
3,5 |
|
4 |
4,1 |
4,2 |
4,3 |
4,4 |
4,5 |
|
5 |
5,1 |
5,2 |
5,3 |
5,4 |
5,5 |
Yukarıdaki tablodan da anlaşılacağı üzere her satır için bulunduğumuz satır numarası (i) ve karenin boyutu- satır numarası (n-i) koordinatlarına * sembolü koyuyoruz. Bunu yapan bir özyineli fonksiyonu (recursive function) aşağıdaki şekilde kodlayabiliriz:
Yukarıdaki kodda, n kullanıcının girdiği ve X harfinin boyutlarını tutan değişken, i satır, j ise sütun sayısı olmak üzere kodun 5. satırında bulunan if döngüsü i ve j ikilisinin köşegen olup olmadığını kontrol etmektedir. Şayet köşegen olan bir hücre ise * koymakta diğer durumlarda da boşluk koymaktadır.
Yukarıdaki fonksiyon her satır için yeniden çalışmakta ve n+1 değerine ulaştığında hiçbir işlem yapmadan nihayete ermektedir.
Yukarıdaki yeni fonksiyon, ilk fonksiyonu n kere çağırmakta ve her çağırma işleminde satır sayısı olan i değişkeninin değerini 1 arttırmaktadır. Böylelikle ilk fonksiyonumuz hangi satır için yazma işlemi yaptığını bilmektedir.
Yukarıdaki iki fonksiyonu birleştirip main fonksiyonu ekleyince aşağıdaki şekilde olur:
Kodun çalışan hali ise aşağıda verilmiştir:
Bir dizgiden (String) boşlukları temizleyen kod
Gelen bir soru üzerine bir dizgideki (string) boşlukları temizleyen kodu yazıp yayınlıyorum:
Kodu kısaca anlatacak olursak. Öncelikle boslukTemizle fonksiyonumuzun üç farklı durum için kodlandığını söyleyebiliriz.
- parametre olarak verilen dizgi boş olabilir. Bu durumda dizgi göstericimizin gösterdiği değer, dizgi sonu olan karakteridir (end of string). Bu kontrol farklı şekillerde de yapılabilir. Örneğin if(strlen(dzgi)==0) kontrolü de dizginin sonuna geldiğimizi anlatır. Dizgi boşsa veya özyineli fonksiyonumuz marifetiyle, dizginin sonuna kadar geldiysek, yine boş bir dizgi döndürerek bu durumu çözüyoruz.
- parametre olarak verilen dizginin ilk karakteri boşluk karakteridir. Bu durumda, bu karakterin sonuçta bulunmaması gerekir ve dolayısyla bu karakter atlanarak fonksiyonumuz özyineli bir şekilde çağırılır ve işlenmeye devam eder. Yine çeşitlilik olması açısından buradaki kontrol de if(strncmp(dizgi,” “,1)) şeklinde yapılabilir.
- parametre olarak verilen dizgi, yukarıdak iki durum dışında bir karakter ile başlıyordur. Bu durumda dizginin sonraki karakterlerine bakılması için dizginin ilk karakteri dışındaki karakterleri yeniden fonksiyona verilir. Ancak çıkan sonucun başına, şu andaki karakter eklenir. Dolayısıyla dizginin geri kalan elemanları, yeniden verildikleri fonksiyonun içerisinden, boşlukları temizlenmiş olarak dönecek ve şu anda bakılan karakter bu dönen dizginin başına eklenecektir.
SORU-131: Bağımsız düğümler (Anti Clique, Independent Set)
Klik yapısının tersi olarak düşünülebilir. Basitçe bir grafta birbiri ile doğrudan bağlantısı olmayan düğümlerin oluşturduğu alt graftır.
Yukarıdaki tasvirde iki adet graf verilmiştir. Üstte bütün graf görülmekte altta ise bu grafın bir alt grafı görülmketedir. Dikkat edilirse sadece altta bulunan {A,E,F} düğümleri alındığında aşağıdaki graf elde edilir ve bu grafta bulunan düğümleri birbirlerine bağlayan kenar bulunmamaktadır.
Graf teorisinde her iki düğümü birbirine bir kenar ile bağlanmış alt graflara verilen isimdir. Örneğin aşağıdaki grafikte bir klik kırmızı çizgiler ile işaretlenmiştir. Buna göre {A,B,C,D} alt grafı bir kliktir.
Sosyal bilimlerde de aynı kelime(klik) bir toplumun en alt birimine verilen isimdir. Bunun sebebi doğrudan bağlantısı olan ve komşuluğu bulunan bireylerden oluşması olarak yorumlanabilir.
Klik yapısının tersi için bağımsız düğümler veya anti clique veya independent set terimleri kullanılır.
SORU-133: İstikra ile ispat (Tüme varım, Proof by Induction)
Bir kaziyeyi (önerme) ispat ederek nazariye (teorem) elde etme yöntemidir. İstikra cüz’îler (tikeller) den küllî
(tümel) ye gitme yöntemidir dolayısıyla örneklerden yola çıkarak her zaman için geçerli bir sonuç elde ederek ispat yapılır.
Her istikra için bir esas(basis) bir de istikra(induction) safhası(step) bulunur. Bu iki safhanın ispatı bütün durumların ispatı demektir. Çünkü isikra edilen herşeyin dayanacağı bir esas bulunmalıdır.
Bu durum şöyle bir örnek ile de ifade edilebilir örneğin iki ayaklı bir sandalyenin ayakta durabilmesi için başka bir iki ayaklı sandalyeye dayanması gerekir, onunda ayakta durması için bir diğerine ve böylece bütün sandalyelerin bir diğerine dayanaması gerekir. En başta ise bir adet 4 ayaklı sandalyeye ihtiyaç vardır ki bütün birbirine dayanan sandalyeler gelip bu sandalyeye dayansın. İşte şayet en başta bulunan bu 4 ayaklı sandalye yanı esas safhası (basis step) ispat edilirse ve her sandalyeninde birbirine dayanarak ayakta duracağı yani istikra safhası (induction step) ispat edilirse istenilen sayıdaki sandalyenin ayakta durabileceği ispatlanmış olur.
Daha sayısal bir örnek için 0′dan n’e kadar olan sayıların toplamının (n * (n + 1) ) / 2 olduğunu ıspatlayalım.
bu ispattaki esas safhamız (basis step) n = 1 durumu olsun ve bu durumda (1 * ( 1 + 1) ) / 2 = 2/ 2 = 1 olarak denklemimizin doğru çalıştığını gösterebiliriz.
istikra safhamız (induction step) ise n + 1 için olan durum olmalıdır. Bu durum da ( ( n + 1) * ( (n + 1) + 1)) / 2 olur. daha sade bir şekilde ( ( n + 1 ) * ( n + 2) ) / 2 yazılabilir.
şayet istikra safhamızı ispatlayabilirsek esas safhamızdaki ispata dayandırarak istikra edebiliriz ( bütün durumlar için genelleme yapabiliriz).
şayet n sayının toplamı (n * (n + 1) ) / 2 ise n+1 sayının toplamı da (n * (n + 1) ) / 2 + ( n + 1) olmalıdır.
isitkra safhasında elde ettiğimiz sonuç da n+1 sayının toplamını vermektedir o halde :
( ( n + 1 ) * ( n + 2) ) / 2 = (n * (n + 1) ) / 2 + ( n + 1)
eşitliği doğru olmalıdır denilebilir. Bu eşitlik çözülecek olursa sonucun 1= 1 doğruluğu ile ispat edildiği görülür.
Dolayısıyla bir esas alınmış ve ispat edilmiştir. Ayrıca bu esasa bina edilen istikra adımları da ispat edilmiştir. Dolayısıyla bütün pozitif sayılar için doğru sonuç veren bir istikra yapılmıştır. Bu duruma tam istikra da denilebilir.
SORU-134: burhan-ı mütenakıs (proof by contradiction, olmayana ergi)
Çok kullanılan ispat yöntemlerinden birisidir. Buna göre ispatlanmak istenen kaziyenin (önermenin) tersinin yanlışlığı ispat edilirse sonuca ulaşılmış ve bir nazariye (teorem) elde edilmiş demektir.
Basit bir günlük örnek şu şekilde verilebilir. Örneğin Ali, Ahmetin kapıdan girdiğini gördü ve Ahmet’in elbiselerinin kuru olduğunu gördü. Ali yağmur yağmadığını ispatlamak için yağmurun yağdığını farz edebilir. Bu durumda “Şayet yağmur yağsaydı Ahmet’in üzeri ıslak olurdu” çıkarımını yapar. Sonuç olarak Ahmet’in üzeri kuru o halde yağmur yağmıyor çıkarımı ile yağmurun yağmadığını ispatlamış olur.
Farklı bir örnek ise √2 sayısının kesirli ifadesinin imkansız olduğunu (irrasyonel sayı olduğunu) göstermek olabilir.
Bu durumda öncelikle kaziyenin (önermenin) tersini doğru kabul etmek gerekir ve farz edelim ki √2 sayısı kesirli bir sayı olsaydı o halde
√2 = n/m şeklinde bir kesir ile ifade edilebilirdi.
Kesirli sayılarda bilindiği üzere pay ve paydadaki sayılar tam sayı olmak zorundadır. Ayrıca yine hatırlanırsa kesirli sayılarda hem pay hem de paydadaki sayı aynı tam sayıya bölünürse değer bozulmazdı. Bu durumda m ve n sayılarının aynı anda çift sayı olamayacağını kabul etmek gerekir (velev ki çift olsalardı 2 ile sadeleştirilirler ve yine çift olmayan bir sonuca ulaşılırdı)
Eşitliğin her iki tarafını n sayısı ile çarparsak eşitlik bozulmaz:
n √2 = m
Her iki tarafın karesi alınırsa yine eşitlik bozulmaz
2n2 = m2
Yukarıdaki eşitlik dikkatlice inelendiğinde m2 ifadesi başka bir ifade olan n2 ifadesinin 2 mislidir. Bilindiği üzere bir ifade başka bir ifadenin 2 misli ise bu ifade çift sayıdır. Yani yukarıdaki örnekte bulunan m2 sayısı çift sayı olmak zorundadır.
Ancak ne yazık ki başta kabul ettiğimiz ve m sayısının tek sayı olduğu gerçeği ile bu durum tenakuza düşmektedir (çelişmektedir) çünkü hiçbir tek sayının karesi çift olamaz. Bu durumun daha doğru gösterimi aşağıdaki şekilde yapılabilir:
m sayısı çift sayı ise m=2k şeklinde yazılabilir.
o halde 2n2 = (2k)2 şeklinde önceki eşitlikte yerine konulması da doğru olmalıdır.
2n2 = 4k2 eşitliğinde her iki tarafa da 2′ye bölnürse n2 = 2k2 eşitliği elde edilir.
Bu durum ise daha önce de ifade edidliği üzere n2 sayısının çift olması demektir.Yine hatırlanırsa hiçbir tek sayının karesi çift olamaz. Ve yine hatırlanırsa aynı anda hem n hem de m çift olamaz.
Görüldüğü üzere yukarıdaki iki örnekde de öncelikle ıspatlanmak istenen kaziyenin (önermenin) tersinin doğru olduğu farz edilmiş ve bu faraziyenin yanlışlığı ispatlanmıştır. Bu yanlışlık gösterilirken de iki farklı yargı elde edilip bu yargıların tenakuza (çelişkiye) düşmelerinden faydalanılmıştır.
Unutulmamalıdır ki bu yöntemin geçerli olabilmesi için ikili mantık içinde çalışmak gerekir (yani doğru ile yanlışın birbirinin tersi olduğu mantık, 3. bir ihtimalin olmadığı mantık).
SORU-135: Binaen Burhan (İnşâa ile İspat , Proof by Construction, Binaenaleyh)
Bilgisayar bilimlerinde kullanılan ispat yöntemlerinden birisidir. Bu yöntemde bir varlığın oluşmasının gösterilmesi hedeflenir. Örneğin aşağıdaki teoriyi inşaa yöntemi ile ispat edelim:
“2′den büyük her çift n sayısı için n düğüm içeren 3-düzenli graf bulunur”
Öncelikle k-düzenli graf tanımını hatırlayalım:
Bir graf üzerindeki her düğümün “k” kadar komşusu bulunması durumuna k-düzenli graf denilir. Örneğin aşağıdaki graf 2-düzenli bir graftır çünkü her düğümün derecesi 2′dir.
Dolayısıyla ispatlanmak istenen nazariyede bize düğüm derecelerinin en az 3 ve daha fazla olabildiği bildirilmiş bir k-düzenli graf isteniyor.
Bir graftaki kenarları aşağıdaki küme ile ifade etmek mümkündür:
K =
{ {i,i+1} | 0 ≤ i ≤ n-2} U { n – 1 , 0 } }
U { {i, i+ n/2 } | 0 ≤ i ≤ n/2 – 1 }
Yukarıda iki satır olarak tanımlanan kenarlar kümesindeki ilk satırı komşu düğümleri gösteren satır ve ikinci satırı karşı düğümleri gösteren satır olarak düşünebiliriz. Buna göre n adet düğüm içeren bir grafta her düğüm içi kendisinden önce ve kendisinden sonraki düğümlere birer kenar olduğu kabul edilirse (ilk satır) ayrıca karşısındaki düğüme de bir kenar olduğu kabul edilirse (alttaki satır) bu durumda her düğümün 3. derece olması mümkündür denilebilir.
Yukarıdaki grafta bu durum tasvir edilmiştir. Grafın bir çember olmasını sonsuz sayıda düğüm içermesi olara düğünebiliriz. Bu çeber üzerindeki bir noktayı komşusu olan 2 noktaya bağlayan birer kenar (mavi ile gösterilmiştir) ve tam karşısında buluna noktaya bağlayan(kırmızı ile gösterilmiştir) birer kenar çizilmesi durumunda n > 2 düğümü bulunan herhangi bir graf için 3-düzenli graf bulunabileceğini tasvir etmiş oluruz.
Yukarıdaki örnekte kullanılan ispat yöntemine bakıldığında bir varlığı oluşturan diğer varlıkların ispatta kullanıldığını görürüz. Örneğin 3-düzenli graf kavramını oluşturan bir düğüm ve 3 kenar varlıklarının ayrı ayrı gösterilmesi ve bu varlıkların üzerine ispatın inşası mümkün olmuştur.
SORU-136: k-düzenli graf ( k-regular graph)
Bir graf üzerindeki her düğümün “k” kadar komşusu bulunması durumuna k-düzenli graf denilir. Örneğin aşağıdaki graf 2-düzenli bir graftır çünkü her düğümün derecesi 2′dir.
SORU-137: Sembol (Harf, İşaret, Symbol)
Bilgisayar bilimlerinde kullanılan ve yazıları ifade etmeye yarayan en küçük ifade birimine verilen isimdir. Buna göre bir dildeki olası bütün semboller kullanılarak oluşturulan alfabeler kullanılarak metinlerin elde edilmesi mümkündür.
Bilgisayar bilimlerindeki alfabelerde bulunan semboller sınırlı sayıda kabul edilmiştir. Örneğin aşağıda çeşitli semboller içeren alfabe örnekleri verilmiştir:
∑1 = {0,1}
∑2 = {a,b,c,d,e,f}
Örneğin yukarıdaki ∑1 alfabesi “0″ ve “1″ sayılarını birer sembol olarak kabul etmiştir ve bu semboller dışındaki semboller bu alfabede tanımlı değildir.
Alfabeler kullanılarak elde edilen dizgiler (string) w harfi ile ifade edilir.Buna göre örneğin w=”debdebe”dizgisi ∑2 üzerinde tanımlı bir dizgidir.
Bir dizginin boyutu |w| işareti ile gösterilir ve o dizgideki harf sayısına eşittir. Örneğin yukarıda verilen w=”debdebe” dizgisi için |w| = 7 olarak kabul edilir. Bir dizginin içeriği boş olması durumunda ise |w|=0 olarak kabul edilir.
SORU-138: Güçlü Bağlı Graf (Strongly Connected Graph)
Bir grafta bulunan bütün düğümleri diğer bütün düğümlere bağlayan birer kenar bulunuyorsa bu grafa güçlü bağlı graf adı verilir.
SORU-139: Basit Döngü (Simple Cycle)
Bir graftaki bir döngünün başlangıç ve bitiş düğümleri olan düğümü dışındaki bütün düğümlerin, bu döngü içerisinde sadece bir kere geçmesi durumunda bu döngüye basit döngü adı verilir.
SORU-140: Bağlı graf (conected graph)
Bir graftaki bütün düğümleri diğer bütün düğümlere bağlayan bir yol bulunuyorsa bu graflara bağlı graf denilir.
Graf teorisinde bir düğümden başlayıp aynı düğümde biten yola döngü adı verilir
Örneğin yukarıdaki grafta A düğümünden başlayarak gene bu düğümde biten {A,C,D} döngüsü tasvir edilmiştir.
Bir grafikte bulunan düğüm ve kenarlardan sadece bir kısmını içeren grafa verilen isimdir. Her altgraf da bir graftır. Ayrıca grafın kendisi de altgraflarından bir tanesidir.
Örneğin yukarıdaki şekilde bir graf ve bir alt grafı yanyana gösterilmiştir.
Bir graf üzerinde bir veya daha fazla düğümden ve kenardan geçen rotaya verilen isimdir. Örneğin aşağıdaki graf üzerinde bir yol gösterilmiştir.
Yolların yazılışı ise geçtikleri düğümlerin sırasıyla yazılması ile elde edilir. Örneğin yukarıdaki yolu {A,C,D} olarak göstermek mümkündür.
SORU-144: Yönlü Graflar (Directed Graphs)
Bir grafın kenarlarının yön belirtmesi durumunda bu grafa yönlü graf adı verilir.
Bir kenar iki düğümü birleştirmektedir. Yönlü bir kenar ise bir düğümden diğer düğüme gidilebilen yönü göstermektedir. Bu kenarın gösterdiği yönün tersine doğru da hareket edilebilmesi durumunda bu ikinci bir kenar ile ifade edilir.
Yukarıda A ile B düğümleri arasında her iki yönde de hareket edilebildiğini gösteren iki adet kenar bulunmaktadır. Bu kenarlardan birsinin bulunmaması durumunda;
tek yönlü hareket etmek mümkün olurken tersi yönde hareket mümkün değildir. Örneğin yukarıdaki şekilde A düğümünden B düğümüne geçiş mümkün iken tersi olan B düğümünden A düğümüne hareket edilememektedir.
SORU-145: Özetleme Fonksiyonları (Hash Function)
Özetleme fonksiyonlarının çalışma şekli, uzun bir girdiyi alarak daha kısa bir alanda göstermektir. Amaç girende bir değişiklik olduğunda bunun çıkışa da yansımasıdır.
Buna göre özetleme fonksiyonları ya veri güvenliğinde, verinin farklı olup olmadığını kontrol etmeye yarar ya da verileri sınıflandırmak için kullanılır.
Anlaşılması en basit özetleme fonksiyonu modülo işlemidir. Buna göre örneğin mod 10 işlemini ele alalım, aşağıdaki sayıların mod 10 sonuçları listelenmiş ve gruplanmıştır:
Sayılar: 8,3 ,4,12,432,34,95,344,549,389,2339,349,54,81,17,62,94,67,44,9
|
Demet (Buket, Bucket) |
Sayılar |
|||||
|
0 |
||||||
|
1 |
81 |
|||||
|
2 |
12 |
432 |
62 |
|||
|
3 |
3 |
|||||
|
4 |
4 |
34 |
344 |
54 |
94 |
44 |
|
5 |
95 |
|||||
|
6 |
||||||
|
7 |
17 |
67 |
||||
|
8 |
8 |
|||||
|
9 |
389 |
2339 |
349 |
9 |
||
Kısaca yukarıdaki sayıların hepsi 1 haneli bir sayıya özetlenmiştir. Örneğin 81 -> 1, 344 -> 4 gibi. Elbette aynı sayıya özetlenen birden fazla sayı bulunmaktadır. Bu duruma çakışma (collusion) adı verilmektedir.
Özeteleme fonksiyonlarının ingilizcesi olan Hash kelimesinin kökü arapçadan girmiş olan haşhaş kelimesi ile aynıdır. Ve insan üzerinde yapmış olduğu deformasyondan esinlenerek hash function’a giren bilgilere yapmış olduğu deformasyondan dolayı bu ismi almıştır.
SORU-146: Ağaçlarda Dengeleme (Rotation, Balancing)
En çok karşılaşılan durum, ikili arama ağaçlarında bir düğüm için çocuklarının derinliklerinin 2 olması durumudu. Bu durum aşağıdaki örnekte gösterilmiştir:
Yukarıdaki tasvirde ayrıca bu ağacın dengelenmiş hale nasıl dönüştürüldüğü de gösterilmiştir. Buna göre ağaç sağa dengelenmiş ve ikili arama ağacı özelliği bozulmamıştır. Yani dengelendikten sonra da ağacın sağ kolundaki değerler, sol kolundaki değerlerden büyüktür.
Yukarıdaki şekilde de gösterildiği üzere ağacın kolları arasındaki derinlik farkı 2 olduğunda dengeleme yapılabilir. Bu durum iki türlü olabilir ya sağ kolu daha uzun ya da sol kolu daha uzun olacaktır. Her iki durum içinde yapılan işlemler sola dengele veya sağa dengele olarak sınıflandırılabilir.
Örneğin yukarıda, bir ağacın kolları arasındaki derinlik farkı 2′yi geçmiş ve bu dengesizlikde derin olan taraf sol kolu olmuştur. Buna göre ağacın dengelenmiş halinde, ilk halinde kökte bulunan düğüm sağ kola yerleştirilir.
Benzer bir durumda ağacın sağ kolunun daha derin olamsı halidir. Bu durum da da ağacın kökünde bulunan düğüm sol kola yerleştirilir.
Ağaçlarda dengesiz olan bütün durumlar yukarıda anlatılan örnekte olduğu gibi dengelenebilir. Basitçe şayet ağacımız dengeli yapılmak isteniyorsa ve her ekleme veya silme işleminden sonra bu dengeleme kontrolü çalışıyorsa ağaç dengeli bir halde kalacaktır. Ayrıca ikili ağaçların iki kolu bulunduğu için yukarıdaki durumlar dışında bir durum ile karşılaşılamaz.
Aşağıda ise sadece silme işleminden sonra karşılaşılabilecek bir durum verilmiştir:
Yukarıdaki bu durum ekleme işlemleri sırasında karşılaşılamaz çünkü eklenen her adımdan sonra ağaç dengeli hale getirileceği için C eklenmesi durumunda D, D eklenmesi durumunda ise C dengeli bir ağaca eklenecektir. Ancak silme durumlarında yukarıdaki şekilde dengeleme işlemi yapılabilir. Ağacın sola dengelenmesi gerektiği durum ise yukarıda tasvir edilen halin simetriğidir.
Metin ağaçları, her düğümün kendisinden sonra gelen harfi işaret ettiği ağaçlardır. Basitçe ağacın üzerine bir metin kodlanabilir ve bu metni veren ağacın üzerinde tek bir yol izlenebilir (deterministic). Durum aşağıdaki örnek üzerinde daha rahat anlaşılabilir:
Yukarıdaki ağaçta dikkat edilirse kök düğüm her zaman boş metni (string) ifade etmektedir. Bu boş metin hangi harf ile devam edilirse ilgili kolu takip eder ve gitmiş olduğu düğüm o ana kadar geçmiş olduğu kollardaki harflerin birleştirilmiş halidir. Bir düğümden bir harf taşıyan sadece bir kol çıkabilir.
Trie ağacının ismi retrieval kelimesininin ortasındaki 4 harften gelmektedir.
Metin ağaçlarının (trie), ikili arama ağaçlarına göre en önemli avantajları bir metni aramanın, metin boyutu kadar işlem gerektirmesidir. İkili arama ağaçlarında ise bu süre log n kadar varkit almaktadır. Buradaki n, ağaçtaki düğüm sayısıdır dolayısıyla ikili arama ağaçları, ağaçtaki bilgiye göre hızlı veya yavaş çalışırken, metin ağaçları, ağaçta ne kadar bilgi bulunduğundan bağımsız olarak çalışırlar.
Metin ağaçları hafızayı da verimli kullanırlar çünkü bir metin ağacının en derin noktası, ağaç üzerindeki en uzun metin kadardır. İkili ağaçlarda ise bu derinlik eklenen düğüm sayısına göre en kötü ihtimalle düğüm sayısı kadar olabilmektedir.
Ayrıca metin ağaçları en uzun önek eşlemesi (longest prefix matching) gibi problemlerin çözümünde de avantaj sağlar.
Bir graf şayet bağlı grafsa ve hiç döngü içermiyorsa bu grafa ağaç adı verilir.
Bilgisayar bilimlerinin önemli veri tutma yöntemlerinden birisi de ağaçlardır. Buna göre veriler bir ağaç yapısına benzer şekilde (kök gövde yapraklar) tutulur.
Örneğin yukarıdaki ağaç tasvirinde 7 düğümden (node) oluşan ve yapraklarında (leaf) 4 düğüm bulunan bir ağaç gösterilmiştir. Bu ağacın derinliği (depth) 2 dir ve her seviyenin(level) değeri yanında verilmiştir. Ağaçların 1 tane başlangıç düğümü bulunur ve bu başlangıç düğümüne kök(root) denilir.
Özel olarak yukarıdaki ağacın her düğümünden sadece ikişer alt düğüme bağlantı bulunduğu için bu ağaca ikili ağaç (binary tree) adı da verilebilir.
SORU-149: Dairesel Grup (Cyclic Group)
Grup teorisinde, bir sayıdan bütün grubun üretilebildiği gruplara dairesel grup (cyclic group) ve bu üretmek için kullanılan sayıya üreteç (generator) ismi verilir.
Örneğin G = { g0, g1, g2, g3, g4, g5 } grubu için 6 üye bulunmaktadır ve dairesel bir grup olduğuna göre son elemandan sonra tekrar ilk elemana geri dönülmesi gerekir. Bu durumda g6 = g0 yorumu yapılabilir. Bu gruba benzer bir grup elde etmek için örneğin ardışık sayılarda mod 6 fonksiyonu hayal edilebilir. Buna göre artan ardışık sayıların mod 6 fonksiyonuna göre sonucu G = { 0,1,2,3,4,5 } olmakta ve 5′ten sonra tekrar 0 değerine geri dönmektedir.
Burada yanlış anlaşılması muhtemel bir konu dairesel kavramıdır. Yani dairesel olması grubun mutlaka tekrar içermesini gerektirmez. Grubun içerisindeki bütün üyeler diğer üyelerden farklı ve yine de dairesl bir grup olabilir. Bu duruma uyan dairesel gruplara sonsuz dairesel gruplar (infinite cyclic group) adı verilir.
SORU-150: OWL Time (OWL Zaman, Web Varlıkbilim Dili Zaman)
Gelişen zamanlama ihtiyaçları ile birlikte zamanın gösterimi ve formüllenmesi de bir ihtiyaç haline gelmiştir. Örneğin yapılan her siparişte, siparişin zamanının tutulması, basir bir kiralama işleminde veya bilet satış işleminde yapılan işlemin hangi tarih ve saatler için yapıldığının tutulması artık sıradan birer gereksinim haline gelmiştir. Bu amaçla doğmuş olan OWL Time , Web Ontology Language (Baş harflerinin sırası okunmasını kolaylaştırmak için değiştirilmiş ve OWL olarak kısaltılmıştır) altında zaman göstermek amacıyla XML üzerine kurulu bir dil olarak tasarlanmıştır. Projenin daha önceki ismi DAML-Time olarak geçmekteydi.
Dilin yapısında varlıklar TemporalEntity (zaman varlığı veya fânî varlık olarak isimlendirilmektedir) ve her TemporalEntity altında Instant (kesin, ânî ) veya Interval (aralık, zaman aralığı) olarak iki farklı varlık bulunur.
:Instant a owl:Class ; rdfs:subClassOf :TemporalEntity .:Interval a owl:Class ; rdfs:subClassOf :TemporalEntity .:TemporalEntity a owl:Class ; rdfs:subClassOf :TemporalThing ; owl:equivalentClass [ a owl:Class ; owl:unionOf (:Instant :Interval) ] .
Bu durum yukarıdaki RDF/XML gösterimi ile temsil edilmiştir. Dikkat edilirse Instant ve Interval sınıfları (Class) , TemporalEntity sınıfının (Class) birer alt sınıfıdır (subclass)
Sürelerin İfade edilmesi
OWL-Time kullanılarak sürelerin ifade edilmesi de ayrı bir problemdir. Örneğin bir eylemin süresi 1 gün ve 2 saat veya 26 saat veya 1560 dakika olabilir ve bütün bu zamanlar aynı süreye işaret etmektedir. Dolayısıyla sağlıklı bir zaman gösteriminde Yıl, Ay, Hafta, Gün, Saat, Dakika, Saniye olmak üzere 7 farklı zaman gösterimi gerekmektedir. Ayrıca bu zaman gösterimini belirli eden ilave bir gösterici ile toplam 8lik gösterici kullanılmaktadır. Bu durum aşağıdaki sınıf yapısında gösterilmiştir:
:DurationDescription a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :seconds ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :minutes ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :hours ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :days ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :weeks ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :months ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :years ] .
Yukarıda gösterilen sınıf DurationDescription yani Süre Tanımlayıcı sınıfının yapısıdır. Bu sınıftan üretilen bir örnek aşağıda verilmiştir:
duration a :DurationDescription ; :seconds 20 ; :hours 5 ; :days 15 .
Yukarıdaki örnekte 15 gün 5 saat ve 20 saniyelik bir zaman dilimi temsil edilmiştir.
Yukarıda Süre Tanılmayıcısı (DurationDescription) sınıfının tanımına dikkat edilirse her zaman birimi için ayrıca bir de maxCardinality (azami sayısallık) tanımı yapılmış ve bütün zaman birimleri için bu değer 1 atanmıştır. Bunun OWL dili açısından anlamı bu değerlerin sayısal olarak istedikleri gibi atanabilecekleridir. Ancak OWL dili bilindiği üzere varlıkbilimsel bir dildir ve her zamanın bütün bu birimleri ifade etmesi beklenemez. Örneğin üniversite eğitiminin saniyeler dakikalar cinsinden ifadesi beklenmedik bir durumdur, genelde üniversite eğitimi 2,3,4,5,6 gibi yıllar ile ifade edilir. Veya bir yemek süresi dakika ve saatler mertebesindedir bu sürenin yıllar ile ifade edilmesi beklenmedik bir durumdur. İşte OWL-Time bu gibi durumlarda diğer zaman birimlerinin kapatılmasına imkan verir ve yapılması gereken basitçe bu zaman birimlerinin maxCardinality bilgisini 0 yapmaktır:
:Year a owl:Class ; rdfs:subClassOf :DurationDescription ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty :years ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 0 ; owl:onProperty :months ] ; ... rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 0 ; owl:onProperty :seconds ] .
Örneğin yukarıda bir yıl sınıfı tanımı yapılmıştır ve yıl dışındaki bütün zaman birimlerinin cardinality (sayısallık) bilgisi 0 yapılmıştır.
Tarih ve Saat kavramı
OWL-Time kullanılarak belirli bir tarihin veya saatin ifade edilmesi de gerekmektedir. Örneğin 10:30 bir saat ifade etmektedir buna karşılık 26 mart 2008 ise bir tarih ifade etmektedir. Dolayısıyla herhangi bir zamanı ifade etmek için öncelikle hangi birimden ifadenin yapılacağına karar verilmelidir. Yani ifade edilecek olan zaman bir dakikayı mı bir günü mü yada bir saati mi ifade etmek amacındadır belirlenmelidir. ( “saat 10″ önermesi bir saat ifade ederken “26 mayıs” bir günü ifade etmektedir). Bu belirlenen birime unitType (birim tipi) denilirse geriye zaman diliminin ifade edilmesi sorunu kalmaktadır. Yani istenilen saat acaba hangi zaman dilimine göre (örneğin GMT, EST, PST ) belirtilmektedir.
Son olarak tarih ve saati ifade eden zaman birimleri aşağıdaki şekilde tanımlanabilir:
:DateTimeDescription a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty :unitType ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :second ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :minute ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :hour ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :day ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :dayOfWeek ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :dayOfYear ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :week ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :month ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :year ] ; rdfs:subClassOf [ a owl:Restriction ; owl:maxCardinality 1 ; owl:onProperty :timeZone ] . :hasDateTimeDescription a owl:ObjectProperty ; rdfs:domain :DateTimeInterval ; rdfs:range :DateTimeDescription .
Görüldüğü üzere yukarıdaki gösterimde haftanın günleri veya yılın günü gibi kavramlar da ilave edilerek bir tarih/saat ifadesine yer verilmiştir. İlk eklenen alt sınıf (subclass) bir zaman birmini gösterirken son alt sınıf ise zaman dilimini (timeZone) göstermektedir.
Bu bilgiler paralelinde aşağıdaki toplantı varlığını inceleyelim:
:meetingStart a :Instant ; :inDateTime :meetingStartDescription ; :inXSDDateTime 2006-01-01T10:30:00-5:00 . :meetingStartDescription a :DateTimeDescription ; :unitType :unitMinute ; :minute 30 ; :hour 10 ; :day 1 ; :dayOfWeek :Sunday ; :dayOfYear 1 ; :week 1 ; :month 1 ; :timeZone tz-us:EST ; :year 2006 .
Yukarıda aynı toplantı varlığı için iki farklı gösterim bulunmaktadır. İlk gösterimde XSD zamanı denilen XML Scheme Definition kelimelerinin kısaltılmışı olan ve XML üzerinde tanımlama yapılan dilin biçemine uygun olarak yazılmış gösterime yer verilmiştir. Aynı tarih değeri altta bizim tanımladığımız tarih saat yapısına uygun olarak tekrar gösterilmiştri. Buna göre toplantı dakika cinsinden bir zamana işaret etmekte olup zaman birimi EST’dir ve toplantının zamanı: 1/1/2006 saat 10:30′dur.
OWL Time kullanarak zaman aralığı gösterimi
Bilindiği üzere zaman gösterimlerinde her zaman kesin ve belirli zamanları ifade etmek mümkün değildir. Örneğin bir kargo firmasının teslim süresi olarak 2-3 gün arasında demesi, teslimin en az 2 en çok 3 günde gerçekleşeceğini ifade etmektedir. Bu durum OWL Time kullanılarak aşağıdaki şekilde ifade edilebilir:
:DeliveryDuration a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty :maxDeliveryDuration ] ; rdfs:subClassOf [ a owl:Restriction ; owl:cardinality 1 ; owl:onProperty :minDeliveryDuration ] . :maxDeliveryDuration a rdf:Property ; rdfs:domain :DeliveryDuration ; rdfs:range time:Interval . :minDeliveryDuration a rdf:Property ; rdfs:domain :DeliveryDuration ; rdfs:range time:Interval .
Yukarıdaki gösterimde bir teslim tarihi için iki alt bilgi girilmiş bunlardan birisi minDelivery (asgari teslim) diğeri ise maxDelivery (azami teslim) süresi olarak belirlenmiştir.
Bu belirlemenin yanında 2 günlük ve 3 günlük sürelerde aşağıdaki şekilde tanımlanabilir:
:Interval2Days a owl:Class ; rdfs:subClassOf time:Interval ; owl:subClassOf [ a owl:Restriction ; owl:hasValue P2D ; owl:onProperty time:durationDescriptionDataType ] . :Interval3Days a owl:Class ; rdfs:subClassOf time:Interval ; owl:subClassOf [ a owl:Restriction ; owl:hasValue P3D ; owl:onProperty time:durationDescriptionDataType ] .
Artık bu tanımlamalardan sonra kargo firmasının teslim süresi tanımlanabilir:
:Cargo2-3dayDuration a owl:Class ; rdfs:subClassOf :DeliveryDuration ; rdfs:subClassOf [ a owl:Restriction ; owl:allValuesFrom :Interval3Days ; owl:onProperty :maxDeliveryDuration ] ; rdfs:subClassOf [ a owl:Restriction ; owl:allValuesFrom :Interval2Days ; owl:onProperty :minDeliveryDuration ] .
yukarıda görüldüğü üzere kargo firmasının teslim süresi 2 günlük zaman diliminde asgari ve 3günlük zaman diliminde azami süreler olarak tanımlanmıştır.
OWL-Time kullanarak ne yazık ki belirsiz iki eylemin göreceli zamanlarını tanımlamak mümkün değildir. Örneğin A olayı B olayından sonra olmuştur önermesi OWL-Time kullanımıyla gösterilemez.
SORU-151: TTML (Time Tabling Markup Language, Zaman Çizelgeleme İşaretleme Dili)
Zaman çizelge işareteleme dili (Timetabling Markup Language (TTML)), XML üzerine kurulmuştur. MathML üzerine kurulu zaman çizelgeleme problemlerinin çözümünde kullanılmaktadır.
TTML üzerinde kullanılan formülleme dili küme teorisine dayandırılabilir. Örneğin MathML üzerinde kullanıcılara tamamen yeni semboller ile bu sembollerin ifade ettiği yeni fonksiyon ve formülleri tanımlama imkanı sağlanır. Bu durum MathML kullanan TTML dilinin de küme teorisini desteklemesini sağlamaktadır. Bu sayede kullanıcı esnek bir şekilde kendi kıstaslarını belirleyebilmekte ve zaman çizelgelemesini özelleştirebilmektedir.
Bilindiği üzere zaman çizelgelemesi NP Complete (NP Tam) problem ailesindendir ve zaman çizelgelemesindeki amaç uygun zaman aralıklarına uygun olaylar kümesini yerleştirmektir. Gelişmekte olan şartlara göre zaman çizelgelemede aranan özellikler ve kurallar kümesi de gelişmekte dolayısıyla problemin karmaşıklığı artmaktadır. Bu konuda bir standart bulunmaması ayrıca araştırmacıların karşılaştırma imkanını kaldırmakta ve mukayeseli sonuçlar çıkarılmasını zorlaştırmaktadır. Bu konuda ilk çalışma Andrw Cumming tarafından ICPTAT’95 ‘de yapılmış ve çalışmanın sonucu olan dile SSTL ismi verilmiştir. Beklenildiği üzere SSTL dili de bir standartlaşmayı sağlayamamıştır çünkü elde bulunan problemlerin bu dile çevrilmesi tam anlamıyla mümkün olmamıştır. Günümüzde neredeyse her araştırmacı kendi gösterimini kullanmakta ve bu konuda bir standartlaşma henüz sağlanamamaktadır.
SORU-152: Arc Constraint (Kiriş Şartı)
AC3 algoritması olarak bilinen ve kiriş tutarlılıklarını (arc consistency) işlemek için kullanılan algoritma anlatılacaktır. Şart işleme (Constraint Processing) konusunda kullanılan önemli yöntemlerden birisidir. Bu algoritmanın AC1 ve AC2 diye adlandırılan daha eski ancak performans olarak daha kötü çözümleri bulunduğu gibi AC4, AC5 gibi daha başarılı ancak uygulaması daha zor ve algoritması daha karmaşık çözümleri de vardır.
AC3 algoritmasında amaç birer kiriş listesi olarak verilen şartların çözümüdür. Yani algoritmanın girişi bir grafik gibi düşünülebilir ve grafiğin üzerinde hangi varlıktan hangi varlığa şart bulunuyorsa işaretlidir.
Bu algoritmanın detaylarına geçmeden önce 4 vezir problemi diye bilinen problemin tanımına ve modellenmesine göz atabilirsiniz.
SORU-153: 4 vezir problemi (4 queen problem)
Satranç oyunundan esinlenerek üretilen bu problemde 4×4′lük bir satranç tahtasına (orjinal satranç tahtası 8×8′lik olmakla beraber problem tahtanın küçültülmüş bir kısmında tasarlanmıştır) 4 adet vezirin birbirini yemeden nasıl yerleştirileceği tartışılır. Bilindiği üzere satranç oyununda vezirler satır sütün veya diyagonal olarak (ileri, geri, sağa, sola veya istenilen bir çapraz yöne) istedikleri kadar hareket edebilmektedirler.
Dolayısıyla örneğin aşağıdaki kırmızı yıldız ile gösterilen köşeye bir vezir konulması durumunda, daireler ile kapatılan karelere vezir konulması imkânsızdır.
Problemin modellenmesinde kiriş şartlarından (arc constraints) faydalanılabilir. Buna göre yukarıdaki resimde gösterilen vezir yerleştirme durumu aşağıdaki kirişleri doğurur:
yukarıdaki şekilde, tahta üzerine yerleştirilmiş olan vezirlerin 4 değişken (variable) için gösterimi yer almaktadır. Buna göre her satırı bir X değişkeni ile gösterecek olursak, Örneğin 3. satır için yani X3 için vezir konulabilecek sütünlar 2 ve 4 numaralı sütunlardır.
Dolayısıyla 4 vezir problemi 4 satırı gösteren 4 değişken ve her değişkene karşılık gelen değerler ile gösterilmiştir. Problem ilk başta bütün ihtimalleri kapsamaktadır. Yani 4 satırda da 1,2,3,4 numaralı sütunlara vezir konulması mümkündür. Ancak sol üst köşeye vezir konulduktan sonra yukarıdaki şekilde kirişlerin güncellenmesi gerekir.
SORU-154: Belirsiz Çokterimli Tam (NP-Complete, Nondeterministic Polynomial Complete)
Bilgisayar bilimlerinde problem sınıflamada kullanılan sınıflardan birisidir. Bu sınıfa giren problemler için çözümleme zamanı arttıkça artan (super increasing) yapıya sahip olmaktadır. Buna göre her adımdaki çözümleme zamanı kendinden çözümleme zamanlarından daha fazladır.
Problem yapı olarak artan zamanda çözüldüğü için de bu problem tiplerinin çokterimli zamanda (polynomial time) çözülmesi mümkün değildir. Bu problemin tanımında ayrıca Turing Makinesinden de yararlanılır.
Turing makineleri beliri (deterministic) ve belirsiz (nondeterministic) olarak ikiye ayrılır. Buna göre NP-Complete bir problem Belirsiz Turing Makinesi tarafından belirli zamanda çözülebilmektedir.
Ayrıca problemleri kümelerken diğer bir küme olan P kümesi yani çokterimli (polynomial) kümesi, NP kümesinin bir alt kümesi olarak kabul edilir.
Bir problemin NP kümesinde olduğunu ispatlamanın yollarından birisi de bu problemi NP kümesinde bulunan başka bir probleme dönüştürmektir.
SORU-155: Açgözlü Yaklaşımı (Greedy Approach)
Algoritma üretme yöntemlerinden birisi olan açgözlü yaklaşımına göre mümkün olan ve sonuca en yakın olan seçim yapılır. Yani basitçe bir seçim yapılması gerektiğinde sonuca en çok yaklaştıracak olan seçimin yapılmasını önerir. Ancak mâlum olduğu üzere bu seçim her zaman için en iyi seçim değildir.
Örneğin para üzeri verilmesi (coin exchange problem) için açgözlü yaklaşımının kullanılmasını düşünelim. Bu problemde bir satıcı kendisinden alışveriş yapan kişiye para üzeri vermektedir. Ödenmesi gereken miktar bu örnekte 24 olsun ve para birimlerimiz 20, 19, 5, 1 olsun. (yani para birimi olarak bu para birimleri bulunuyor)
açgözlü yaklaşımına göre satıcı 24′ü tamamlamak için elindeki para birimlerinden sonuca en çok yaklaştıran 20lik birimi seçecektir. Daha sonra geriye kalan boşluğu (24-20=4) doldurmak için elindeki tek imkan olan 4 tane 1lik birimle dolduracaktır ve toplamda 5 adet bozuk parayı müşteriye geri verecektir. Oysaki aynı problem bir 19luk bir de 5lik bozuk paralar ile çözülerek 2 bozuk para vermek mümkün olabilirdi.
SORU-156: Torba Problemi (knapsack problem)
Torba problemi basitçe bir torbanın içerisine en fazla eşyanın yerleştirilmesini hedefler. Problem hırsız örneğinden daha iyi anlaşılabilir. Buna göre bir hırsız çantasına ağırlıkça en az, pâhâca en çok eşyayı doldurmak ister. Bu durumda her eşyanın
Aşağıdaki her özel problem durumu için eşyaların pahası pi olsun ve her eşyanın ağırlığı ai olsun. Çantanın taşıyabileceği azamî kapasite de ci olarak tanımlansın.
0-1 Torbla problemi: Bu problem tipinde eşyalar ya tamamen alınır ya da tamamen bırakılır. Alınacak olan eşyanın bir kısmını almak mümkün değildir. Dolayısıyla bir eşyanın alınıp alınmamasını xi ile gösterecek olursak problem aşağıdaki şekilde modellenebilir:
Bu modelde görüldüğü üzere, xi değeri, 0 veya 1 olabilmektedir. 0 olması durumunda i elemanından alınmıyor 1 olduğunda ise i elemanının tamamı alınıyor demektir.
Örnek
Elimizde aşağıdaki eşyalar bulunsun:
- saat, 0.2 kg 100TL
- fotoğraf makinesi 0.5kg 300TL
- kamera 0.7kg 700TL
- cep telefonu 0.3kg 500TL
- anahtar 0.1kg 10TL
Şimdi problemimiz, elimizdeki bir torbaya (örneğin 1kg kapasiteli) yukarıdaki eşyaları en fazla para edicek şekilde yerleştirmek istememiz.
bu durumda ci=1kg olarak tanımlamış oluyoruz.
Çözümde ise i = { 3,4} kümesi alınıyor. Yani kamera ve cep telefonunu aldığımızda 1200 TL ile en pahalı ve torbamıza sığan kümeyi almış oluyoruz.
Sınırlı Torba Problemi (Bounded Knapsack Problem): Bu problem tipinde ise her eşyadan alınabilecek miktarda sınır vardır. Bu problemin modeli aşağıda verilmiştir:
Sınırsız Torba Problemi (Unbounded Knapsack Problem): Bir önceki sınırsız torba probleminden farklı olarak bu problemde alınabilecek malzemelerin herhangi bir sınırı bulunmamaktadır.
Örnek
Bu problem tipinde ise örneğimizi değiştirmemiz gerekiyor.
- elma 2TL , 4kg
- armut 5TL, 3kg
- kiraz 4TL, 6kg
- portakal 3.5TL, 5kg
şeklinde verilen ürünlerden örneğin 10kg’lık bir fileye en pahalı nasıl bir seçim yapılmalıdır sorusu sorulabilir.
Bu durumda her üründen değişk miktarlarda var. Örneğin en fazla 4kg elma alabiliyoruz bu değer modelimizdeki bi ile gösterilen ve bir üründen en fazla alınabilen değerdir. Toplamda en fazla alabileceğimiz değer ise hala ci‘dir. Bu problem tipinde her üründen alınan miktar ise değişmektedir ve bu miktar xi ile gösterilir.
Örneğin yukarıdaki problemin çözümünde 1kg portakal fileye konulacaksa, bu durumda x4 = 1, b4 = 5 , p4 = 3.5 olarak alınmalıdır.
Torba problemi, veri güvenliği (cryptography) konusunda genellikle alt küme toplam problemi olarak kullanılır ve örneğin merkle-hellman yönteminde bu özelliğinden faydalanılır.
Torba problemi aynı zamanda dinamik programlama (dynamic programming) için de oldukça elverişli bir problemdir ve aşağıdaki yaklaşımla çözülebilir:
Troba probleminin bir diğer çözüm önerisi açgözlü yaklaşımı (greedy approach) iledir. Bu çözüme göre alınabilecek en yüksek meblağdaki eşya torbaya doldurularak her seferinde kalan yer için mümkün olan en değerli eşya aranır. En sonunda yer kalmayana veya kalan yere bir eşya konulamayana kadar bu şekilde gidilir.
Açgözlü yaklaşımının (greedy approach) bir dez avantajı her zaman en verimli sonucu üretememesidir.
SORU-157: Sonlu Durum Makinası (Finite State Machine, Finite State Automaton)
Sonlu durum makinaları bir çizim şeklidir. Bu çizim şeklinde çeşitli durumlar ve bu durumlar arası geçiş şekilleri gösterilir. Örneğin aşağıda basit bir kapı açma ve kapama makinesi verilmiştir:
Yukarıdaki şekilde makine, açık durumdan kapalıya geçmek için kapama düğmesine basılmasını bekler. Tersi durumda da kapalıdan açığa geçmek için kapama düğmesinin basılmasını bekler. Durum makinemizde kapalı durumdayken kapama düğmesine ve açık durumdayken açma düğmesine basılması bir durum değişikliği doğurmaz ve istenildiği kadar basılabilir.
Yukarıdaki şekilde bir başlangıç veya bitiş durumu belirtilmemiştir. Yani makine sonsuza kadar çalışmaktadır ve herhangi bir başlangıç koşulu yoktur. Yukarıdaki bu makinanın bir de açma ve kapama tuşları olduğunu düşünelim. Bu durumda makinenin başlangıcı açma düğmesi ve bitişi de kapama düğmesi ile olacaktır. Aşağıdaki şekilde yeni makinemizin çizimi gösterilmiştir:
Yukarıdaki şekilde sonlu durum makinemize ilave olarak başlangıç ve bitiş durumları da eklenmiştir. Buna göre kalın gidi oku başlangıç durumunu gösterir ve makinemiz buradan başlar. Çift çember içindeki durum ise bitiş durumudur ve istenirse makine burada sona erdirilir, veya çalışmaya devam edebilir. Görüldüğü üzere yukarıdaki şekilde kapı otomatı başlatıldıktan sonra istenildiği kadar açma kapama işlemi yapılmakta, gelen bu komutlara göre kapını durumu değişmektedir. Kapı otomatı kapatıldıktan sonra gelen açma ve kapama emirleri doğal olarak icra edilemez.
SORU-158: Kruskal Asgari Tarama Ağacı Algoritması
Bir asgari tarama ağacı (minimum spanning tree) algoritması olan Dijkstra algoritması, işaretlemiş olduğu komşuluklara en yakın düğümü bünyesine katarak ilerler. Buna göre aşağıdaki grafiğin asgari tarama ağacını çıkaralım:
Yukarıdaki grafikte her düğüm için bir temsili harf ve her bağlantı için bir ağırlık değeri atanmıştır. Buna göre her düğümden diğerine gitmenin maliyeti belirlenmiştir.
Kruskal algoritmasında bütün yollar listelenip küçükten büyüğe doğru sıralanır. Bu liste yukarıdaki grafik için aşağıda verilmiştir:
x-v:1
w-v:1
w-u:1
x-w:2
u-s:2
x-y:3
t-u:3
u-v:3
y-v:4
s-t:4
y-z:5
y-t:5
z-t:10
Yukarıdaki liste çıkarıldıktan sonra sırasıyla en küçükten en büyüğe doğru komşuluklar işaretlenir. Bu işaretleme sırasında ada grupları ve grupların birbiri ile ilişkisine dikkat edilir. Yani şayet listedeki iki düğüm harfi de aynı adadan ise bu bağlantı atlanır. Aşağıda sırasıyla bu grafikteki adaların oluşması ve asgari tarama ağacının çıkarılması gösterilmiştir:
x-v:1
w-v:1
w-u:1
x-w bağlantısı atlanır, çünkü iki düğüm de zaten dolaşılmıştır bunun yerine u-s:2 bağlantısına atlanır.
x-y:3
t-u:3
Bu noktadan sonra u-v:3 , y-v:4 , s-t:4 , y-z:5 bağlantılarındaki her iki düğümde aynı adada olduğu için atlanır ve y-t:5 bağlantısına geçilir.
z-t:10 bağlantısı ise iki düğüm de gezildiği için yine gereksizdir.
SORU-159: Prim asgari tarama ağacı Algoritması
Bir asgari tarama ağacı (minimum spanning tree) algoritması olan Prim algoritması, işaretlemiş olduğu komşuluklara en yakın düğümü bünyesine katarak ilerler. Buna göre aşağıdaki grafiğin asgari tarama ağacını çıkaralım:
Yukarıdaki grafikte her düğüm için bir temsili harf ve her bağlantı için bir ağırlık değeri atanmıştır. Buna göre her düğümden diğerine gitmenin maliyeti belirlenmiştir.
Prim algoritmasında rasgele bir başlangıç noktası seçilir. Örneğin bizim başlangıç noktamız “z” düğümü olsun. Bu durumda ilk inceleyeceğimiz komşuluk, “z” düğümünden gidilebilen düğümler ve maliyetleri olacaktır.
z düğümünden gidilebilen düğümler ve maliyetleri aşağıda listelenmiştir:
y:5
t:10
Prim algoritması bu listedeki en küçük maliyetli komşuyu bünyesine dahil eder. Buna göre yeni üyelerimiz {z,y} olacaktır ve gidilen yollar {z-y:5} olacaktır. (ilk üyeler listesinde şimdiye kadar ziyaret edilmiş düğümler bulunur. Bu düğümler listesinde zaten olan bir düğüm listeye eklenemez. yollar listesinde ise hangi düğümden hangi düğüme ne kadar maliyetle gidildiği tutulur.) Dolayısıyla grafiğimizde Prim algoritması tarafından işaretlenen düğümler aşağıda gösterilmiştir:
şimdi üyelerimizin durduğu listedeki bütün düğümlerin komşularını listeleyelim:
t:5
t:10
v:4
x:3
yukarıdaki listede t düğümüne iki farklı gidiş bulunmaktadır ( hem z hem de y üzerinden). Biz algoritmamıza devam edip en küçük yolu bünyemize dahil edelim. En yakın komşu x:3′tür. Bu durumda üyelerimiz {z,y,x} olacak ve yollarımız {z-y:5,y-x:3} olacaktır. Bu durum aşağıdaki grafikte gösterilmiştir:
Yeniden komşularımızı listelersek:
t:5
v:1
w:2
yukarıdaki listede bünyemize aldığımız adadan, bir düğüme giden birden fazla yol bulunması durumunda en kısası alınmıştır. Bu durumda listenin en küçük elemanı olan v:1 tercih edilir ve üyelerimiz {z,y,x,v} yollarımız ise {z-y:5,y-x:3,x-v:1} olur. Durum aşağıdaki grafikte gösterilmiştir:
Yeniden komşularımızı listelersek:
t:5
u:3
w:1
yukarıdaki listede bünyemize aldığımız adadan, bir düğüme giden birden fazla yol bulunması durumunda en kısası alınmıştır. Bu durumda listenin en küçük elemanı olan w:1 tercih edilir ve üyelerimiz {z,y,x,v,w} yollarımız ise {z-y:5,y-x:3,x-v:1,v-w:1} olur. Durum aşağıdaki grafikte gösterilmiştir:
Yeniden komşularımızı listelersek:
t:5
u:1
yukarıdaki listede bünyemize aldığımız adadan, bir düğüme giden birden fazla yol bulunması durumunda en kısası alınmıştır. Bu durumda listenin en küçük elemanı olan u:1 tercih edilir ve üyelerimiz {z,y,x,v,w,u} yollarımız ise {z-y:5,y-x:3,x-v:1,v-w:1,w-u:1} olur. Durum aşağıdaki grafikte gösterilmiştir:
Yeniden komşularımızı listelersek:
t:3
s:2
yukarıdaki listede bünyemize aldığımız adadan, bir düğüme giden birden fazla yol bulunması durumunda en kısası alınmıştır. Bu durumda listenin en küçük elemanı olan s:2 tercih edilir ve üyelerimiz {z,y,x,v,w,u,s} yollarımız ise {z-y:5,y-x:3,x-v:1,v-w:1,w-u:1,u-s:2} olur. Durum aşağıdaki grafikte gösterilmiştir:
Son komşumuz olan t için en kısa ulaşım
t:3 değeridir ve u üzerinden sağlanır. Bu durumda listenin en küçük elemanı olan t:3 tercih edilir ve üyelerimiz {z,y,x,v,w,u,s,t} yollarımız ise {z-y:5,y-x:3,x-v:1,v-w:1,w-u:1,u-s:2,u-t:3} olur. Sonuçta elde edilen asgari tarama ağacı aşağıda verilmiştir:
SORU-160: asgari tarama ağacı (en kısa örten ağaç, minimum spanning tree)
Asgarai tarama ağacı, ağırlıklık bir ağda (weighted graph, yani her düğümü birbirine bağlayan yolların maliyeti (ağırlığı) olması durumu), bütün düğümleri dolaşan en kısa yolu verir. Örneğim aşağıdaki grafikte bütün düğümlere uğrayan en kısa yol işaretlenmiştir:
SORU-161: alt program (subprogram, subroutine)
bir programın herhangi bir alt parçasına verilen isimdir. Daha resmî tanımı için ilave olarak bu alt parçanın belirli bir amaca yönelik olması gerektiği söylenebilir. Yani programın herhangi bir alt parçası olmasının yanında bir amaç için bölünmüş parça’ya alt program diyebiliriz. Basitçe dilde bulunan fonksiyon (function), prosedür (procedure) , metod(method) veya herhangi bir blok için (if, while, for bloğu gibi) alt program tâbiri kullanılabilir.
Alt programlarda değişken kontrolü dilin özelliğine göre değişmektedir. (bkz. sabit alanlı değişkenler ve dinamik alan değişkenleri
Aşağıda örnek bir kod ve her kod parçası için bir alt program bölümü verilmiştir:
Yukarıda örnek bir 3 boyutlu savaş oyununun java kodunun bir kısmı bulunmaktadır. Bu koddaki alt programlar farklı renkler ile işaretlenmiştir. Buna göre alt program kavramı, resimde de görüleceği üzere her if, else, fonksiyon veya döngü parçasıdır. Daha fazla bilgi için yapısal programlama başlığına bakabilirsiniz.
SORU-162: fonksiyon göstericileri (function pointer)
fonksiyon göstericilerinin amacı, programlama dilinde bulunan fonksiyonları gösteren birer referans bilgisi tutmaktır. Bu sayede gösterilmekte olan fonksiyon için hafızada ayrılmış olan yere erişmek ve dolayısıyla örneğin fonksiyonun yerel değişkenlerine ulaşmak mümkündür. Aşağıda C dilinde yazılmış bir fonksiyon göstericisi kullanan kod örneği verilmiştir:
#include #include void func(int); main(){ void (*fp)(int); fp = func; (*fp)(1); fp(2); } voidfunc(int arg){ printf("%dn", arg);}
Yukarıdaki kodda “func” isminde bir fonksiyon tanımlanmıştır. Ayrıca void tipinde dönüş değerine sahip ve int tipinde parametre alan bir fonksiyoö göstericisi “fp” tanımlanmıştır. Dikkat edilirse “func” fonksiyonunun ve “fp” göstericisinin hem parametreleri hem de dönüş değerleri aynıdır. Bu durum bir göstericinin foknsiyonu göstermesi (refer etmesi ) için gereklidir. Bu gösterme işlemi atama satırı olan
fp=func;
satırı ile yapılmaktadır. artık bu satırdan sonra “fp” göstericisine verilen her değer “func” fonksiyonuna verilmiş gibi icra edilir. Yani yukarıdaki kod çalışıtırıldığında ekranda önce 1 sonra 2 görülmektedir.
Karıştırılmaması gereken bir nokta:
void (*fp)(int);
void *fp(int);
Yukarıdaki iki satır birbirinden farklıdır. İlk satır bir fonksiyon göstericisini, ikinci satır ise gösterici döndüren bir fonksiyonu tanımlarken kullanılmalıdır. Yani yukarıdaki iki satır aynı değildir.
Fonksiyon göstericileirinin fonksiyonlara parametre olarak verilmesi.
bir fonksiyon parametre olarak bir fonksiyon göstericisini alabilir. Aşağıda bunu yapan temsili kod verilmiştir:
#include #include int func(int);void PassPtr(int (*pt2Func)(int)){ int sonuc = (*pt2Func)(12); printf("%d",sonuc);} main(){ int (*fp)(int); fp = func; PassPtr(fp); }intfunc(int arg){ return ++arg;}
Yukarıdaki örnek kodda, func ismindeki fonksiyonu gösteren fp isminde bir fonksiyon göstericisi tanımlanmıştır. Bu gösterici PassPtr fonksiyonuna parametre olarak verilmiştir. Bu kod çalıştırıldığında ekranda 13 sayısı görlür çünkü, func fonksiyonunu gösteren fp göstericisi PassPtr fonksiyonunun içinden çağrılmış ve değer olarak 12 parametresi atanmıştır. Fonksiyon incelenirse parametre olarak aldığı sayıyı bir arttırdığı görülür. Bu durumda ekrandaki değer 13 olmaktadır.
Dikkat edilirse yukarıdaki kod ile, bir fonksiyona başka bir fonksiyon verilebilmektedir. Bu sayede genel amaçlı fonksiyonlar yazılarak ve bu fonksiyonlar parametre olarak geçirilerek programlamada avantaj elde edilebilmektedir.
Fonskiyon göstericilerinden dizi oluşturmak:
Fonksiyon göstericileri de birer gösterici olduğu için normal bir göstericiye yapılan herşey bu göstericilere de yapılabilir. Bunlardan birisi de bir dizi tanımlamaktır.
Aşağıdaki örnek kodu inceleyelim:
#include #include typedef int (*pt2Function)(int);intfunc(int arg){ return ++arg;}int main(){ pt2Function funcArr1[10] = {NULL}; int (*funcArr2[10])(int) = {NULL}; funcArr1[0] = funcArr2[1] = func; printf("%dn", funcArr1[1](12));}
Yukarıdaki örnek kodda, iki adet fonksiyon gösterici dizisi tanımlanmıştır. İlk dizi olan funcArr1 dizisi, typedef marifeti ile tanımlanmıştır ve bu tip tanımı daha önceden yapılmıştır. İkinci dizi olan funcArr2 dizisi ise daha önceden tanımlanmış herhangi bir tip kullanmaksızın tanımlanmıştır. Sonuçta ekranda 13 sayısını gördüğümüz bu yukarıdaki kodda fonksiyon göstericisi, bir dizinin elemanı olarak tutulmakta ve çağrılmaktadır.
SORU-163: En uzun Ortak Küme (longest common subsequence, Lcs)
İki küme arasındaki ortak elamanların (sıralı olmak şartıyla) en uzun ortaklığını arar. Örnek:
A-> {X,M,J,Y,A,U}
B-> {M,Z,J,A,W,X,U}
olarak verilmiş olsun. Bu iki kümenin, sırası bozulmadan ortak olan en uzun alt kümesi:
LCS -> {M,J,A,U} olarak bulunur.
Bu problem karmaşıklık açısından NP-hard problemlere bir örnektir. Aynı zamanda çözüm için önerilen yöntemler incelendiğinde dinamik programlamanın anlaşılması için ideal bir örnektir.
Örnek bir çözüm yöntemini inceleyelim:
Problemin ilk akla gelen ve en basit çözümü kümelerden birisini ana küme seçerek, bu kümedeki elemanları teker teker diğer küme ile sınamak olabilir.
Örneğin A kümesi seçilmiş olsun. A kümesinin ilk elemanından son elemanına kadar ortak elemanlara bakılacaktır. Bu durumda ilk eleman X, B kümesinde aranacaktır, daha sonra X ile birlikte U aranacaktır. ihtimallerin sonuna gelindiğinde bu ikili bırakılıp yeni bir ihtimal denenecektir:
Örnek çalışma:
A kümesinden X seçilir. B kümesinde sınanır. Karşılığı bulununca LCS1 olarak kaydedilir. devamı olan harflerden uygun olan seçilir (bu örnekte U) LCS1′e eklenir.
LCS1->{X,U}
LCS2->{M,J,A,U}
LCS3->{J,A,U}
LCS4->Y’nin eşi diğer kümede yok
LCS5->{A,U}
LCS6->{U}
yukarıdaki yönteme göre A, kümesinin bütün elemanları başlangıç elemanı olarak denenmiştir. Fakat dinamik programlama yaklaşımı incelenecek olursa bu işlemin çok daha kısa zamanda yapılabileceği görülür:
Aşağıda bir çözüm yöntemi olarak tablo kullanılması önerilmiştir. Buna göre tablodaki her hücre, ilgili satır ve sütün başlığına kadar olan en uzun ortak kümeyi tutmaktadır:
yukarıdaki şekilde örnek bir tablo verilmiştir. Buna göre verilen problemdeki en uzun ortak kümeyi bulmak hedeflenmektedir. Tablonun sağ alt köşesinde bulunan bu küme sorunun cevabıdır. Sorunun çözümünde tablo oluşturulurken her hücre kendisinin bir üstündeki veya bir solundaki hücreyi aynen kopyalamakta, ve üzerine satır ve sütün ismini şayet ortaksa yazmaktadır. Örneğin son oluşturulan sağ alt köşedeki küme, solundaki ve tepesindeki kümelerin kopyalanması ile oluşur. Bu kopyalama işlemine U elemanı ilave edilir çünkü bu hücrenin bulunduğu sütun ile satır ismi aynıdır.
Bu işlem yapılırken karşılaşılabilecek bir problem de solundaki bilgi ile tepesindeki bilginin aynı olmamasıdır. Bu durum kırmızı çember içerisinde gösterilmiştir. Bu durumda daha uzun bir küme elde edilince kısa kalan küme bırakılarak, uzun küme ile yola devam edilir. (problemin en uzun kümeyi bulmak olduğunu hatırlayınız).
Bu işlem sonunda aşağıdaki ortak küme uzunluklarını gösteren tablo elde edilir:
| 0 1 2 3 4 5 6 7 | M Z J A W X U-----|-----------------0 | 0 0 0 0 0 0 0 01 X | 0 0 0 0 0 0 1 12 M | 0 1 1 1 1 1 1 13 J | 0 1 1 2 2 2 2 24 Y | 0 1 1 2 2 2 2 25 A | 0 1 1 2 3 3 3 36 U | 0 1 1 2 3 3 3 4
Buna göre her seferinde kendinden önceki elemanlara bakılması önlenmiş olur. Yani örneğin tablonun 4,5 hücresine bakıldığında A harfleri kontrol edilmektedir. Bu kontrol sırasında kendisinden önceki harflerin kontrolüne gerek kalmaz. (tekrar B kümesinin MZJ harflerine bakılmaz), bu sayede ortak yapılan işlemler, dinamik programlama ilkesine uygun olarak elenmiş olur.
SORU-164: Dinamik Programlama (Dynamic programming)
Bir problem tahlil ve çözüm yöntemi olan dinamik programlama yapı olarak parçala fethet yöntemine benzer. Tek farkı problemi parçalara böldükten sonra aynı problemin tekrarı olan parçaları bir kerede çözüp her tekrar için ayrı bir çözüm yapmamasıdır.
Örneğin fibonacci serilerini ele alalım, Bu seriyi üreten örnek kod aşağıda verilmiştir:
int fibonacci(int n)
{
if (0 == n) {
return 0;
} else if (1 == n) {
return 1;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
Yukarıda verilen bu recursive (kendi kendini çağıran) koda bakıldığında ve kodun tahlili yapıldığında aşağıdaki fonksiyon iç içe çağırma ağacı (recursion tree ) fark edilir:
fibonacci(4) +------------------------------ | |fibonacci(2) fibonacci(3) +----------------- +--------------- | | | |fibonacci(0) fibonacci(1) fibonacci(1) fibonacci(2) +----------- | | fibonacci(0) fibonacci(1)
yani yukarıdaki örnekte, fibonacci(4) fonksiyonu için çağırma işlemleri sırasıyla gösterilmiştir. Dikkat edilirse fonksiyonlar açıldğında kendisinden önceki iki sayının toplamını bulmakta, bu işlemi yaparken de ortak elemanlar kullanmaktadır. Örneğin fibonacci(2) fonksiyonu ağacın iki farklı yerinde bulunmaktadır ve iki farklı kere içi hesaplanmıştır. İşte dinamik programlamada amaç bunu kaldırarak bir kerede çözüme ulaşmaktır.
Dinamik programlamada aşağıdaki adımlar takip edilebilir:
Verimli bir çözüm için problemin yapsınının çıkarılması
Kendini çağıran bir şekilde (Recursive) verimli çözüme değer atanması
Verimli çözümün değerini aşağıdan yukarı (bottom-up) olarak hesaplanması
Hesaplanan bu çözümle daha verimli bir çözüm varsa aranıp üretilmesi
SORU-165: parçala fethet yöntemi (divide and conquer)
Bu yöntem algoritma analizinde çok kullanılan, bir algoritmayı tahlil etmek veya yeni bir algoritma oluşturmak için kullanılan yaklaşımlardan birisidir.
Bu yaklaşıma göre problem ufak ve çözülmesi nispeten daha kolay olan parçalara bölünür. Her parça ayrı ayrı çözüldükten sonra sonuçlar birleştirilerek genel problemin çözümü elde edilir.