MIT Açık Ders malzemeleri

http://ocw.mit.edu

 

6.046J Algoritmalara Giriş, Güz 2005

 

Bu materyallerden alıntı yapmak veya kullanım şartları hakkında bilgi almak için:

 

 Erik Demaine ve Charles Leiserson, 6.046J Introduction to Algorithms, Fall 2005. (Massachusetts Institute of Technology: MIT OpenCourseWare). http://ocw.mit.edu (Giriş Tarihi:  08, 01, 2010).                               Lisans: Creative Commons Attribution-Noncommercial-Share Alike.

Not: Lütfen atıf taparken bilgilere eriştiğiniz gerçek tarihi kullanınız.

Atıflar ve Kullanım Koşulları konusunda daha fazla bilgi için:

 http://ocw.mit.edu/terms ve http://www.acikders.org.tr sitesini ziyaret ediniz.

 

DERS 4

 

Günaydın; bugün çok ilginç bir algoritma olan Quicksort yani Çabuk sıralamadan bahsedeceğiz. Bu algoritma Tony Hoare tarafından 1962 de bulunmuştu. Birçok açıdan bakıldığında gerçekten ilginç bir algoritma haline dönüştü; ismine yakışır bir şekilde. Bu yüzden bugünkü ders de hem zor, hem de hızlı olacak.

...Bu nedenle yanınızdakinin uyuduğunu görürseniz ona “haydi uyan, ders devam ediyor” demeniz gerekiyor.

--Bu bir “divide & conquer” yani “böl ve fethet” tipi bir algoritmadır.

Burada elemanlar “yerlerinde” sıralanıyor, yani dizilimin elemanları bulundukları dizi içerisinde, yani oldukları yerde. Aynı araya yerleştirme sırasında olduğu gibi oldukları yerde sıraya konuluyorlar. Birleştirerek sıralama, bunu yapmaz, birleştirme işlemini yürütmek için ek bir bellek alanına ihtiyaç duyulur. Bu birleştirmeyi doğrusal zamanda yapar ama aynı mekanda yapmaz. Bu işlemi sadece yeniden sıralayarak yapmaz. Bu algoritma ise yerleştirmeyi yerinde yaptığı için işlevseldir, çünkü depolama alanının verimli kullanımını sağlar; ve bununla biraz ince ayar yaparsanız daha da pratik bir uygulama haline gelebilir. İlk halinin uygulamasını kullanacak olursanız o kadar verimli bir algoritma değildir, ama yaptığınız şey standart türde uygulamalarsa ve bir şeyin koşma süresini biraz geliştirmek istiyorsanız, çabuk sıralama çok uygun bir araç haline gelebilir. Bunun nerelerde kullanılacağından az sonra bahsedeceğim; evet bu böl fethet  paradigmasına uyan bir algoritmadır.

---Bunun birinci adımı bölmektir. Bu işi eldeki dizilimi partition’lara yani bölüntülere ayırarak yapıyor.Yani girdi dizilimini pivot dediğimiz bir elemanın etrafında 2 altdizilim halinde bölüntülere ayırıyorsunuz

 

Bu iki bölüntüden ilki, soldakindeki tüm elemanlar, pivot yani x’den küçük eşit, ve sağ alt dizilimdeki elemanlar ise x’den büyük veya eşit oluyor.  

 

Bunu bir şekille girdi dizilimi üzerinde açıklayacak olursak, bu bölüntü alımına bir x elemanı alınıyor, pivot elemanıdır bu, ve bölüntünün bu bölgesindeki elemanlar,  x’e eşit yada küçük, bu bölüntü adımından sonraki, buradaki elemamlar ise x’den daha büyük yada eşit oluyorlar.

 

---Bundan sonra fethetme adımı daha kolay hale geliyor öyle ki sadece bu iki altdizilim özyinelemeli olarak aynı yöntemle sıralanıyor. Yani x’ e eşit yada  daha küçük olan elemanları özyinelemeli olarak sıralıyorum veya x’ e eşit veya daha büyük olan elemanları özyinelemeli olarak sıralıyorum.

 

---Sonunda sonra bunları birleştirmek önemsiz bir iş. Bu birleştirmeden önce x’e eşit veya küçük olan elemanları sıraladığım için ondan sonra da x’e eşit veya büyük olan elemanları sıraladığım için iki liste de sıralanmış durumda; böylece bunların birleşmesi için çok önemli bir iş gerekmiyor.

 

---Quicksort ya da çabuk sıralamadaki anahtar adım bölüntülere ayırmadır. Bütün işin çoğu burada yapılıyor; bu durumda siz çabuk sıralamayı bir özyinelemeli bölüntülere ayırma olarak düşünebilirsiniz. Yapılan tüm iş bu.

Birleştirerek sıralamadaki ana iş özyinelemeli birleştirme yaparken, çabuk sıralamada ise bunun tersine özyinelemeli bölüntüler elde ediliyor Buradaki anahtar olay, bölüntü alt yordamının doğrusal zamanda, yani Θ(n) olması. Şimdi bunun sözde kodunu yazacağım, buradaki, kod kitaptakinden biraz farklı. Kitapta da bir tane var, aslında kitapta da biraz daha farklı bir problem göreceksiniz ama temelde ikisi de aynı fikir etrafında oluşturuluyor.

 

---Özyinelemenin bir aşamasını ele alalım ve, buradaki A diziliminin p ve q arasındaki parçası için (A [p,q] bölüntüsünü bakalım. Burada baktığımız şey, A altdiziliminde [p,q]  arasındaki değerler; burada biz pivot olarak bu aralıktaki ilk eleman olan  A[p]’yi seçiyoruz.  Bilginiz için kitapta p yerine q olarak seçilmiş, ben burada p’yi kullanacağım; aslında bir şey fark etmez. Bundan sonra p için bir indeks oluşturacağız ve onun etrafında bir döngü kuracağız. Kodu işte bu; temelde bu bir for (için) döngüsü ve ortasında if (eğer) eğer komutu var.

Bu bölüntü adımında algoritmanın yapısı şöyle görünecek: 

Biz pivot olarak ilk elemanı tanımlıyoruz; ---burası p ve şurası da q. ---Bu bizim döngümüzün değişmezi olacak. Bu döngü çalışırken herhangi bir anda elimde indeks değeri en fazla i olan bölgede x’den küçük eşit elemanlar olacak. Aynı biçimde indeks değeri en fazla (j-1) olan bölgede de x’e eşit ya da büyük elemanlar da olacak; bunun dışındakiler hakkında bir bilgim yok. Bu nedenle i=p olarak başlıyorum ve j=(p+1) olarak başlıyorum. Bu şekilde işlem (p+1) den başlıyor ve x’in buradaki değeri dışında hiçbir şey bilinmiyor. Bu işlemin anafikri, değişmezin sonuna kadar korunacağına göre, ve bunu yapmasının yolu da bu döngü içerisinde ilerlerken bir A(j) değerini alıyor ve bunun x’den büyük olup olmadığına ya da eşit olup olmadığına bakıyoruz. Af edersiniz bunun x’den küçük ya da x’e eşit olup olmadığına bakıyor; eğer bu değer x’e eşit ya da ondan büyükse hiçbir şey yapmıyor. Çünkü o durumda, eğer bu x’e eşit ya da ondan büyükse, o zaman tekrar döngüsüne giriyor, bu sınır değişiyor ve döngü değişmezi geçerliliğini koruyor.  Eğer değişmezimi muhafaza etmek istiyorsam ve bu sonraki eleman x’e eşit veya ondan küçük ise bir problemim var demektir. Bu durumda yaptığı şey “pekiyi öyleyse, bu sınırı biraz kaydırayım ve x’e eşit veya ondan büyük bu elemanla x’ e eşit veya ondan küçük şu elemanın yerlerini değiştireyim.”  Bu yöntemle şu altdizilimin boyutunu değiştiriyor ve değişmezin geçerliği sağlanııyor. Aslında oldukça basit bir algoritma..

Evet, aslında hem çok sıkı hem de kolay bir algoritma; bu nedenle bu kod çok önemli bir kod, çünkü çok verimli çalışıyor. Şimdi prensipte n elemanı üzerindeki koşma süresi, Teta(n) düzeyinde, çünkü ben temelde n sayısında eleman üzerinde çalışıyorum ve her birisiyle sabit miktarda iş yapıyorum; sonra dışarıda da sabit miktarda iş yapıyorum. Onun için bu çok akıllı bir kod parçası, aslında bölüntülere ayırmanın prensibi de kolay degil mi? Eğer işlemi olduğu  “yerde”  yapma kaygısı olmasaydı, o zaman bunu yapmak çok kolay olurdu; bir elemanı ele alırdım ve o elemanı diğerlerinin hepsi ile karşılaştırırdım. Bir tanesini bir kutuya, diğerini de öteki kutuya atardım. Bu kesinlikle doğrusal zamanda olurdu ama genellikle bunu teorik olarak bu yolla yapmanız demek, sonunda elinizde iyi bir kod var anlamına gelmez. Ama bu kod aslında işlemi kendi yerinde yapmaya izin veriyor. Bunun özellikle iyi bir algoritma olmasının sebeplerinden biri de sabitlerin iyi seçilmiş olması; biz asimptotik çözümleme yaparken sabitlerle ilgilenmiyoruz ama aslında kod yazmaya başladığınız zaman sabitlerle ilgilenmek zorundasınız. Ama başlangıçta sabitlere çok zaman ayırmak yerine algoritmanızın bütününün hızlı olup olmadığını anlamanız gerekir.

 

Size bu yöntemin bir özetini vermek için şurada bir örnek yapacağım, ---burada öylesine uydurduğum bir örnek dizilim var, ve burada biz x’i yani pivotu ilk eleman olan 6  olarak belirliyoruz. Şimdi bu algoritmanın nasıl çalıştığına bakalım:

Başlangıç koşullarında i burada başlıyor ve j de şurada başlıyor; ve yaptığımız şey sağa doğru taramaya başlamak. Aslında kod, j indeksini birer birer artırarak önüne pivot’a eşit ya da ondan daha küçük bir değer çıkana kadar dizilimi sağa doğru tarıyor. Bu şartı sağlayan değer 5’dir. Ondan sonra kod bu iki değerin yerlerini değiştiriyor; bu şekilde.

---Böylece elimizde oluşan dizlim değerleri 6,5,13,10,8,3,2,11 olur. Bu arada i değeri bir artar ve j bıraktığı yerden işe devam eder ve elimizde pivottan daha küçük veya ona eşit bir değer olana kadar sağa doğru taramaya devam ederiz. Bu durumda bu değer 3; biz 3 ile 5’in yerini değiştiririz ve ---6,3,13,10,8,5,2,11 elde ederiz. Bu aşamada bu sefer i’ yi artırmaya devam ederiz ve j ile de buradan başlarız ve bu durumda karşımızda x’den daha küçük olan veya ona eşit olan bir şey olur. Sonra bu ikisinin değiş tokuşunu yapacağız; bir dakika burada hata yaptım değil mi? Ne yaptım ben, yanlış şeyleri değiş tokuş yaptım değil mi? Hah ne yazık ki bir bilgisayar değilim; iyi, değiştirmemiz gereken şey şuydu değil mi? Değiş tokuşa girmesi gereken (i+1) olmalıydı, değil mi? Önceden ben i’ yi değiştirmiştim oysaki (i+1)’i değiştirmem gerekiyor. Onun için şu ana kadar olan her şey yanlış, şimdi doğru şeylerin yerlerini değiştirelim: Elimizde 6,5,3,10,8,13,2,11 var; hayret edilecek  şey benim notlarımda da yanlış yazılmış olması.. Bu i, bu da j ve şimdi baktığımızda pivottan küçük eşit olan bir şey görebiliyorum. Bu nedenle bunun ve (i+1)’in değiş tokuşunu yapacağız ve böylece elimizde 6,5,3,2,8,13,10 ve 11 olacak ve bu noktada da i’yi ---şuraya artıracağız. Bu sırada j buraya gidiyor ve j sona kadar gidiyor ve döngü duruyor. Döngü durduğu zaman yapmamız gereken bir değiş tokuş daha olacak; bu durumda pivot elemanımızı iki alt dizilimin arasına yerleştiriyoruz. Burada bununla şunu değiştiriyoruz ve bu da bize 2,5,3,6,8,13,10 ve 11 i veriyor. Buradaki pivot da şu ve burada gördüğünüz her şey pivottan küçük eşit ve burada gördüğünüz her şey de pivottan büyük eşit. .

 

Evet, Quicksort yani çabuk sıralama yordamı bu. Bu bölüntü yordamını bir kez elde ettikten sonra çabuk sıralama kodunun yazılması kolaydır. ---Burada return i yazmam gerekiyordu değil mi? Çünkü pivotun indeksi olan i değerinin döndürülmesi gerekiyor; burada ben pivotun son bulunduğu yerin indeksi olan i değerini döndürmeliyim ki bir sonraki bölüntüyü tanımlayabileyim. Özür dilerim; Sonuçta bu değer Partiton (A, p, q) çağrısı ile r’ye atanıyor ve ondan sonra bölüntülerden (A, p, r-1) in; ve ardından (A, r+1, q)’ya kadar olan kısımların  çabuk sıralamasını yine özyinelemeli olarak yapıyoruz. Hepsi bu işte, kod bu.

 

İlk hal, ilk çağrı, (A, 1, n) nin çabuk sıralamasını bulmak; ama bölüntü yapmış olmamız nedeni ile iki bölümü yani soldaki ve sağdaki bölümleri ayrı ayrı çabuk sıralamanız gerekiyor. Bu arada sınır durumunuzdan da bahsetmemizde yarar var; eğer burada 0 veya1 eleman varsa, ki olabilir, bu durumda yapılacak hiçbir şey olmaz, çünkü dizilim ya boştur ya da sadece bir elemanı olduğundan zaten sıralıdır.

Çabuk sıralamayı hızlandırmak için yapılabilecek numaralardan ya da ince ayarlardan biri dizilim bölüntüsünü içerisinde az sayıda eleman olması durumunda özel amaçlı bir sıralama yordamı geliştirmektir. Örneğin, elinizde sadece beş elemanınız varsa, bu kadar yinelemeyi yapmak yerine bu beşini çözümlemek için başka bir kod yazabilirsiniz. Kodlamaların başka türleri de var; bu koda kuyruk özyinelemeli kod deriz ve bunlarla siz tail recursion optimization yani kuyruk özyineleme iyileştirmesi yapabilirsiniz. Bu kodu hızlandırmak için bunun dışında da başka optimizasyon ya da en iyileme yöntemleri vardır; evet daha da iyileştirmek için orasında burasında biraz ince ayar yapabilirsiniz ama özünde burada açıklananlar bu verimli bölüntü yordamının çekirdeğidir, yada ana damarıdır. Algoritması budur. Bunun ne kadar hızlı çalıştığını ölçümlemek aslında biraz zor bir iştir, bu çözümlemeyi yaparken bütün elemanların birbirinden farklı olduğunu var sayacağız. Aslında elimizdeki kodun tekrarlanan elemanlar varsa o kadar başarılı olmadığını görürüz. Böyle bir durumda Hoare’nin orijinal bölüntü yordamı bundan çok daha verimli; yani sıralamanızda tekrarlar, ya da duplikasyonlar varsa..

 

Size Hoare’un orijinal yordamına bakmanızı öneririm; bölüntü yordamı için çok daha karmaşık bir değişmez yapısı var ama aynı şeyi yapıyor, yalnız biraz daha karmaşık. Eğer sayıların bazıları aynıysa, o zaman bunları farklı hale getirmek için yapılacak işler var ama Hoare’un kodunu kullansanız da olur; Çünkü Hoare’un orijinal kodu tekrarlayan sayılar durumunda oldukça iyi çalışır.

 

Ama bu örneğin anlaşılması daha kolay.  

--Diyelim ki n elemanı üzerindeki en kötü koşma süresi T( n) olsun; bu durumda en kötü durum nedir? Yani çabuk sıralama için en kötü durum ne olacaktır?

---Doğru, eğer pivotu seçerken buradaki her şey bundan küçük veya her şey bundan büyük olursa, o zaman dizilimi bölüntülere ayırmada pek başarılı olamazsınız. Bu ne zaman olur? Bunu ortaya çıkaracak orijinal girdiler nelerdir?

--Eğer girdi sıralanmış veya tersten sıralanmışsa..

Bunu anlamak önemli çünkü en çok yapılan işlem zaten sıralanmış olan veya kısmen sıralanmış olan listelerin sıralanmasıdır. Ama bazen bu sıralanmış olabilir ve birileri bunu kontrol etmek ister yani bunun sıralı olup olmadığına bakmak yerine yeniden yapalım diyebilirlerler.

--Bu durumda bölüntünün bir tarafında hiç eleman olmaz; bu durum için biz özyinelemenin nasıl yazılacağına bakalım.

Elinizde T(n) var; eğer bir tarafta hiç eleman yoksa buraya T(0) diyebiliriz; bu durumda öteki tarafta da T(n-1) olur. Şimdi özyinelemeyi buna göre yazıyoruz: bir tarafta hiç eleman yok, diğer tarafta (n-1) eleman var. Bu ana kadar bütün bölüntüler ve kayıtlar Teta(n) düzeyinde;

---öyleyse T(0) nedir? Asimptotik olarak T(0) nedir? Evet, Bu bir sabittir yani 1 düzeyindedir.

Yani Θ(1) + T(n-1) + Θ(n) düzeyinde; aslında Θ(1),  Θ(n) düzeyi içinde yok edilebilir ve biz T(n)= T(n-1) + Θ(n) diyebiliriz. Pekiyi, bu neye eşittir? Evet, bu Θ ()’dir. Neden Θ()’dir? Evet, çünkü bu bir aritmetik seridir. Aslında biz araya yerleştirme sıralamasında bu yaklaşımı görmüştük, aynı araya yerleştirme sırasında olduğu gibi bu bir aritmetik seri.

 

Bütün bu işleri yaptık, elimizde çabuk sıralama diye bir algoritma var ve görüyoruz ki bu araya yerleştirme sıralamasından daha hızlı değil. Ancak ben buna iyi bir algoritma demiştim. Buna iyi bir algoritma dememin sebebi bunun ortalama durum zamanının yani average case time‘ın çok iyi olmasıdır; şimdi bunu göreceğiz.

 

Ama şimdi durumu daha iyi anlamaya çalışalım ki, ortalama durumda ne olacağı ile en kötü durumda ne olacağı arasındaki farkı kavrayabilelim.

 

--Bunun için bir özyineleme ağacı çizelim ve bu T(n)=T(0)+T(n-1)+cn sabiti ile yazalım; bu şekilde neler olacağını sezmeye çalışalım. Yani bir sabit çarpı n..  

Buraya T(n)’i ve sabiti cn olarak yazalım; ondan sonra buraya T(0)’ı koyalım ve buraya da T(n-1)’i yerleştirelim.

 

Şimdi ben biliyorum, hepiniz çok hızlısınız ve hemen tam ağacı görmek isteyeceksiniz ama size bir tavsiyem var; birkaç dakikayı bu ağacı yazmaya ayırın. Bu ağaç üstel olarak büyüdüğünden küçük durumları yazmak size belli bir yazma ek zamanı çıkaracak ama, geliştirmeye çalıştığınız şekli kavradığınızdan tam emin olmalısınız.

 

Onun için buraya bir ara adım daha yazacağım.  Burada T(0) var, bu şimdi c(n-1) oluyor ; bundan sonra bir T(0)’ımız daha var ve yanına T(n-2) geliyor. Buna nokta nokta noktalarla devam ediyoruz. Bunların hepsi en üstte cn’e eşit ve bir düzey altında T(0) ve c(n-1) var; burada T(0) ve c(n-2) var; yine  T(0) ve aşağıya doğru noktalar devam ediyor; sonunda Θ(1) elde edilene kadar.

 

Bu ağacın yüksekliği ne acaba? Buradaki bu ağacın yüksekliği nedir? ---Evet,n, güzel. Çünkü her adımda buradaki bağımsız değişkeni 1 azaltıyoruz. Bu nedenle yükseklik n’dir. Bunu çözümlemek için öncelikle buradaki her şeyi toplayalım. Buradakilerin nereden geldiğini anlamaya çalışırsak, bu Teta içinde, k 1’den n’e kadar ck’ların toplamıdır; Burada olan değerler budur. Ve bu da Teta(n²) ‘dir.

Bizim aritmatik serimiz işte buradan geliyor; evet, bu Teta(n²). Ve burada olan diğer şeylerin hepsi Θ(1).  Bunlardan kaç tane var? Burada n sayısı kadar Θ(1) var.

 

---Böylece toplam miktar, yer kalmadı, yukarıdaki kısmı kullanayım tahtada,  T(n) = Θ(n) + Θ(n²), yani sonuçta T(n), Θ(n²)’e eşit.  Bu yineleme ağacının yapısına bakacak olursak çok dengesiz bir özyineleme ağacı olduğunu görürüz.

 

Şimdi size hiçbir zaman yapmamanız gerektiğini söylediğim bir şey yapacağım. Yapacağım şey de en iyi durum analizi. Bunu sadece önsezi için yapacağız. Biliyorsunuz genelde biz en iyi durum analizleri yapmayız. Aslında bu yapacağımız işlemin önseziyi geliştirmekten başka hiçbir anlamı yok.

 

--Ayrıca temelde matematiksel olarak hiçbir anlamı da yok, çünkü bize hiçbir garanti sağlamıyor. Onun için en iyi durum analizi sadece önseziyi geliştirmek için..

---Eğer gerçekten şanslıysak, nasıl bir bölüntü elde ederiz? Yani şanslı durum hangisi? Evet, tam ortadan ikiye bölünebilmesi. Yani aslında bölüntülerde n/2 ve n/2 durumunu elde etmek. Aslında gerçek değerler (n)/2 ve (n-1)/2 ama bu işi sadece önsezi için yaptığımızdan bu tip detaylar konusunda fazla kafa yormuyoruz.

 

Çünkü bunu. en iyi durumun önsezisi için yapıyoruz ve  en iyi durum analizi bizim istediğimiz bir şey değil. Eğer bu gerçekleşirse nasıl bir özyineleme elde ederiz? Her seferinde tam ortadan 2 ye bölünebildiğini hayal edin, o zaman ne olur?

Evet o zaman T(n) = 2 tane T(n/2) + Θ(n) düzeyinde bir şey olur; Θ(n) bölüntüler ve ek işleri ifade eder. Pekiyi, bu özyinelemenin çözümü nedir? Evet, Bu özyinelemenin çözümü nlogn’dir; yani birleştirerek sıralama özyinelemesinin aynısıdır.

 

Bu master teoremin yani ana teoremin hangi durumudur? Doğru, ikinci durumu. Çünkü n’in 2’nin 2 bazında logaritmasını alırsak bu n’in birinci kuvveti demektir. Ve bununla aynı şeydir, böylece biz ekstra logn’i buna eklemiş oluruz. Ana teoremin 2. durumu. Bu iyi bir şey. Yani bu demek ki en iyi durumda çabuk birleştirme iyi sonuç verecek gibi görünüyor.

 

--Bu bölüntü işleminin, her zaman 1/10 . 9/10 biçiminde bölüntülendiğini varsaysak.. yani 1/10 kere n ile  9/10 kere n. Bu durumda biz şanslı mı yoksa şanssız mıyız? Yani söylemek istediğim şey bölme işlemi bu kadar oransızsa aslında şanssız olacağız, değil mi?

 

Çünkü o zaman diyelim ki durum 1 den n ye kadar olacak ve tam ortadan bölünebiliyorsa bu nlog’dir. Peki 1/10 e 9/10 durumunda ne elde edebileceğimizi düşünüyorsunuz? Şanslı mıyız, şanssız mı? Burada biraz demokrasiye başvuracağım. Kimler bunun şanslı bir durum olduğunu söylüyor? Yani koşma süresinin hızlı olacağını düşünüyor.  Pekiyi, kimler bunun şanssız bir  durum olacağını söylüyor? Tamam biraz cesaret gerekiyor. Kaç kişi oy kullanmadı? Haydi, oy kullanın. Tamam kararınızı verin. Burada evet veya hayır demek, doğru ya da yanlış olmak her zaman iyidir, çünkü o zaman cevaba bir duygusal bağlılığınız olur ve ileride bunu daha iyi hatırlarsınız. Onun için orada oturup sessiz kalmayın. Siz duygularınızı her şeyi daha iyi hatırlamak için kontrol edemezsiniz. Onun için bu konuda fikrini belirten ya da oylamaya katılanlar katılmayanlara göre doğru da olsalar yanlış da olsalar her zaman daha kazanırlar

... Haydi şimdi duruma bakalım; yineleme burada.. T(n)= T(1/10 n) + T( 9/10 n) + Θ (n) ve sonra da bunu çözümlemek üzere Θ (n) bölümünün bir cn ‘e küçük eşit olduğunu varsayacağız. Bunun için bir özyineleme ağacı yapacağız ve bakacağız.

 

--Özyineleme ağacı burada. Elimizde T(n) = cn var; sonra T(1/10 n)  ve T(9/10n). Tepede yine cn’yi görüyoruz; karmaşıklaşıyor değil mi?

 

Bu cn’in 1/10’u olur. Şimdi şu tarafta 1/10’numuz var; bunu özyinelememizin içine uyguladığımızda burada T(1/100n) elde ediyoruz. Burada da T(9/100n) elde ediyoruz. Burada elimizde 9/10 cn var. Bu bize T(9/100 n)’i  tekrar veriyor. Ve burada da  T(81/100 n) elde ediyoruz, ve bu şekilde devam ediyoruz.

 

Şuraya sıkıştırmaya çalışayım. Önce cn var; altta 1/10 cn var. Bu yolla bir aşağıya indiğimizde 1/100 cn elde ediyoruz ve bu işlem aslında en altta Teta(1) elde edene kadar devam ediyor. Sağda 9/10 cn var. Bir altında 9/100 cn olacak ve bu da şimdi 9/100 cn olacak. Ve bu da 81/100 cn olacak ve bu bütün bu işlemler aşağıda Teta(1) elde edilene kadar sürdürülecek.

 

Ama buradaki yaprakların yapısı tekdüzey değil, değil mi? Bu taraftaki işlemler diğerinden çok daha uzun sürüyor. Çünkü burada her seferinde 9/10 aşağıya iniyoruz; o zaman buradaki yolun uzunluğu ne olur?  Bu yolun uzunluğu..

 

Yani soldaki yolu ele alacak olursam aşağıya kadar yolun uzunluğu ne olur? Orada birisi el kaldırdı, evet?  Doğru, 10 tabanına gore log(n).  Çünkü her seferinde 10 faktörüyle bunu kesiyorum ve bunu en aşağıda 1’e kadar indirgemek ne kadar sürer diye sorduğumda, bu aslında 10 tabanlı logaritmanın tanımı oluyor. Pekiyi bu nedir? Bu yol ne kadar sürecek? n’in logaritması; daha doğrusu   n; çünkü her seferinde 9/10 oranında aşağıya doğru gidiyoruz.

 

Aslında bu n’nin tanımı. Yani bu arada her şey, 10 tabanında log(n) ki haliyle 10/9 tabanı log(n) arasında. Yani bu aradaki her şey bu değerlerde. Şimdi burada birleştirerek sıralamada yaptığımız numarayı yapacağız, ve bunun değerlendirmesini düzeylerin yukarıdan başlayarak maliyetlerini topladıktan sonra ele alacağız.

--Bu sadece cn dir. Peki bundan sonraki düzeyin maliyeti nedir? O da cn dir. Bundan sonrakinin maliyeti nedir?. cn dir. Yani her seviyede aslında biz aynı miktarda iş yapıyoruz ve bu şekilde bütün hat boyunca aşağıya doğru iniyoruz. Sona doğru, artık bu değer cn ye eşit olmuyor; cn’e küçük eşit olmaya başlıyor. Çünkü bu düzeylerden itibaren bazı yapraklar hesaptan düşmüş oluyor.

 

Temelde bu yolda belli bir düzeye kadar 10 tabanlı log(n) oluyor; ondan sonra da cn’den daha küçük ya da ona eşit değerler elde ediyoruz. Ve sonunda bütün bu değerleri topluyoruz.

 

Yani T(n), cn’nin bir şeyle çarpımına eşit ya da küçük. Pekiyi, bu şey nedir? Buradaki en uzun yol ne olacaktır? Bu n nin 10/9 tabanında log(n)’i. Artı bunlara ilaveten eklememiz gereken yapraklar var. Ama bütün bu yaprakların toplamı da aslında n düzeyinde; böylece n düzeyinde olan tüm yapraklar + Θ(n)’ i elde ediyoruz. Bu durumda bu ne eder?

 

--Yani bütün bunları birbiri ile toplayacak olursam bu asimptotik olarak nedir? Bu n log n dir. Yani T(n) aslında n log n ile sınırlıdır. Şanslıyız. Şanslı olduğumuzu tahmin edenler haklı çıktı. Yani 1/10 a 9/10 bölmesi aslında asimptotik olarak %50 ye %50 bölmesi kadar iyi bir sonuç veriyor. Bunu bir alt sınır olarak bile tanımlayabiliriz; buradaki şeylere sadece bakarak T(n)’nin aslında cn çarpı n’in 10 tabanında log(n) gibi bir değerle sınırlandığını görüyoruz. Yani bu durumda T(n)  asimptotik olarak n log(n) tarafından da alttan sınırlanıyor. Böylece T(n) aslında Θ(n log n)’e dönüşüyor.

 

Şimdi bu gerçek bir kanıtlama değil. Ben gerçek bir kanıtlama için bu tip şeyleri yapmamanızı öneririm. Bu, yineleme ağaçlarının yapılarının önsezisinde yararlı olabilir. Pekiyi, bunu nasıl kanıtlayacağız? Yerine koyma metoduyla. Doğru. Yani yapmanız gereken şey bu yöntemi tahmininiz için kullanmak ondan sonra da tahmininizin doğru olduğunu kanıtlamak için de yerine koyma metodunu kullanmak. Çünkü bu ilk metodla hata yapma ihtimaliniz oldukça yüksek. Hata yapma olasılığı çok yüksek. Yerine koyma metodunda ise hata yapmanız daha zordur; çünkü orada uğraştığınız şey cebirin kendisidir. Cebirle kanıtlama, bu yöntemde yapabileceğiniz nokta nokta hatalarından, yanlış çizilen ağaçlardan ve yanlış girilen değerlerinizin daha kolaydır. Tamam mı? O halde bu n log n’ dir  ve bu çok iyi bir şey. Düzeyin n log n olması güzel ve biz şanslıyız.

 

Şimdi başka bir şey deneyelim. Bunu yine önsezi için yapacağız. Size şimdiden söyleyeyim, bu dersin sonunda hepiniz kapıya doğru koşacaksınız. Çünkü bugün iyi matematik uygulamaları da yapacağız. Bu aslında bana göre eğlenceli bir  matematik, ama sizi aynı zamanda sizi zorlayacak. Şu anda uyanık değilseniz  uyumaya devam edebilirsiniz ama size ne zaman uyanmanız gerektiğini ben söyleyeceğim. Bir önsezi uygulaması daha yapalım; diyelim ki adımların sırasını değiştirdik.  

 

Öncelikle bölüntü işlemini yaptığımızı varsayalım. Bu işlemde şöyle bir durum olduğunu düşünelim: Şanslı bir seçimle başladık. Sonra bir bölüntü adımı olsun ve bunda şanssız olalım. Ondan sonraki adımda yine şanslı olalım. Sonra yine şanssız olalım ve ağaç boyunca aşağıya kadar şanslı şanssız, bu şekilde  sürsün.

 

--Yani alternatif olarak bir şanslı bir şanssız gittiğimizi düşünelim. Bunun sonucunda şanslı mıyız, şanssız mıyız? Bu sefer herkesin oy vermesini istiyorum; vereceğiniz yanıtın doğruluğu veya yanlışlığı önemli değil. Bu oyunda herkes rol alacak. Bunu bir at yarışı gibi düşünün. Hiç at yarışı seyrettiniz mi? Yarış sıkıcıdır ama buna birazcık para yatırırsanız yani ganyan gibi bir şey oynarsanız, at yarışı ilginç hale gelir. Burada da aynı şey olacak. Bu oyunda herkesin bir şeyler katmasını istiyorum. Kaçınız bu işte şanslı olacağınızı düşünüyorsunuz? Kaçınız bu işte şanssız olacağınızı düşünüyorsunuz?. Tamam, oy vermeyenler kimler? Sizler, oyuna katılmadınız ha. Şimdi bunu çözümlemeye başlayalım ve bir özyineleme yapalım.

 

Şanslı adımımızda n boyutunda bir işlemin koşma süresi L(n) olsun. Ve bu 2 defa olacak. Ondan sonraki adımın şanssız olduğunu düşünelim. Yani bu  2U(n/2) +Teta(n). Bu bizim şanslı adımımız. Şanssız adım yani U(n) = L(n-1) zira ondan sonraki adım şanslı adım olacak artı Θ(n). Bu şanssız adım. Bu sistemin davranışını özyinelemelerle nasıl anlattığımı görüyor musunuz? Burada sınır koşullarına bağlılık kesin belirlenmiş değil, ama sabit girdilerde özyinelemenin sabit bir çözümü var. Şimdi yerine koymayı yöntemini kullanarak biraz cebir yapacağız.

 

L(n) neye eşit bir bakalım. Buraya ben U(n/2) yerine buradaki değerini yerleştirebilirim. Bu bana L(n) = 2 ( L(n/2 -1) + Θ(n))+ Θ(n) yi verecektir. Burada ne yaptığımı görüyor musunuz?  U(n/2) ler için şu yinelemeyi  yerine koydum. Aslında teknik olarak söylemem gereken Θ(n/2) ydi. Bunu daha açık anlatabilmek için Θ(n/2) kullanmam gerekirdi. Aslında bu aynı şeydir ama adım atlamamak için böyle yapıyorum. Şimdi bunu işlem sürecince yürütebiliriz. Ve böylece 2L(n/2-1) ve şimdi burada 2 tane Θ(n/2) ve şurada da bir tane daha var; bunların hepsinin toplamı Θ(n) . Pekiyi, bu özyinelemenin çözümü nedir? nlogn, yani Θ(n log n).  Herkes bunu görebiliyor mu? Tamam. Θ(nlogn). Bu aslında temelde master teorem yani ana teorem ama birazcık şurasında oynadık; oradaki eksi 1, master teoremin çözümünde bize yardımcı olur. Ne demiştik? Bu n log n düzeyinde; yani ---şanslıyız. Eğer şanslı olmakla şanssız olmak arasında dönüşümlü olarak bir değişiklik yapıyorsak, biz sonuçta yine şanslıyız.

 

Peki genelde şanslı olmayı nasıl garanti edebiliriz? Eğer girdi sıralıysa bu şansız olacağım demektir. Af edersiniz. Evet elemanları rastgele düzenleyebilirsiniz. Yollardan birisi budur. Bundan başka yol nasıl bir yol seçebiliriz? Aslında söylediğiniz  oldukça iyi bir yol ve genelde yapılması gereken şey. Pivotu rastgele seçmek. Doğru. Aslında işin sonunda iki yöntemin de aynı verimlilikte oldukları görülür ama burada biz pivotu rastgele seçmeyi ele alacağız çünkü bu yolla çözümlememiz daha kolay olacak.  Ama algoritmanın etkinliği açısından birbirine eşittirler. Bu bize rastgele çabuk sıralama algoritması olarak adlandırılan Randomized Quicksort’u verir. Ve rastgele seçilmiş çabuk sıralama algoritması ile ilgili güzel olan şey, bunun koşma süresinin girdi sırasına bağlı olmayışıdır. Aslında girdiyi karıştırarak da girdi sıralamasından bağımsız hale getirebilirim; eğer girdiyi istediğim gibi karıştırırsam girdinin sırasının ne olduğunun önemi kalmaz. Öte yandan orijinal çabuk sıralamanın bazı yavaş durumları vardı; anımsarsanız girdinin sıralanmış olması veya girdinin tersten sıralanmış olması işi yavaşlatıyordu. Ama bazı hızlı durumları da vardı. Sonuçta görülür ki eğer girdi rastgele sıralanmışsa, o zaman çabuk sıralama hızlı bir performans gösterir.

 

Eğer girdiyi rastgele karıştırırsam veya rastgele bir elemanı pivot olarak seçersem, girdinin ne olduğunun bir önemi kalmaz. Bunu hayal etmenin yollarından biri, bir rakibiniz olduğunu düşünmektir. Şöyle bir durum düşünün. Bir rakibiniz var. Siz diyorsunuz ki “benim sıralama algoritmam iyidir” ve o da “benim de iyi bir sıralama algoritmam var” diyor ve aynı müşteriye siz bunu satmaya çalışıyorsunuz. Bunun üzerine müşteri de diyor ki “tamam arkadaşlar o zaman ikiniz de diğerinin algoritması konusunda benchmark’ lar oluşturun.  Bu şekilde onun algoritmasını görüyorsunuz. Rakibinizin algoritmasına baktığınız zaman, çabuk sıralama kullandığını anlıyorsunuz ve “ben onu nasıl yenebilirim?” diyorsunuz. “Bu durumda zaten sıralanmış olan bir girdi veririm” diyorsunuz; ona yapabileceğiniz şey budur. Eğer sizinki de çabuk sıralamaysa, onun da size uygulayacağı benchmark testi budur. Bu durumda onu nasıl yenebilirsiniz Randomization yani rastgelelikle. Buradaki anafikir, eğer sıralamada rastgele permütasyon  yaparsam veya rastgele yerlerde pivot seçersem, bu durumda girdi sırasının öneminin kalmaması önemlidir. Bu durumda benim kodumu yavaşlatmak için rakibimin yapabileceği herhangi bir kötü sıralama yoktur.

 

Bu durumda şanssız olabilirim ama şanssızlığım benim rastgele sayı yaratıcısından kaynaklanır; bu da girdi ile ilgili bir şanssızlık değildir. Girdinin ne olduğunun hiç önemi olmaz. Bunu herkes takip edebiliyor mu? Yani rastgele çabuk sıralamanın güzel yanı girdi dağılımı ile ilgili herhangi bir varsayıma dayanmamasıdır. Bu durumda siz bütün girdilerin aynı olasılıkta olduğunu varsaymak zorunda değilsiniz; çünkü dilerseniz bunu böyle yaparsınız veya öyle bir pivot seçersiniz ki, bütün içerisinde etkin sonucu alırsınız. Böylece hiçbir seçilmiş girdi en kötü davranış durumunu yaratmaz. En kötü durum sadece rastgele sayı yaratıcısı aracılığı ile  belirlenir. Ve en kötü durum sadece rastgele sayı yaratıcısı ile belirlendiği için aslında biz bu şanssızlığı matematik olarak sınırlayabiliriz. Şunu sorabiliriz; ihtimaller nedir? Şimdi biz bunu çözümleyeceğiz ve bu noktada bu derse ait olup olmadığınızı anlayacaksınız.

 

Eğer daha önce Bilgisayar için Matematik (6.042) dersini herhangi bir nedenle almadıysanız bu uygulama karşılaştırmayı yapmak için uygun bir ortam olacak. Şimdi bu işlem biraz zamanımızı alacak. Onun için hepiniz neden bir süre ayağa kalkıp şöyle bir gerinme molası vermiyorsunuz? Biz aslında güzel bir matematik uygulaması yapacağız. Ve buna başlamadan önce sizin kendinizi iyi ve zinde hissetmeniz gerekiyor.

 

Gerinme molası bitti.

 

Çözümleme. Sanıyorum bunu başaracağız; aslında kendimi yarışıyor gibi hissediyorum. Bugün kapsamamız gereken epey konu var. Şimdi, T(n) nin, koşma süresinin  rastgele bir değişkeni olduğunu varsayalım.

Aa, iyi de ne yaptığımı buraya yazmamışım bile. Şimdi rastgele bir elemanı pivot olarak seçeceğiz. Yapacağımız ana işlem bu. Bunu yapma yolu da, bölüntüde çalışırken, 1. bölüntüyü oluşturmadan önce, dizilim içindeki ilk elemanı başka bir elemanla, belki de kendisiyle, değiştirmek olacak; bu durumda her ikisi de pivot olmak için eşit şansa sahip olacaklar. Ondan sonra da normal bölüntüyü işleteceğim.

 

T(n) değişkeninin zaman ekseninde çalıştığını, ancak olasılık çalışmalarından  kaynaklanan varsayımın da Rastgele sayıların bağımsız olduğu durumunu kabul edeceğiz. Bu şekilde bu algoritma çalışırken herhangi bir noktada pivot seçimi başka bir noktadaki seçimden bağımsız olmasını sağlar hale gelir. Ondan sonra bunu çözümlemek için nerede pivot seçtiğimi bilmek isterim.

 

k= 0,1 den ,… (n-1) e kadarken, belli bir bölüntüde değişken    1’e eşit olsun, Eğer k : n-k-1 bölüntüsü oluşturuyorsa; o zaman da bu değer 0’a eşit  olsun. Yani, bölüntü yordamında pivot olması için rastgele bir eleman seçiyorum. Burada  benim rastgele değişkenim oluyor. Sol tarafta k sayıda eleman sağ tarafta da (n-k-1) eleman oluşturuyorsa bu 1 sonucunu veriyor. Bunların toplamı doğal olarak n-1; çünkü elimde pivotum da var. Diğer bütün durumlarda  0 değerini alıyor. Şimdi elimde bir bölüntüde n sayıda rastgele değişkenim var; bunların hepsi 0 olacak, sadece biri, hangisi bilmiyorum, 1 değerini alacak.

 

Bu rastgele değişkenin adı ne acaba?

 

Bernoulli. Bernoulli’in başka varsayımları var; bu göstergesel rastgele değişken’ dir. Bunun adına Bernoulli denebilir ama bu bir göstergesel rasgele değişkendir ve değer olarak 0 ve 1 değerlerinden birini alır ve Bernoulli rasgele değişkenleri de belli bir tür göstergesel rasgele değişkendir. Burada biz bunlara göstergesel rasgele değişken diyeceğiz. Bir şeylerin toplamının ne olduğunu anlamaya çalıştığınızda göstergesel rasgele değişkenler iyi bir yöntemdir. Bu büyük boyuttaki rasgele değişkenleri küçük parçalara ayırarak bunların çözümlenmesini sağlar.

Buradaki göstergesel rasgele değişkene bir bakalım:

 

-- beklenen değeri yani E[]’nın neye eşit olacağını umuyorsunuz acaba? Başka bir şekilde sorarsam bir (k : n-k-1) bölüntüyü üretmemin olasılığı nedir? Belleklerinizi tazelemek için  nın ne olduğunu ve bunun ne anlama geldiğini yazalım; ---bu  nın 0 a eşit olma ihtimalinin 0 ile çarpımı artı  nın 1 e eşit olma ihtimalinin 1 ile çarpımına eşittir; buradakiler zaten 0 eder. Bu durumda elimizde eşitlik olarak  nın 1 e eşit olma ihtimali kalır.  Ve göstergesel rasgele değişkenlerin genel özelliği bunların beklenen olasılığının 1 olmasıdır. Yani burada güzel olan şey aslında beklenen olasılığın başka terimlerin varlığına bağlı olmadan göstergesel rasgele değişkenler ile ilintisidir; öyleyse  nın 1’e eşit olma olasılığı nedir? -1/n; bu durumda bütün bölünmelerin ihtimali aynıdır, yani elimde n elemanı varsa bunların her birinin pivot olarak seçilme olasılığı 1/n dir. Ve bir kez pivot seçtiğinizde de bunun solunda ve sağında kalanlar da belirlenmiş olur. Bu olasılık 1/n’dir; şu ana kadar herkes benimle birlikte mi? Aşağı yukarı,.. tamam. Yani daha önce söylediğim gibi şu andaki kavrayışınız benimle birlikte sınıfta olup olmadığınızın bir göstergesi olacak. Eve gittikten sonra bu söylediklerimi çalışırsanız ve matematik geçmişinizden dolayı konuyu  anlayamazsanız, bu size dersin boyunuzu aştığını gösterebilir.

 

Bunun, T(n)’ nin neye eşit olduğunu buraya yazalım: T(n) = T(0) + T(n-1)+ Θ(n); eğer bölüntü 0’dan n-1’e kadarsa., aksi takdirde T(1) +T(n-2) + Θ(n)’dir; eğer bölüntünüz 1 : n-2 kadarsa. Ve aşağıda bir yerde T(n-1) +T(0) +Θ(n) haline gelecektir, eğer n-1 : 0 bölüntünüz varsa. Burada T(n)’le ilgili özyinelemelerimiz bu olacak, maalesef bu özyinelemenin zor tarafı elimizde n kadar durum olmasıdır. İşte bu noktada göstergesel rasgele değişkenlerin kullanımıyla ilgili parlak fikir ortaya çıkar. Çünkü bu durum analizini ele alıp bunu matematiksel olarak indirgeyebiliriz; göstergesel rasgele değişkenleri kullanan durumların olmadığını görürüz.

 

---Bunu yapmanın yolu da değişik durumları bir toplam haline getirecek bir teknik kullanmaktır. Şu iki toplam değerlerin neden aynı olduğuna bir bakalım: Göstergesel rasgele değişkeni, belirlenen bölüntünün olmaması durumunda 0 olur; bu nedenle, knın daha önce belirtiğimiz değer olması durumu haricindeki durumlarda bunun toplamı 0 olacaktır. Şimdi 0 ile 1’in çarpımlarının nasıl bir sonuç çıkaracağını görebiliyor musunuz?  Bence bu çok akıllıca, evet, çok akıllıca; ve bu göstergesel rasgele değişkenler ile yapılan klasik bir uygulamadır. Sonuçta çok güçlü bir metot oluşturur, çünkü her ne kadar zor görünse de şimdi özyinelememiz için bir matematiksel modelimiz var.

 

Şimdi T(n)’in beklenen değerini çözümlemeye çalışalım; yapmak istediğimiz şey bu.  T(n)’in beklenen değeri nedir?

---Bunu yapmak içinde ben T(n)’in beklenen değerinin bu büyük toplamın beklenen değerine eşit olduğunu yazıyorum. Bu noktadan sonra artık biz şu toplamanın beklenen değerini hesaplamaya başlayabiliriz; herkes benimle mi? Bu aşamada sorular var mı?

 

Başparmakları yukarıya doğru görüyorum; bunu görmek hoş ama bana göre aslında görmek istediğim başparmakların aşağıya dönük olmaması.. Baş parmakların yukarıya doğru olmasını görmek güzel, fakat bu birinizin anladığını veya anladığını sandığını gösterir.

 

Yani benim söylediğim, bunun şu aşağıdakine eşit olduğu; burada biraz daha fazla yere ihtiyacım olacak, şu eşit işaretini bu nedenle biraz kaydıracağım. Ben bu toplamanın şuna eşit olduğunu savunuyorum; bu beklenen değer bu beklenen değerlerin toplamına eşittir. Acaba neden? Bu adımı haklı çıkaracak sihirli sözcükler nedir? Linearity of Expectation, yani beklenenın doğrusallığı.. Bir toplamanın beklenen değeri beklenenlerın toplamına eşittir ve buna beklenenin doğrusallığı denir. Bunun için bağımsızlığa ihtiyacım yok; bu herhangi bir rasgele değişkenin beklenenı için her zaman doğrudur, beklenen değerlerin toplamı toplam beklenen değere eşittir, veya bunun tam tersi de geçerlidir. Burada biz tam tersini uyguladık. Bu durumda bu değer k 0’dan n-1’e kadar olan   beklenen değerlerinin toplamı ile [(T(k) + T(n-k-1) + Θ(n)]’nin beklenen değerinin  çarpımına eşittir. Bu neden doğru? Çünkü yaptığım şey çarpımın beklenen, çarpanların beklenenlerın çarpımına eşittir demek. Bu bağımsızlıktan kaynaklanır. Neden bağımsız? Buradaki , ki rasgele değişkendir, diğer bölüntülerin içinde bağımsızdır; yani her özyinelemeli çağrı için  vardır. Yani burada olan biten her şey şurada olan bitenden bağımsızdır. Aslında biz saklanıyoruz. Elimizde bir özyineleme olduğundan aynı iş zamanını bölüntülere ayırmıyoruz; farklı bir değerle çalışıyoruz.

 

Aslında görünen matematiğin altında bir şeyler yapıyoruz ve buna dikkat etmeniz gerekiyor; burada T(k) aslında rasgele seçimlerin bir seti halinde ve bu nedenle buradaki değerlerin şuradakilerden farklı olduğunu anlamanız gerekiyor. Bu durumda da biz beklenen değerlerin olasılıklarını çarpabiliyoruz. Herkes benimle birlikte mi?

Bu önemli bir kavram,  nın diğer rasgele seçimlerden bağımsız olması. Her şeyden önce bu güzel bir olgu, () nın beklenen değeri nedir? 1/n. Aslında bunun toplamayla da hiçbir ilişkisi yok, bunu toplamın dışına çıkarabiliriz. Çıkan sonuç, 1/n çarpı k 0’dan n-1’e, T(k)nın beklenen değerlerinin toplamı, artı 1/n çarpı k 0’dan n-1’e, T(n-k-1) beklenen değerlerinin toplamı, artı 1/n çarpı k 0’dan n-1’e, Θ(n)’lerin toplamları.. Bunu yaparken beklenen değerlerin doğrusallığından yararlandık ve burada bunu parçalara bölmek ve k’nın beklenen değerinin 1/n olduğunu çıkarmak için kullandık. Hala herkes benimle birlikte mi? Bunların hepsi kolay; zorluk burada çok fazla adım olmasından kaynaklanıyor. Bunların bazılarını eskiden görmüş olduğunuzu umuyorum,

 

Şimdi ikinci gözlemimiz aslında bu iki toplamanın gerçekte birbirine eşdeğer olduğu; bunlar aslında aynı toplamlar ama sıraları farklı.. Bu T(0), T(1), T(2), T(3),T(n-1)’e kadar çıkıyor; bu ise T(n-1), T(n-2), T(n-3)’den T(0)’ a kadar iniyor.

 

Aslında bunlar birbirine eşit; dolayısıyla elimde bunlardan iki tane var, bu durumda bu terim neye eşit olur? ---Bu neye eşit olur? Evet, neye eşit olacak? Θ(n)’e. Şimdi bunun neden böyle olduğuna bir bakalım: Θ(n)’in 0’dan n’e kadar toplamı Θ() ye eşittir ve n’e bölünür; veya burada ben  Θ(n)’i dışarı çıkardığımda, Θ(n)’in n 1’den  (n-1)’e toplamı 1’e eşittir. Yani hangi yöntemle giderseniz gidin sonunda n’ye ulaşıyorsunuz. Bunun böyle olmasının temel sebebi toplamların aynı elemanları içermesi ve bu da sadece cebir; başka bir şey değil.

 

Şimdi yapacağımız şey teknik kolaylık açısından bir uygulama; çünkü k 0 ve 1’i

 teknik kolaylık sağlamak için Θ(n)’in içine alacağız. Burada elimizde n düzeyinde bir özyineleme var; ve ben k=0 ve k=1 durumlarına baktığımda beklenen değerin ne olduğunu biliyorum. 0 ve 1 değerleri için beklenen en kötü durum maliyeti sabit çünkü ben bu problemi bir sabit boyut için çözüyorum.

 

Ve bir varsayımız daha var: Yinelemelerin çözümündeki sınır durumlarında sürenin sabit olduğunu biliyoruz. Bu nedenle temelde bu iki terimi bunun içinden çıkarabilirim ve sonuçta olacak şey Θ(n)’in içinde birkaç sabitin daha birikmesidir. Ama bu, özyinelemenin çözümünü daha kolaylaştırır ve ben bunu yaptığımda T(n)’in beklenen değeri yani ---E[T(n)] = 2/n çarpı k 2’den n-1’ e kadar T(k)’nın beklenen değeri + Θ(n) olur.

Bütün bu çalışma özyinelemeyi çıkarmak içindi; şimdi biz bunu çözmek zorundayız.  

 

Şu ana kadar yaptıklarımızı tekrar edecek olursak, işe bir özyineleme ile başladık ve bu bir durum komutu olan rasgele değişken içindi; sonra matematik kullanarak bunu bir durum komutu olmayan hale dönüştürdük, bir çarpım haline dönüştürdük ve bundan da beklenen değer için bir özyineleme elde ettik. Şimdi bu özyinelemeyi çözme işlemini deneyeceğiz. Yani bu özyinelemeyi anlamak için bazı basitleştirmeler yaptık ve şimdi bunu burada çözeceğiz. Bu arada ben quizlerde ve sınavlarda böyle şeyler sormuyorum; Öte yandan sizin bunu anlamanızı bekliyorum. Herhangi bir test ya da quiz’de buradaki elemanları bulabilirsiniz; bunun hesaplanması yoğun bir iş yükü ve akıllı insanların emeğini gerektirir. Bunun basit olduğunu söylemiştim ama basit bile olsa böyle bir şeyi hesaplamak konuda bilgi sahibi olan insanların çokça emeğini gerektirir.

 

Şimdi şuradaki özyinelemeyi çözeceğiz ve ---T(n)’in beklenen değerinin, 0’dan büyük  bir a sabiti için, a  çarpı (nlogn)’den küçük eşit olduğunu kanıtlayacağız. Şimdi bunu kanıtlamak için hangi tekniğin kullanılmasının uygun olduğunu düşüyorsunuz? Master metodu ya da ana metod gibi mi görünüyor? Master metoda benzemiyor. Böyle durumlarda şüpheniz varsa, sunstitution yani yerine koyma metodunu kullanın. Bu bütün diğer metotların büyük babasıdır.

 

Yapacağımız şey, taban durumunda yeterince büyük bir a değeri ve yeterince küçük bir n değeri seçerek, a(n lg n)’nin  T(n)’nin beklenen değerinden daha büyük olmasını sağlamaktır. Daha önce özyinelemeden 0 ve 1’i çıkarmak istememin temelinde de bu yatıyordu. Çünkü mesela n sıfır olduğunda log 0, 0’a bölmek gibidir ve bu nedenle bu işlemi yapamazsınız. log1 = 0 dır; onun için burada bunu 1 ile sınırlasaydım 0 elde edecektim ve o zaman a’yı bu durumların üzerinde hakim olacak yeterince büyük bir değer olarak seçemeyecektim. Bu nedenle diyorum ki bunlar beni çok ilgilendirmiyor, teknik kolaylık açısından bu maliyetleri Θ(n) nin içine yedirebilirim ve bu sayede ben a(n log n) i yeterince büyük kabul edip taban durumu çözmede kullanabilirim; bu nedenle bir teknik varsayımda bulunduk.

 

--Burada klogk’ nın k 2’den n-1’e kadar olan değişimlerinin toplamı, bu da 1/2logn - 1/8 ‘den küçük eşit olması. Bunu size bir egzersiz olarak bırakıyorum. Bu egzersizin kitapta da olduğunu sanıyorum. Sizin bunu değerlendirmenizi istiyorum. Bunu hesaplamanın iki yolu vardır. Bunlardan birisi sadece toplama işlemi kullanmak ve bunu yaparken toplamı iki parçaya bölmek, toplamaların özelliklerinden yararlanmak ve yeniden yapılandırarak bu sınırlamayı kanıtlamak., İkinci yol ise toplamaların çözümünde entegral metodunu kullanmaktır. Bunu her iki yolla da kanıtlayabilirsiniz; aslında entegral metodunu kullandığınızda burada elde edeceğimizden daha sıkı bir sınır elde edersiniz. Bu temel bir bilgidir ve bunu yapmayı bilmeniz lazım.

 

Haydi şimdi yerine koyma metodunu uygulayalım.

 

---T(n)’nin beklenen değeri küçük eşit, 2/n çarpı k 2’den n-1 ‘e giderken , ak log k’ nın toplamından küçük ya da eşittir;  bu noktada  ak log k’ nın yerine koyma işleminin  daha küçük değerler için yapacağız ve buna Θ(n)’i ekleyeceğiz. Burada zor olan şeyi anlatmam gerek; bu terimin sınırı olan (1/2 logn) i kolay elde edebiliriz. İkinci derecedeki terimi elde etmek daha zor. Ama sonuç olarak yapacağımız şeyi gerçekleştirebilmek için ikinci derecedeki terime de ihtiyaç var. Bu kanıtlamayı gerçekleştirmek için (log n)’den denklemin karesel kısmını çıkaracak durumda olmamız lazım. Ve bu toplamayı değerlendirmenin şaşırtıcı yönü de bu. Elde edilen bu ara sonucun aslında şu formülü kullanarak başka bir anlatımını biliyorum; bunu uygulayıp bu terimi 2a/n çarpı (1/2 log n - 1/8  + Θ n)’ den küçük eşit olarak elde edebilirim. Bir hata mı yaptım? log n Evet, şimdi oldu. Bu neye eşit? Birinci kısımda çarpma yaparsam, şurası (an log n) oluyor. Bunu istediğim gibi sonuçlandırmak için şimdi dikkatli olup hata yapmamalıyım. İstediğimi şu parça olarak yazıyorum; bir de eksi kalanımın olması lazım.  Kalanımı buraya yazacağım parantez içerisinde, ve bu eksi olacak. Kalan buradaki terimle aynı olacağından sonuçta da an/4 olacak ve eksi Θ(n).

 

Bu da eğer bu bölüm pozitif ise küçük eşit a çarpı n çarpı logn olacak;  .. Buranın artı olmasını sağlamak için yeterince büyük bir a değeri seçmeliyim ve an/4, Θ(n) terimini domine etsin. Buradaki değer ne olursa olsun öyle bir a değeri seçebilirim ki bu buradaki terimi pozitif yapar. Yani an/4 ün Θ(n) den daha büyük olması için a’ nın yeterince büyük olması gerekli.

 

Böylece rasgele çabuk sıralamanın koşma zamanı n logn düzeyinde olur. Evet şu anda kanıtladığımız şey beklenen koşma zamanının nlogn düzeyinde olduğudur. Pratikte çabuk sıralama çok iyi bir algoritmadır. Aslında birleştirerek sıralamadan üç veya daha fazla kat daha hızlıdır. Size sonuç olarak birleştirerek sıralamadaki kadar kuvvetli bir garanti vermez ama en kötü durumu nlogn dir. Öte yandan rasgele sıralamayı seçerseniz uygulamada size üç kere daha hızlı bir koşma zamanı verir. Ama bu kadar hızlı olması için kodunda biraz ince ayar yapmanız gerekebilir. Yani taban durumlarını biraz daha kabalaştırıp başka düzenlemeler de yapmanız gerekir, ama iyi sıralama algoritmalarının çoğu çabuk sıralama temeli üzerine kurulmuştur. Buna iyi çalışır denilmesinin diğer bir sebebi de sanal bellekteki cache yani önbelleklerde  iyi çalışmasıdır. Burada ön bellek modelleri gibi şeyler üzerine çok fazla konuşmuyoruz ve bugünün algoritmalarında bu çok moda ama, bilmeniz gereken sanal hafızanın ön belleklerinde çabuk sıralamanın iyi sonuç verdiğidir. Bu nedenle buralarda kullanmak için iyi bir algoritmadır. Size iyi bir problem çözme saati diliyorum. Orada başka bir nlog n algoritması göreceksiniz.

 

Dersimiz bitmiştir.