BÖLÜM. Özyineleme Özyineleme Nedir? 10.3 Özyineleme Örnek. Bölümün İçindekileri

Benzer belgeler
ALGORİTMA ANALİZİ. Cumhuriyet Üniversitesi Bilgisayar Mühendisliği Bölümü

Yrd. Doç. Dr. A. Burak İNNER Bilgisayar Mühendisliği

BİLGİSAYAR PROGRAMLAMA. Algoritma ve Akış Şemaları

ELN1002 BİLGİSAYAR PROGRAMLAMA 2

ALGORİTMA VE PROGRAMLAMA I

Algoritmalar ve Programlama. Algoritma

NESNEYE YÖNELİK PROGRAMLAMA

HSancak Nesne Tabanlı Programlama I Ders Notları

elemanlarının gezilmesine yönelik bir örnek sunulmuştur, inceleyiniz.

Fonksiyonlar. C++ ve NESNEYE DAYALI PROGRAMLAMA 51. /* Fonksiyon: kup Bir tamsayının küpünü hesaplar */ long int kup(int x) {

YZM ALGORİTMA ANALİZİ VE TASARIM DERS#3: ALGORİTMA ANALİZİ#2

ALGORİTMA VE PROGRAMLAMA I

DÖNGÜLER BMÜ-111 ALGORİTMA VE PROGRAMLAMA-I YRD. DOÇ. DR. İLHAN AYDIN

Alıştırma 1: Yineleme

DÖNGÜLER (LOOPS) while(), do-while(), for(), foreach()

EM205 26/9/2014. Programlamaya giriş Algoritmalar. Amaçlar

Özyineleme (Recursion)

BİLGİSAYAR PROGRAMLAMA DERSİ

/ C Bilgisayar Programlama Final Sınavı Test Soruları. Adı soyadı :... Öğrenci no :... İmza :... Tarih, Süre : , 60 dak.

while(), do-while(), for() M.İLKUÇAR 2010 MAKU-MYO

3.Hafta Master Teorem ve Böl-Fethet Metodu

MAK 1005 Bilgisayar Programlamaya Giriş. Fonksiyonlar. Prof. Dr. Necmettin Kaya

ÜNİT E ÜNİTE GİRİŞ. Algoritma Mantığı. Algoritma Özellikleri PROGRAMLAMA TEMELLERİ ÜNİTE 3 ALGORİTMA

C PROGRAMLAMA YRD.DOÇ.DR. BUKET DOĞAN PROGRAM - ALGORİTMA AKIŞ ŞEMASI

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

BM102 BİLGİSAYAR PROGRAMLAMA II LABORATUVAR UYGULAMALARI. 3Hafta

C++ Dersi: Nesne Tabanlı Programlama

ELN1001 BİLGİSAYAR PROGRAMLAMA I

Programın Akışının Denetimi. Bir arada yürütülmesi istenen deyimleri içeren bir yapıdır. Söz dizimi şöyledir:

Yrd. Doç. Dr. Caner ÖZCAN

ÖZEL EGE LİSESİ FİBONACCİ DİZİLERİ YARDIMIYLA DEĞERİNİ HESAPLAYAN BİR FORMÜL

Dr. Fatih AY Tel: fatihay@fatihay.net

Mühendislik Fakültesi Elektrik-Elektronik Mühendisliği C Programlama 7. Bölüm Metot Tanımlama ve Kullanma

Özyineleme (recursion)

ALGORİTMA VE PROGRAMLAMA I

ÖDEV (Vize Dönemi) CEVAPLAR. 1. Ekrana Merhaba Dünya! yazdıran algoritmanın akış diyagramını çiziniz ve sözde kod olarak yazınız.

Algoritma ve Programlamaya Giriş

BLM-431 YAPAY ZEKA. Ders-3 Durum Uzayında Arama. Yrd. Doç. Dr. Ümit ATİLA

Bir dizinin boyutları sabittir ve kullanılmadan önce belirlenmelidir. Dizi boyutunu belirlemek için başka bir değişkende kullanabilirsiniz.

Çoktan Seçmeli Değerlendirme Soruları Akış Şemaları İle Algoritma Geliştirme Örnekleri Giriş 39 1.Gündelik Hayattan Algoritma Örnekleri 39 2.Say

BLM 112- Programlama Dilleri II. Hafta 5 İşaretçiler (Pointers)

MTK467 Nesneye Yönelik Programlama. Hafta 4 - Döngüler Zümra Kavafoğlu

Toplama işlemi için bir ikili operatör olan artı işareti aynı zamanda tekli operatör olarak da kullanılabilir.

BMÜ-111 Algoritma ve Programlama. Bölüm 5. Tek Boyutlu Diziler

Dersin Sorumlusu: Yrd. Doç. Dr. Birol SOYSAL. Sunumları Hazırlayan: Doç. Dr. Bülent ÇAKMAK

BLM-111 PROGRAMLAMA DİLLERİ I. Ders-12 Fonksiyonlar. Yrd. Doç. Dr. Ümit ATİLA

Döngüler - Loops 4/9/2010. ENF-102 Jeoloji Giriş. Sayaç kontrollü bir döngüdeki temel öğeler. Sayaç (counter) Kontrollü Döngüler.

Sınav tarihi : Süre : 60 dak. a) ABCDE b) BCDE c) ABCD d) kod hatalı e) BCD

Yrd. Doç. Dr. Caner ÖZCAN

ALGORİTMA NEDİR? (Adım adım işlem basamaklarının yazılmasıdır.)

YZM ALGORİTMA ANALİZİ VE TASARIM DERS#6: AZALT VE FETHET YÖNTEMİ

Akış Kontrol Mekanizmaları

BIL1202 ALGORİTMA VE PROGRAMLAMAYA GİRİŞ (Algoritma Geliştirmek, Satır Kod)

Temel Bilgisayar Programlama Final Sınavı Çalışma Notları

BLM 111 ALGORİTMA VE PROGRAMLAMA I

Algoritma ve Akış Diyagramları

Algoritmalar. 3. Açıklık: Her işlem (komut) açık olmalı ve farklı anlamlar içermemeli.

BIL1202 ALGORİTMA VE PROGRAMLAMAYA GİRİŞ

F(A, N, K) // A dizi; N, K integer if N<0 then return K; if A[N]>K then K = A[N]; return F(A, N-1, K);

Binary Search. (Yarılama) Bölüm Dizide Bir Öğe Arama

Internet Programming I. Hafta III. Elbistan Meslek Yüksek Okulu Güz Yarıyılı. Öğr. Gör. Murat KEÇECĠOĞLU

BİLGİSAYAR TEMELLERİ VE PROGRAMLAMAYA GİRİŞ

ALGORİTMA VE PROGRAMLAMA I

Sınav Dağılım & IMKB Endeks

Hafta 13 Fonksiyonlar

public static int Toplam int x, int y

İÇERİK PROGRAMLAMAYA GİRİŞ ALGORİTMA AKIŞ DİYAGRAMLARI PROGRAMLAMA DİLLERİ JAVA DİLİNİN YAPISI JAVA DA KULLANILAN VERİ TİPLERİ JAVA DA PROGRAM YAZMA

ÖZYİNELEME RECURSION. Doç. Dr. Aybars UĞUR

Yrd. Doç. Dr. Caner ÖZCAN

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

Algoritmalar ve Karmaşıklık

GÜMÜŞHANE ÜNĐVERSĐTESĐ MÜHENDĐSLĐK VE DOĞA BĐLĐMLERĐ FAKÜLTESĐ ELEKTRĐK-ELEKTRONĐK MÜHENDĐSLĐĞĐ EEM 114 ALGORĐTMA TASARIMI VE PROGRAMLAMA DĐLLERĐ

disp VEYA fprintf KOMUTLARIYLA EKRANA MESAJ YAZDIRMA

Algoritmaların Karşılaştırılması. Doç. Dr. Aybars UĞUR

Dizi elemanları yukarıdaki gibi tek tek tanımlanabileceği gibi matematiksel ifadelerdeki diziler gibi de tanımlanabilir.

Algoritma ve Programlama: Karar Yapıları ve Döngüler

2 ALGORİTMA VE AKIŞ DİYAGRAMLARI

WEB TASARIM I. Öğr. Gör. M. Mutlu YAPICI. Ankara Üniversitesi Elmadağ Meslek Yüksekokulu

Döngüler - Loops 4/13/2011. ENF-102 Jeoloji Sayaç kontrollü bir döngüdeki temel öğeler. Sayaç (counter) Kontrollü Döngüler.

Genel Programlama II

C++ Dilinde Bazı Temel Algoritmalar

HSancak Nesne Tabanlı Programlama I Ders Notları

JAVA PROGRAMLAMA DİLİ ÖZELLİKLERİ

ALGORİTMA VE PROGRAMLAMA I

ALGORİTMA KAVRAMI. Bir sorunu / problemi çözmek veya belirli bir amaca ulaşmak için gerekli olan sıralı mantıksal adımların tümüne algoritma denir.

Pointer Kavramı. Veri Yapıları

Daha komplike uygulamalar elektronik ticaret, elektronik kimlik belgeleme, güvenli e-posta,

ÖZYİNELEME RECURSION. Yrd. Doç. Dr. Aybars UĞUR

Algoritmalar. Heap Sort. Bahar 2017 Doç. Dr. Suat Özdemir 1

TEMEL BİLGİSAYAR BİLİMLERİ. Programcılık, problem çözme ve algoritma oluşturma

BMT 101 Algoritma ve Programlama I 11. Hafta. Yük. Müh. Köksal Gündoğdu 1

Özyineleme (Recursion)

BLGM BÖLÜM. Problem Çözme Kavramları (Algoritma ve Akış Şemaları)

DİZİLER. Bu ünitede yapılan örnekler Visual Studio 2010 programındaki Visual Basic programlama diliyle çözülmüştür.

NESNE YÖNELİMLİ PROGRAMLAMA HAFTA # 2

YZM 2116 Veri Yapıları

YZM 2116 Veri Yapıları

Length: metin uzunluğunu yada diğer bir deyişle dizi elaman sayısını döndürür.

MATLAB de. Programlama. Kontrol Yapıları. Döngü Yapıları. Doç. Dr. İrfan KAYMAZ Matlab Ders Notları

Transkript:

327 BÖLÜM 10 Özyineleme 10 Bölümün İçindekileri Karışık kombinasyon problemlerini ve konfigürasyonlarını analiz ederken, permütasyon ve türevlerini üretirken ve iç içe geçmiş döngüleri analiz ederken özyineleme ve uygulamalarını kullanacağız. Metot içinden yine metotun kendisi için yapılan çağrı veya çağrıları temsil eden güçlü programlama tekniklerine özyinelemeli denir. Bu bölümde özyinelemenin doğru ve yanlış kullanımları ile ilgili pek çok örnek göstermeye ve nasıl yararlı olabileceği hakkında doğru yolu göstermeye sizi ikna etmeliyiz. Özyineleme tekniğini kullanmak için yola çıkıyoruz. 10.2 Özyineleme Nedir? Özyineleme, belirli bir sorunu çözmek için bir yöntemin kendisini çağırdığı bir programlama tekniğidir. Metotun kendisi için yapılan bir çağrının belirli konudaki bir sorunu çözmesi olarak da tanımlanabilir. Özyineleme nesnenin kendisini içermesi yada kendi kendisi tarafından tanımlanmasıdır. Doğru kullanıldığında belirli problemlere zarif çözümler getiren bir programlama tekniğidir. Kullanımı, programlama kodunu ve okunabilirliğini bazı durumlar için kolay hale getirir. Bu programlama teknikleri genellikle kodun okunulabilirliğini artırır. 10.3 Özyineleme Örnek Aşağıda bir Fibonacci sayı dizisinin bazı elemanlarını görüyorsunuz. 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,

328 Verilen bir Fibonacci dizisinin her elemanı iki önceki elemanın toplamından oluşur. Birinci ve ikinci elemanlar tanım gereği 1 değerini alırken, diğer elemanların diziliş kuralı aşağıda verilmiştir: F 1 = F 2 = 1 F i = F i-1 + F i-2 (for i > 2) Tanıma göre n inci elemanın hesaplanması için aşağıdaki özyineleme metodunu kullanırız: int 2 return 1 return 1 2 Bu örnek bir çözümün uygulanması için özyineleme metotunun nasıl kullanılacağını gösteriyor. Öte yandan, özyineleme ile programlama yaparken sezgisel olmalısınız, çünkü programın verimliği ve hızını etkileyen pek çok faktör vardır. Özyinelemenin avantaj ve dezavantajları için bkz. bu bölümün sonraki alt başlıkları. 10.4 Doğrudan ve Dolaylı Özyineleme Bir metot gövdesi içinden aynı metotun kendisine çağrı yapmışsa, bu doğrudan özyineleme veya direkt özyinelemedir. Verilen bir A metotu herhangi bir B metotunu çağırıyor ve bu B metotu da verilen bir C başka metotunu çağırıyor ve bu C metotu da gerisin geriye tekrar A metotunu çağırmışsa, bunun adı literatürde dolaylı özyineleme veya A, B ve C metotları aralarında indirekt özyineleme vardır demeklerdir. Dolaylı özyineleme çağrılarında birden fazla metot zinciri var olabilir, ancak koşullara veya verilere, parametre değerlerine bağlı olarak bir metot bir başka metota özyineleme çağrısı yaparken, yine başka nedenlerle bir başka metota özyineleme çağrısı yapabilir.

329 10.5 Özyinelemenin Sonlandırılması Özyinelemeli programlamada dikkat edilmesi gereken en önemli programlama tekniği, özyineleme zincirlerinin son bulması gerektiği, yada somut sayıda tekrar ettikten sonra sonucu döndürmesidir. Bir başka deyişle, sonsuz sayıda özyineleme yoktur, bu durum yanlış programlamaya yol açar. Bu nedenle özyinelemeyi sonlandırmak için bir yada daha fazla programlama yapısının metot içinde kullanılarak sonuç durumlarının programlanması programcının ana görevidir. Yapılması gerekli bu eylem özyinelemenin sonlandırılması olarak da tanınır. Fibonacci sayıları örneğinde, özyinelemenin sonlandırılma şartı n sayısının 2 veya daha az olmasıdır. Sonlandırılma şartının özyinelemeyi sonlandırmasından emin olmalıyız. Sonlandırılmaya gelen bir metot, daha fazla özyinelemeli çağrı yapmadan içinde bulunduğu çağrıyı sona erdirecek sonucu döndürmelidir. Kodda yapılan bir değerlendirme metotun özyinelemeli sonuç döndüren bir program mı, sonsuz döngü mü yapıldığını ortaya çıkaracaktır. Bu nedenle n=1 ve n=2 için sadece 1 döner. 10.6 Özyinelemeli Metot Oluşturulması Özyinelemeli metotun her tekrarında elde edilen sonuç toplanarak gerçek sonuç oluşturulur. Her özyinelemeli çağrı sonunda problem daha küçük alt birimlere bölünür. Bu küçük problemlere ait alt çözümler bilgisayar tarafından saklanır ve dönüş sağlandığında birleştirilir. Birleştirme işlemi için özyinelemenin sonlandırılması gerekir. 10.7 Faktoriyelin Özyinelemeli Hesaplanması Özyineleme kullanımını klasik bir örnek ile göstereceğiz faktoriyelin özyinelemeli hesaplanması. n faktöriyel (n! olarak gösterilir) 1 ve n (dahil) arasındaki her tamsayının çarpımlarından ibaret bir sayı olarak tanımlıdır. 0! = 1 kabul edilir. n! = 1.2.3 n

330 10.7.1 Özyinelemeli Tanım Faktoriyel hesaplanması için bir çözüm oluştururken faktoriyelin özyinelemeli tanımına başvurmak yerinde olacaktır: n! = 1, n = 0 için n! = n.(n-1)!, n > 0 için 10.7.2 Özyinelemeli Bağlılık Tespiti Özyinelemeli bağlılık tespiti için uzun uğraş gerekebilir. Faktoriyel örneğimizde, problem analizi ve ilk birkaç durum için faktoriyel değerlerinin hesaplanması yeterlidir. Buradan kolayca tekrarlayan bağlılıkları görebilirsiniz: 10.7.3 Gerçekleştirilen Algoritma Özyinelemenin sonlandırılma koşulu 1 değerine sahip n=0 şartıdır. Diğer durumlarda n=n-1 için problemi çözmek zorundayız ve bu alt problem için dönen sonucun n ile çarpımını n=n-1 için döndürmeliyiz. Bu algoritma, belirli bir sayıdaki adım sonrasında özyinelemenin sonuna erişir ve 1 n arasındaki somut sayıdaki tamsayıların çarpımını hesaplar. Bu önemli şartlar ile birlikte, faktoriyel hesaplayan metotu yazabiliriz:

331 static int // The bottom of the recursion 0 1 // Recursive call: the method calls itself 1 Bu metotu kullanarak konsoldan bir tamsayı okuyan, bu tamsayının faktoriyelini hesaplayan ve elde edilen değeri yazdıran bir uygulama geliştirebiliriz. RecursiveFactorial static Console n = int int Console Console {0}! = {1} static int // The bottom of the recursion 0 return 1 // Recursive call: the method calls itself return 1 n=5 için uygulamanın çalıştırılması sonucu aşağıda verilmiştir:

332 10.8 Özyineleme veya Yineleme? Özyineleme kavramı öğretilirken genellikle faktoriyel hesaplama örnek olarak seçilmiştir, ancak diğerleri gibi bu durumda da özyineleme en iyi seçim değildir. Verilen bir problem için yinelenen (ardışık) bir çözüm her zaman açık olmayabilir. Sıklıkla eğer bir problemin özyinelemeli tanımı verilmişse, özyinelemeli çözüm sezgisel ve çok zor olmamalıdır. Faktoriyel örnek için yinelemeli çözümün gerçekleştirilmesi özyineleme kadar kısa, basit ve hatta biraz daha verimli görünmektedir. int for 1 1 return Bu bölümde daha sonra özyineleme ve yineleme kullanmanın avantaj ve dezavantajlarını dikkate alacağız. Şu andan itibaren özyinelemenin gerçekleştirilmesi ile devam etmeden önce şunu da hatırlatmamız gerekir ki, yinelenen bir değişebilir şart üzerinde düşünmeli ve bu şart sonrasında daha iyi bir çözüm bulmalıyız. Özyineleme kullanılarak çözülen bir probleme göz atalım. Bu örnek için yinelemeli bir çözümü de dikkate alacağız. 10.9 İç içe N Döngünün Simülasyonu Sık sık iç içe döngüler yazmak zorundayız. İki, üç adet veya daha önceden belirli bir sayı kadar döngünün kodlanması kolaydır. Ancak sayılar önceden bilinmiyorsa, farklı bir yaklaşım düşünmekte fayda var. N sayısının 1..K defa tekrarlanarak gösterimini gerçekleştiren aşağıdaki örneği göz önüne alınız: N ve K kullanıcı tarafından girilmelidir.

333 for 1 for 1 for 1 Console 1 {0} {1} {2} {N} Örneğin N=2 ve K=3 için (1..3 defa tekrarlanarak gösterimi), ve N=3 ve K=2 için (1..2 defa tekrarlanarak gösterimi), çıktılar aşağıdaki gibidir: Bu problemi çözen algoritmayı önceki örnekte olduğu gibi açıkça anlayamayabilirsiniz. Bunun için biri özyinelemeli, diğeri yinelemeli iki çözüm geliştirebiliriz. Sonucun her satırında, N adet sayının bir sıralı dizilimini görüyorsunuz. Birinci değişken birinci döngü sayacının geçerli değerini temsil eder. İkinci değişken ikinci döngü sayacının geçerli değerini temsil eder, vs. Her sayı 1 ve K arasında değer alabilir. N ve K verildiğinde görevimiz, N elemanın sıralı dizilimlerini bulmaktır. 10.9.1 İç içe Döngüler Özyinelemeli Yaklaşım Eğer problem için özyinelemeli çözüm arıyorsanız, ilk yaklaşımınız özyinelemeli bağlılık bulmaktır. Örnekte tanımlı olan probleme biraz daha dikkatli bakalım. N=2 için cevabı hesaplasaydık, birinci sayı olarak K koyarak (örneğimizde K=3) N=3 için de cevabı elde edecektik, çünkü diğer sayılar N=2 için hali hazırda yazılı olacaktı. Bu sonlanma koşulu N>3 için de geçerlidir.

334 Bu şekilde aşağıdaki bağımlılığı yakaladık ilk pozisyondan itibaren geçerli her konuma 1..K arası değerleri sırasıyla yerleştirdikten sonra bir sonraki pozisyon için özyinelemeli olarak kendimizi çağırdık. Özyineleme N defa kendisini çağırdığında sonlanır ve sayılar K değerine kadar sıralı bir şekilde ard arda yazılır. C# metotu aşağıda gösterilmiştir: return for 1 1 Sayı dizisi loops adlı bir dizide saklanırken, PrintLoops() metoduyla özyineleme sonlandığında bu sayılar basılacaktır. NestedLoops( ) metotu sayıların yerleştirileceği pozisyonları belirten bir parametre almaktadır. Bir sonraki pozisyon için NestedLoops( ) metotunu özyinelemeli olarak çağırdıktan sonra döngü içinde adlandırdığımız olası her değer yazdırılır. numberofiterations değişkeni kullanıcı tarafından girilen K değeri için oluşturulmuştur.

335 Geçerli konum N olduğunda en son satır yazılmıştır ve özyineleme sonlanır. numberofloops değişkeni kullanıcı tarafından girilen K değeri için oluşturulmuştur. Geçerli konum N olduğunda en son satır yazılmıştır ve özyineleme sonlanır. Bu noktada tüm pozisyonlarda değerlerimiz vardır ve diziyi yazdırırız. Özyinelemeli olarak iç içe döngü ile ilgili bir çözümün tam bir uygulaması aşağıda verilmiştir: RecursiveNestedLoops static int static int static int static Console N = int Console K = static int 0 int Console Console return for 1 1 static for 0 Console {0} Console

336 N=2 ve K=4 için uygulama çıktısı aşağıdaki gibidir: 10.9 İç içe Döngüler - Yinelemeli Yaklaşım İç içe geçmiş döngülerin yinelemeli bir çözümünün uygulanması için, bir sonraki sayı dizisini bulan ve her yinelemede yazdıran aşağıdaki algoritmayı kullanabiliriz: 1. Her pozisyon başına 1 sayısını yerleştirin. 2. Mevcut sayı dizisini bastırın. 3. N pozisyon numarasını 1 artırdıktan sonra elde ettiğiniz değer K yı aşmışsa, onu tekrar 1 e eşitleyin ve N-1 pozisyonundaki değeri 1 ile artırın. Bu değer de K yı aşmışsa, onu tekrar 1 e eşitleyin ve N-2 pozisyonundaki değeri 1 ile artırın, ve bu böyle devam eder. 4. İlk pozisyon değeri K yı aştığı takdirde, algoritma sonlanır. 5. Adım 2 ile devam edin. Açıkladığımız yinelemeli iç içe döngüler algoritmasının uygulaması aşağıda sunulmuştur:

337 using class IterativeNestedLoops int void Console N = int Console K = int int Console Console void int while true 1 1 1 if 0 return 1 void int 0 1

338 static int 0 Console 0 Console Main() ve PrintLoops() metotları özyinelemeli uygulamadaki gibidir. NestedLoops() metotu yinelemeli ve parametresiz olarak çalıştığı için farklı uygulanmalıdır. Metotun en başında, InitLoops() metotuna bir çağrı yapılarak dizinin her pozisyonu için 1 atanır. Algoritmanın adımları çalışırken sonsuz döngüden metotların return deyimi ile çıkar. Algoritmanın 3.adımını uygulamak oldukça ilginçtir. K nın üzerindeki değerlerin kontrolü, 1 e eşitlenmeleri, ve bir önceki pozisyonun değerinin 1 ile artırılması (aynı kontrolü onun için de gerçekleştirdikten sonra) ardından K yı aşan değerler için while döngüsüne girilir. Mevcut pozisyon değeri bu amaçla 1 e eşitlenir. Bunun sonrasında bir önceki pozisyon geçerli olur. Ardından geçerli pozisyonun değeri 1 e eşitlenir ve döngünün başlangıcına dönülür. Bu eylemler mevcut pozisyon değeri K veya daha az iken devam eder. K yı aşınca (numberofiterations değişkeni K değerine erişince) sona erer. İlk pozisyon değeri K yı aşınca, döngü K defa çalışmıştır. Çıktının devamlılığını sağlayabilmek için (sonsuz döngüye devam etmek için) sayı dizisinin bir sonraki elemanı tekrar 1 e eşitlenir ve bu anda bu alt döngünün yinelenme sayısı K yı tükettiği için geçerli pozisyon (currentposition) 0 dan az ve negatif bir değer almıştır. Dizi endeksi negatif değer alamayacağı için bir if-kontrol deyimi içindeki return ile alt döngüden çıkılır. N=3 ve K=2 için uygulamayı test edebiliriz:

339 10.9.3 Hangisi Daha İyi: Özyineleme veya Yineleme? Eğer problem çözme algoritması özyinelemeli ise, özyinelemeli çözüm uygulaması aynı problem için geliştirilebilecek bir yinelemeli çözüm uygulamasından çok daha okunaklıdır ve tercih edilir. Eşdeğer bir algoritmanın tanımlanması ve algoritmaların eşdeğerliliklerini kanıtlamak bazen zor olabilir. Özyineleme kullanarak belirli durumlarda çok daha basit, daha kısa ve kolay çözümler sunmayı başarabiliriz. Öte yandan, özyinelemeli çağrılar çok daha fazla kaynak tüketebilir (CPU zamanı ve bellek). Her özyinelemeli çağrı için ayrıca yığında, metota giren ve/veya çıkan argümanlar, yerel değişkenler ve döndürülen değerleri saklayan yeni bellek alanları ayrılmalıdır. Çok fazla özyinelemeli çağrılar bir yan etki olarak bellek akıntısına neden olabilir. Özyinelemeli çözümler, bazı durumlarda, eşdeğer yinelemeli çözümlerinden daha zor okunabilir ve anlaşılabilir. Özyineleme güçlü bir programlama tekniğidir, ancak dikkatli analiz edilmelidir. Yanlış kullanımında, çözümlerin anlaşılması ve bakımı zorlaşır.! Eğer özyineleme kullanarak daha basit, daha kısa ve daha kolay bir çözüme varırsanız tercih edilebilir. Aksi takdirde, yinelemeli bir çözüm düşününüz.

340 Aşağıda özyinelemenin doğru kullanımını gösteren bir uygulama verilmiştir: RecursiveFibonacciMemoization static long static Console n = int int Console long 2 1 1 2 1 long Console fib({0}) = {1} static int 1 2 return

341 Main() ve PrintLoops() metotları özyinelemeli uygulamadaki gibidir. NestedLoops() metotu yinelemeli ve parametresiz olarak çalıştığı için farklı uygulanmalıdır. Metotun en başında, InitLoops() metotuna bir çağrı yapılarak dizinin her pozisyonu için 1 atanır. Algoritmanın adımları çalışırken sonsuz döngüden metotların return deyimi ile çıkar. Algoritmanın 3.adımını uygulamak oldukça ilginçtir. K nın üzerindeki değerlerin kontrolü, 1 e eşitlenmeleri, ve bir önceki pozisyonun değerinin 1 ile artırılması (aynı kontrolü onun için de gerçekleştirdikten sonra) ardından K yı aşan değerler için while döngüsüne girilir. Mevcut pozisyon değeri bu amaçla 1 e eşitlenir. Bunun sonrasında bir önceki pozisyon geçerli olur. Ardından geçerli pozisyonun değeri 1 e eşitlenir ve döngünün başlangıcına dönülür. Bu eylemler mevcut pozisyon değeri K veya daha az iken devam eder. K yı aşınca (numberofiterations değişkeni K değerine erişince) sona erer. İlk pozisyon değeri K yı aşınca, döngü K defa çalışmıştır. Çıktının devamlılığını sağlayabilmek için (sonsuz döngüye devam etmek için) sayı dizisinin bir sonraki elemanı tekrar 1 e eşitlenir ve bu anda bu alt döngünün yinelenme sayısı K yı tükettiği için geçerli pozisyon (currentposition) 0 dan az ve negatif bir değer almıştır. Dizi endeksi negatif değer alamayacağı için bir if-kontrol deyimi içindeki return ile alt döngüden çıkılır. N=3 ve K=2 için uygulamayı test edebiliriz: Gözle görülür bir fark vardır. Önceki örnekte n=100 için sonsuza giden bir hesaplama nedeniyle sonuç uzun zaman sonra hesaplanırken, optimize edilmiş çözüm ile sonuç hemen hesaplanıyor. Daha sonraki Veri Yapıları ve Algoritma Karmaşıklığı Bölümü nde not edileceği üzere, birinci çözüm üssel, ikinci çözüm doğrusal zamanda çalışmaktadır.

342 10.9.4 Fibonacci Sayıları Özyinelemenin Yanlış Kullanımı Bir Fibonacci dizisinin n.elemanını bulduğumuz örneğe yeniden göz atalım ve özyinelemeli çözüme daha dikkatli olarak yaklaşalım. 2 int return 1 return 1 2 Bu çözüm, sezgisel, kısa ve anlaşılırlığı yüksektir. İlk bakışta özyinelemenin iyi uygulanabileceği bir örnekmiş gibi görünüyor. Ancak gerçekte, özyinelemenin kullanımına dair doğru olmayacak bir örnektir. Nedenini açıklamak gerekirse, özyinelemeli uygulamayı aşağıdaki şekilde geliştirdiğimizi düşünelim: RecursiveFibonacci static Console n = int int Console long Console fib({0}) = {1} static int 2 return 1 return 1 2

343 n=100 için, hesaplamalar kimsenin beklemeyi göze almayacağı kadar uzun süreceği için uygulama son derece verimsiz olacaktır. Özyinelemeli her çağrı için iki özyinelemeli çağrı daha yapılıyor. Çağrılar, bu nedenle, aşağıdaki şekilde gösterildiği gibi katlanarak büyümektedir. fib(100) hesaplanması için gerekli adımlar 100 üssü 1,6 mertebesindedir, matematiksel olarak bu sayı kanıtlanmıştır. Buna rağmen, çözüm doğrusal olsaydı, bu sayı 100 olacaktı. Nedeni, çok fazla hesap gerektirmesindendir. Aşağıdaki Fibonacci ağacından da görülebileceği gibi, fib(2) sayısı birden fazla yerde göze çarpmaktadır. 10.9.5 Fibonacci Sayıları Özyinelemenin Doğru Kullanımı Fibonacci sayılarının hesaplanması için özyinelemeli metotu optimize etmenize yardımcı olabiliriz. Dizide zaten hesaplanan sayıları hatırlayarak (saklayarak) ve ancak eğer sayı henüz hesaplanmamışsa özyinelemeli çağrıyı yapmaya hazırlanmakla optimizasyon sağlanabilir. Bilgisayar bilimlerinde dinamik optimizasyon veya memoizasyon olarak bilinen bu optimizasyon tekniği sayesinde (hafıza teknikleri ile karıştırmayınız), özyinelemeli çözüm adımları doğrusal bir sayı kadar yinelendikten sonra sona erer.

344 Aşağıda özyinelemenin doğru kullanımını gösteren bir uygulama verilmiştir: RecursiveFibonacciMemoization static long static Console n = int int Console long 2 1 1 2 1 long Console fib({0}) = {1} static int 1 2 return Gözle görülür bir fark vardır. Önceki örnekte n=100 için sonsuza giden bir hesaplama nedeniyle sonuç uzun zaman sonra hesaplanırken, optimize edilmiş çözüm ile sonuç hemen hesaplanıyor. Daha sonraki Veri Yapıları ve Algoritma Karmaşıklığı Bölümü nde not edileceği üzere, birinci çözüm üssel, ikinci çözüm doğrusal zamanda çalışmaktadır.

345 10.9.6 Fibonacci Sayıları Yinelemeli Çözüm Fibonacci sayılarının ardışık hesaplanmasıyla özyineleme kullanmadan problemi çözmenin de mümkün olabileceğini fark etmişsinizdir. Bu amaçla dizinin sadece son iki elemanı saklanır ve bir sonraki elemanın değerini hesaplamak için saklanan değerler kullanılır. Fibonacci sayılarını yinelemeli hesaplayan algoritmanın bir uygulaması aşağıda verilmiştir: IterativeFibonacci static Console n = int int Console long Console fib({0}) = {1} static int long 0 long 1 long 1 for 2 return Bu çözüm kısa ve zarif görünüyor, verimlidir ve fazla bellek gerektirmez, ancak yine de özyineleme için varolan riskleri tamamen yok etmiyor. Bir başka öneriyle önceki örnekleri sonuçlandıralım:! Özyineleme güçlü bir programlama tekniğidir, fakat çalışması ve etki alanı hakkında yeterli bilgiye sahip olunmadığı sürece ne olduğunu fark edemeyebilir ve kendi bindiğiniz dalı kesebilirsiniz. Bu da yanlış programlamaya neden olacağı için dikkatle kullanmalısınız!

346 Eğer bu kuralı uygularsanız, özyinelemenin yanlış kullanım olasılığı azalır ve sonuçlarından etkilenmezsiniz. 10.9.7 Özyineleme ve Yineleme Daha Fazlası Doğrusal hesaplama gerektiren süreçler için özyineleme kullanmak genellikle zorunlu değildir, çünkü yinelemeyle sonuçlar kolayca bir araya getirilebilir, bu basit ve etkili bir hesaplama tekniğidir. Örneğin, doğrusal işlem gerektiren faktöriyel hesaplama probleminde, bir sonraki elemanın değeri sadece önceki gelen tüm elemanlara bağlıdır. Doğrusal hesaplama işlemlerinin karakteristik özelliği, her adımda sadece bir kere özyineleme çağrısı ile hesabın sadece bir yönde ilerlenerek tamamlanabilmesidir. Doğrusal hesaplama sürecini şematik olarak şu şekilde tanımlayabiliriz: void Metot gövdesinde sadece bir kere özyinelemeli çağrı yapılmışsa özyineleme kullanmak gerekli değildir, çünkü yineleme anlaşılır ve barizdir. Bununla birlikte, bazen, dallanmış bir hesaplama süreci (ağaç gibi) ile yüz yüze kalırız. Örneğin, N iç içe geçmiş döngülerin taklit edilmesi, yinelemeyle kolayca ifade edilemez. Muhtemelen, iç içe geçmiş döngüleri taklit eden yinelemeli algoritmamızın tamamen farklı bir prensipte çalıştığını fark etmişsinizdir. Aynı şeyi yineleme olmadan uygulamayı deneyin ve kolay olmadığını göreceksiniz. Normalde, her özyineleme, (programın yürütülmesiyle oluşturulan) çağrı yığınını kullanarak yinelemeye dönüşebilir, ancak bu karmaşıktır ve bunu yapmanın hiçbir yararı yoktur. Özyineleme, belirgin bir yinelemeli çözüme sahip olmadığımız bir problem için basit, anlaşılması kolay ve etkili bir çözüm sağlaması durumunda kullanılmalıdır. Yinelemenin her adımında ağaç benzeri (dallı) hesaplama süreçlerinde birkaç özyinelemeli çağrı yapılır ve hesaplamanın şeması ağaç olarak görülebilir (doğrusal hesaplamalarda olduğu gibi bir liste değil). Örneğin, Fibonacci sayılarını hesapladığımızda özyinelemeli çağrı ağacının nasıl olacağını gördük.

347 Ağaç benzeri dallara sahip bir hesaplama sürecinin şematik gösterimi aşağıdaki gibidir: void Ağacın hesaplama süreçleri (doğrusal süreçlerin aksine) doğrudan özyinelemeli olarak ifade edilemez. Fibonacci için durum basittir, Çünkü her bir sonraki sayı önceki sayıya bakarak hesaplanabilir. Bazen, her sonraki sayı, yalnızca önceki yoluyla hesaplanmaz, bir sonraki sayı da gereklidir, ve özyinelemeli bağımlılık o kadar basit değildir. Bu durumda memoizasyon tekniğini uygulayarak yinelenen hesaplamalardan kaçındığınız takdirde, özyineleme doğru olarak uygulanırsa, çok verimli sonuç verir.! Dallı özyinelemeli hesaplamalar için özyineleme kullanın (ve her bir değerin yalnızca bir kez hesaplandığından emin olun). Doğrusal özyinelemeli hesaplamalar için özyinelemeyi tercih edin. Son ifadeyi klasik bir örnekle göstereceğiz. 10.9.8 Labirentte İz Sürmek Örnek N x M kapıdan oluşan dikdörtgen şekilli bir labirent veriliyor. Kapıların her biri geçilir veya geçilmez özelliklidir. Sol üst köşesinden labirente giren bir kimse, sağ alt köşeye ulaşmak istiyor. Kişi yukarı, aşağı, sağa veya sola yöne bir seferde bir kapı hareket edebilir. Bir kapıdan geçildiğinde, geri dönüş yapılmaksızın tekrar aynı kapıdan geçilmesi yasaktır. Ancak kapı serbest hale gelebilirse geçilebilir. Sağ alt köşeye ulaşıldığına yol tamamlanıyor. Labirente giren bir kişinin girişten çıkışa kadar izlediği tüm yolları görüntülemek istiyoruz.

348 Özyineleme kullanılarak çözülebilen problemlere tipik bir örnektir. Bu problemin yineleme ile çözümü daha karmaşık ve uygulaması zor olacaktır. Şekiller üzerinden problemi anlamaya çalışınız: Belirtimleri yerine getiren girişten çıkışa 3 ayrı yol olduğunu görebilirsiniz. (sadece geçerli kapılardan geçilmiştir ve aynı kapıdan iki kere geçilmemiştir). Bu 3 yol aşağıda izlenmiştir: Yukarıdaki şekillerde sayılar yolu izlerken kaç defa dönüş yapıldığına işaret ediyor.

349 10.9.9 Labirent Yolları Özyinelemeli Algoritma Bu problemi nasıl çözeriz? Labirentin herhangi bir pozisyonundan sonuna kadar arama işlemini bir özyinelemeli süreç olarak aşağıda gibi tanımlayabiliriz: 1. Başlangıç pozisyonu (0,0) olmak üzere, labirentin geçerli konumu (row, col) olsun. 2. Mevcut konum (N-1, M-1) ise hedefe varılmıştır. Aranan çıkış sağlanmıştır. 3. Kapı geçilmezse, geri gideriz (durma hakkımız yoktur). 4. Pozisyon daha önce ziyaret edilmişse, geri gideriz (iki kere geçme hakkımız yoktur). 5. Bunların dışında, 4 özyineleme sözkonusudur: Sağa gidiş: (row, col + 1) Aşağı gidiş: (row + 1, col) Yukarı gidiş: (row 1, col) Sola gidiş: (row, col - 1) Bu algoritma çözümünü analiz ederken özyinelemeli düşünmeliyiz. Problem cümlesi verilen bir pozisyondan çıkışa giden yolu bulmaktır. Alt problemlere bölünmesi gerekirse: geçerli konumun bir sağındaki pozisyondan çıkışa giden yolu aramak geçerli konumun bir aşağısındaki pozisyondan çıkışa giden yolu aramak geçerli konumun bir yukarısındaki pozisyondan çıkışa giden yolu aramak geçerli konumun bir solundaki pozisyondan çıkışa giden yolu aramak Ulaştığımız her olası konumdan dört muhtemel yönü kontrol edersek ve bir daire içerisinde hareket etmezsek (daha önce üzerine adım attığımız konumlardan geçmekten kaçınarak) er yada geç çıkış yolunu bulmalıyız (varsa). Bu sefer özyineleme önceki problemler kadar basit değildir. Her adımda çıkışa ulaşıp ulaşmadığımızı ve yasak bir konumda olup olmadığımızı kontrol etmeliyiz; Bundan sonra, bulunduğumuz pozisyonu ziyaret edilmiş olarak işaretlemeliyiz ve dört yönde aramayı özyinelemeli olarak çağırmalıyız. Özyinelemeli aramalardan döndükten sonra, başlangıç noktasını ziyaret edilmemiş olarak işaretlemeliyiz. Bu gibi gezinme, geriye doğru arama (backtracking) olarak bilinir.

350 10.9.10 Labirent Yolları Uygulama Algoritma uygulaması için uygun bir şekilde labirenti temsil etmeliyiz. İki boyutlu bir karakter dizisi kullanacağız. Geçilir pozisyonlar boşluk karakteri ile, çıkış pozisyonu e ile, geçilmez pozisyonlar * yıldız karakteri ile gösterilecek. Başlangıç pozisyonu geçilir pozisyondur. Ziyaret ettiğimiz pozisyonları s ile işaretleyeceğiz. Bir labirent temsili aşağıda verilmiştir: char * * * * * * * * * * e Labirent yollarının bulunması için özyinelemeli metotun uygulaması şöyle olacaktır:

351 char * * * * * * * * * * e 0 0 1 // We are out of the labyrinth return // Check if we have found the exit e // Mark the current cell as visited s // Invoke recursion to explore all possible directions 1 // left 1 // up 1 // right 1 // down // Mark back the current cell as free 0 0

352 Uygulama yukarıdaki tanımı aynen gerçekleştirir. Bu durumda labirentin boyutları N ve M değişkenlerinde saklanmaz, ancak iki-boyutlu bir lab dizisi yardımıyla, labirentin sütun sayısı lab.getlength(1) ve satır sayısı lab.getlength(0) olmak üzere elde edilir. Arama yapan özyinelemeli metotun girişinde ilk koşul labirentin dışına çıkıp çıkmadığımızın kontrolüdür. Bu yasak durumda, labirentin sınırları dışına çıkılmaması için metot sonlandırıldı. Bundan sonra, çıkışı bulup bulmadığımızı kontrol etmeliyiz. Bulduysak, uygun bir mesaj yazdırıyoruz ve bulunduğunuz konumdan ileriye doğru aramayı sonlandırıyoruz. Sonra, mevcut karenin kullanılabilir olup olmadığını kontrol ediyoruz. Eğer pozisyon geçerliyse, ve önceki adımların bazılarında bu kareye basmadıysak (başlangıç pozisyonundan labirentin mevcut hücresine kadar olan mevcut yolun bir parçası değilse), mevcut kare kullanılabilir. Eğer hücre mevcutsa, üzerine basarız. Tanım gereği bu hücreyi s karakteriyle işaretleyerek ziyaret edilmiş olarak kaydetmeliyiz. Bundan sonra dört olası yönde özyinelemeli olarak bir yol ararız. Bu dört özyinelemeli arama çağrısından geri dönüldüğünde, geçerli hücreden geri adım atarak, mevcut hücre ile işaretlenerek serbest bırakılır. Geçerli konumun serbest bırakılması önemlidir, çünkü geri döndüğümüzde mevcut yolun bir parçası değildir. Bu belirtimi atlarsanız, yolların hepsi değil, ancak bazıları bulunmuş olabilir. Labirentin çıkış yollarını bulmak için uygulanan özyinelemeli metot bu şekilde özetlenmiştir. Şimdi sadece bu metotu (0, 0) başlangıç pozisyonundan başlatarak Main() metotu içinden çağırmalıyız. Program yürütüldüğünde aşağıdaki sonucu döndürür: Çıkışın tam üç kez bulunduğunu görebilirsiniz. Algoritma düzgün çalışıyor gibi görünüyor; ancak daha anlamlı olabilmesi için çıkış yolunu yazdırmamız gerekiyor.

353 10.9.11 Labirent Yolları İz Kayıtları Özyinelemeli algoritma ile bulduğumuz yolları yazdırmak için, her adımda atılan adım yönünü saklayacak bir diziye ihtiyacımız var: sağ R (right), aşağı D (down), yukarı U (up), sol L (left). Bu dizi her an için labirentin başından itibaren geçerli yolu tutacak. Bir karakter dizisi ve sayaç yardımıyla, şu anki özyineleme derinliğini, yani özyinelemeli olarak kaç pozisyon ilerlediğimizi tutabiliriz. Özyinelemeye her girişte sayaç 1 artırılmalıdır. Dönüşte 1 azaltılmalıdır. Çıkış yolu bulunduğunda, yazdırılması için gerekli veriler hazırdır (0 ile başlayan ve sayaç endeksine kadar olan dizinin tüm karakterleri). Dizinin boyutları ne olmalıdır? Bu sorunun cevabı kolaydır; bir hücreye en fazla bir kere girilebildiğine göre en uzun yol N * M kadardır. Örneğimizdeki labirent 7 * 5 boyutlarında olduğu için dizinin en fazla uzunluğu 35 kabul edilmelidir. Not: List<T> veri yapısını biliyorsanız, karakter dizisi yerine List<char> kullanmanız daha uygun olabilir. Listeleri daha detaylı olarak Doğrusal Veri Yapıları Bölümü nde öğreneceksiniz. Açıklanan fikrin uygulanması aşağıda verilmiştir:

354 char * * * * * * * * * * e 0 0 1 // We are out of the labyrinth return // Append the direction to the path // Check if we have found the exit e // The current cell is free. Mark it as visited s // Invoke recursion to explore all possible directions 1, L // left 1 U // up 1, R // right 1 D // down // Mark back the current cell as free // Remove the last direction from the path

355 static void char[] int Console Found path to the exit: Console Console. void S Labirentin çıkış yolunu arayan özyinelemeli metota bir parametre daha ekledik: geçerli konuma ulaşmak için kullandığımız yönü belirtir imleç : R, D, U, L. Bu parametre başlangıç pozisyonundan geçerken S değerine sahiptir. Ancak bu bir anlam taşımaz. Yolu yazdırırken, bu ilk elemanı atlarız. Programı başlatırsak, labirentin başından sonuna kadar üç yol bulunur: 10.9.12 Labirent Yolları Programın Test Edilmesi Algoritma düzgün çalışıyor gibi görünüyor. Biraz daha fazla örnek ile test etmek hatasız çalıştığını gösterecektir. 1x1 boş labirent ile programı test edebilirsiniz, 3 x 3 boş labirent ile, yada çıkış yolu olmayan bir labirent ile ve birçok yola sahip boyutları büyük bir labirent ile test edebilirsiniz. Bu testler her durumda programın düzgün çalıştığına sizi ikna edecektir.

356 Örnek girdi (1 x 1 boş labirenti): char e Örnek çıktı: Çıktıdaki yol beklendiği gibi boştur (uzunluğu 0 dır), çünkü başlangıç pozisyonu çıkışla aynıdır. Bu durumda görselleştirmeyi geliştirmek isteyebilirsiniz ( Yol boştur yazdırmak, vs.) Örnek girdi ( 3 x 3 boş labirenti): char e

357 Yukarıdaki örnek labirentin çıktısı: Çıktının doğru olduğunu kabul edebilirsiniz - bunlar tüm çıkış yollarıdır. Bir başka örnek girdi (çıkışı olmayan 5x3 labirenti): char * * * * * e Çıktısı: (çıkış yok) Çıktının doğru olduğunu görebilirsiniz, ancak yine de herhangi bir çıktı yerine daha anlamlı bir mesaj ekleyebilirsiniz (örneğin, Çıkış yok! ).

358 Şimdi çok büyük bir labirent için programa bakalım: Örnek girdi (15x9 labirenti): char Program çalıştırıldığında çıkış yollarını yazdırmaya başlar, ancak sonlanmayacaktır çünkü çıkış yolları oldukça fazladır. Birkaç yol fazlası aşağıda verilmiştir:

359 Şimdi, son bir örnek için programı çalıştıralım: Örnek girdi (çıkışı olmayan 15 x 9 labirenti): char * * * * * * * * * * * * * * * * * * * * * e Program çalıştırıldığında çıktı yazdırmadan kilitleniyor. Çok uzun zamandır çalıştığı için sorun olabilir. Sorun nedir? Ortalama boyutu 20 olan bir labirent için 4 kere özyinelemeli çağrı vardır. Bunun anlamı 420 kere özyineleme çağrılır. Bu da programın aramasını tamamlayıp sonlanması için oldukça büyük bir rakamdır. Belki sayılar tam kesinlikte değildir, ancak programın çalışma zamanı hakkında bize bir fikir veriyor. Sonuç nedir? Değişkenler çok fazla olduğunda geriye doğru izleme yöntemi işe yaramaz. Bu sorun büyük bir labirentin tüm çıkış yollarını bulma problemi için geçerlidir. Çalışma zamanı sorununu çözmek için daha fazla enerji tüketmenizi istemeyeceğiz. Ancak şunu belirtmekte fayda vardır ki, problem tanımımızı çok az değiştirerek, yani örneğin sadece bir yol bulunmasını isteyerek, çalışma zamanı sorunundan sizi kurtarabiliriz. Programda geri dönülen yollar için özyinelemenin mevcut geçerli pozisyonunu serbest bırakmayarak bu değişikliği gerçekleştirebiliriz. Bu, aşağıdaki satırları koddan silmek demektir: // Mark back the current cell as free Bunun sonrasında program çok hızlı olarak çıkış olup olmadığına karar verir, ve varsa onu çabucak bulur. En kısa veya en uzun değil, sadece ilk yolu muhakkak bulacaktır.

360 10.10 Özyineleme Kullanımı Sonuçlar Labirentte çıkış yolu arama probleminden genel sonuç zaten formüle edilmiştir: Eğer özyinelemenin nasıl çalıştığını çok iyi anlamakta güçlük yaşıyorsanız, kullanmayınız! Özyinelemeli metotları yazarken dikkatli olunuz. Özyineleme en çok kombinasyon problemlerini çözmek için elverişli güçlü bir programlama tekniğidir; fakat yanlışlara neden olunabilir ve dikkatsiz kullanma nedeniyle herkese önerilmez. Programın sonlanamaması ve yığın taşmasına neden olması durumlarında, yinelemeli yaklaşımlarla probleme bir çözüm geliştirmeye, problemin tanımı analiz edildikten sonra karar verilmelidir. Örneğin, labirentte en kısa çıkış yolunu bulmak ile tanımlı problemi analiz ederseniz özyineleme kullanmadan, sadece (BFS) Breadth-First Search yardımıyla çözebilirsiniz. Kuyruk (queue) veri yapısı ile uygulamaları bulunan BFS algoritması Wavefront algoritması olarak da bilinir. http://en.wikipedia.org/wiki/breadth-first_search makalesindeki BFS algoritması hakkında Wikipedia da daha fazla bilgi bulabilirsiniz.

361 Alıştırmalar 1. n iç içe döngüyü simüle eden bir program geliştirin. 2. k sınıfından n eleman için tüm çeşitlemeleri tekrarlarıyla listeleyen bir özyinelemeli program geliştirin. Örnek girdi: Çıktısı: Aynı görev için yinelemeli algoritma uygulayın. 3. n elemanlı bir küme içinden k elemanlı tüm kombinasyonları tekrarlarıyla listeleyen bir özyinelemeli program geliştirin. Örnek girdi: Çıktısı: Aynı görev için yinelemeli algoritma uygulayın. 4. Verilen bir sözcük dizisi ve k değeri için, sözcük dizisinin k elemanlı tüm öz alt kümelerini listeleyen özyinelemeli program geliştirin. Örnek girdi: Çıktısı: Aynı görev için yinelemeli bir algoritma uygulayın.

362 5. Verilen bir sözcük dizisi için, sözcük dizisinin tüm alt kümelerini listeleyen özyinelemeli program geliştirin. Örnek girdi: Çıktısı: Aynı görev için yinelemeli algoritma uygulayın. 6. Birleştirme-sıralama (merge-sort) algoritmasını özyinelemeli uygulayın. Bu algoritmaya girdi olarak verilen bir dizi önce iki eşit parçaya bölünür ve özyinelemeli olarak sıralanır. Sonrasında sıralanan parçalar bütün diziyi oluşturmak üzere birleştirilir. 7. Verilen bir n tamsayısı için 1, 2,, n sayılarının tüm permütasyonlarını listeleyen özyinelemeli program geliştirin. Örnek girdi: Çıktısı: Aynı görev için yinelemeli algoritma uygulayın. 8. Verilen bir tamsayı dizisi ve N sayısı için, dizideki sayıların toplamı N olan tüm alt kümelerini bulan özyinelemeli program geliştirin. Örneğin {2, 1, 3, -1} dizisi ve N=4 için, toplamı N=4 olan sayılar şöyle elde edilir: 4=2+3-1; 4=3+1. 9. Verilen bir pozitif tamsayı dizisi için, elemanları toplamı S olan herhangi bir alt kümesinin olup olmadığını hesaplayan bir program geliştirin. Büyük diziler için program verimli çalışabilir mi? 10. Verilen iki pozisyon için, geçerli ve geçersiz kapıları olan bir labirent üzerinde iki pozisyon arasında bulunan tüm yolları hesaplayan bir program geliştirin. 11. Labirentin en kısa yolunu bulmak için BFS (breadth-first search) algoritmasını uygulayan bir program geliştirin.

363 12. İki pozisyon arasındaki tüm yolları hesaplayan bir önceki alıştırmada istenen programı iki pozisyon arasında bir yol olup olmadığını kontrol edecek şekilde değiştirin. 13. Geçerli ve geçersiz kapıları olan verilen bir labirent için tek doğrultuda yol dönüş yapmaksızın, başka tarafa sapmaksızın, komşu ve ardışık pozisyonlardan oluşan en büyük alanı hesaplayan program geliştirin. 14. C:\ hard diski içindeki tüm klasör ve dosyaları ziyaret eden ve özyinelemeli listeleyen bir program geliştirin.