Chapter 6 Dönüştür ve Yönet (Transform-and-Conquer) Copyright © 2007 Pearson Addison-Wesley. All rights reserved.
Dönüştür ve Yönet Problem üzerinde dönüştürme işlemi yapılarak çözme tekniği Problemin giriş verilerini basitleştirme (veri basitleştirmesi) Verileri farklı bir gösterimde yazma(gösterim değiştirlmesi) Problemi başka bir probleme dönüştürme (problem dönüştürme)
Veri basitleştirmesi - Önsıralama Problemi çözmeden önce giriş verilerini sıralama Ön sıralama Bir çok problem giriş verileri sıralandığında daha kolay probleme dönüşürler. Arama problemi Median hesaplama (seçme problemi) Elemanların birbirinden farklı olduğunu kontrol etme
Ne kadar hızlı sıralama yapabiliriz? Ön sıralama yapılacaksa algoritmamızın verimliliği sıralama hızına bağlı olur. n boyutlu bir listeyi karşılaştırarak n log2 n işlem zamanında sıralayabiliriz. (en az) (nedenini gelecekte tartışırız) Örneğin, mergesort.
Ön sıralamalı arama Problem: K sayısnı A[0..n-1] dizisinde ara Önsıralamalı algoritma: Adım 1 Diziyi en verimli sıralama algortiması ile sırala Adım 2 İkili arama algoritması uygula Verimlilik: Θ(nlog n) + O(log n) = Θ(nlog n)
Her elemanın farklılığı problemi Ön sıralama tabanlı algoritma Adım1 1: En verimli sıralama algoritması ile sırala (örneğin, mergesort) Adım 2: Ardışık ikilileri kontrol et Verim: Θ(nlog n) + O(n) = Θ(nlog n) Brute force algoritması Tüm ikilileri kontrol et Verim: O(n2)
Veri basitleştirmesi – Gauss Elemesi Verilen: n doğrusal denklem ve n bilinmeyenli denklem sistemi. Dönüştür: Sistemi bu sisteme denk olan üst üçgen sisteme dönüştür Sonra da en son denklemden başlayarak çöz. a11x1 + a12x2 + … + a1nxn = b1 a1,1x1+ a12x2 + … + a1nxn = b1 a21x1 + a22x2 + … + a2nxn = b2 a22x2 + … + a2nxn = b2 an1x1 + an2x2 + … + annxn = bn annxn = bn
Örnek: Gauss Elemesi Problem 2x1 - 4x2 + x3 = 6 3x1 - x2 + x3 = 11 x1 + x2 - x3 = -3 Gauss elemesi 2 -4 1 6 2 -4 1 6 3 -1 1 11 satır2 – (3/2)*satır1 0 5 -1/2 2 1 1 -1 -3 satır3 – (1/2)*satır1 0 3 -3/2 -6 satır3–(3/5)*satır2 2 -4 1 6 0 5 -1/2 2 0 0 -6/5 -36/5 Geri yerine koyma (Backward substitution) x3 = (-36/5) / (-6/5) = 6 x2 = (2+(1/2)*6) / 5 = 1 x1 = (6 – 6 + 4*1)/2 = 2
Algoritma ve verimlilik Adım 1: Üst üçgen matrise dönüştürme for i ← 1 to n-1 do for j ← i+1 to n do for k ← i to n do A[j, k] ← A[j, k] - A[i, k] * A[j, i] / A[i, i] Adım 2: Geri yerine koyma for j ← n downto 1 do t ← 0 for k ← j +1 to n do t ← t + A[j, k] * x[k] x[j] ← (A[j, n+1] - t) / A[j, j] Verimlilik: Θ(n3) + Θ(n2) = Θ(n3)
Polinom hesabı için Horner kuralı n dereceli p(x) = anxn + an-1xn-1 + … + a1x + a0 polinomunun verilen x için p değerini bulunuz. 2 tane brute force algoritması: p 0 p a0; power 1 for i n downto 0 do for i 1 to n do power 1 power power * x for j 1 to i do p p + ai * power power power * x return p p p + ai * power return p
Horner Kuralı Örnek : p(x) = 2x4 - x3 + 3x2 + x - 5 = = x(2x3 - x2 + 3x + 1) - 5 = = x(x(2x2 - x + 3) + 1) - 5 = = x(x(x(2x - 1) + 3) + 1) - 5 Son formül hızlı hesaplama imkanı veriyor. katsayılar 2 -1 3 1 -5 x=3
Horner Kuralı Horner kuralı verimliliği: # çarpım = # toplam = n
an hesaplama Soldan sağa ikili üst gösterimi Çarpımın başlangıç değeri 1 olsun. n sayısının iki tabanında gösteriminde soldan sağa doğru aşağıdankileri yap: Rakam sıfır ise, çarpımın karesini al(S); Rakam 1 ise çarpımın karesini al ve a ile çarp. Örnek: Hesapla a13. Burada, n = 13 = 11012. 1 1 0 1 SM SM S SM Çarpım: 1 12*a=a a2*a = a3 (a3)2 = a6 (a6)2*a= a13 (soldan sağa çarpım) Verimlilik: (b-1) ≤ M(n) ≤ 2(b-1) burada b = log2 n + 1
an hesaplama Sağdan sola ikili üst hesaplama n sayısının ikili gösteriminde sağdan sola doğru 1 e karşılık gelen rakamlarda oluşan a2 i çarpımlarını çarparak an çarpımını hesapla. Örnek Hesapla a13 . Burada, n = 13 = 11012. 1 1 0 1 a8 a4 a2 a : a2 i a8 * a4 * a : (sağdan sola) Verimlilik: soldan sağa olan durumla aynı
Problem Dönüştürme Dönüştür-ve-yönet yönteminin bu türünde problem farklı bir probleme dönüştürlerek yapılıyor. Dönüştürülmüş problemin çözümü için algoritma varsa işe yarar. Doğal olarak, dönüştürme zamanı ile dönüştürülmüş problemin çözümü için gerekli işlem zamanının toplamı, ana problemin dönüştürülmeden yapılan çözümü için gerekli işlem zamanından çok olmamalıdır.
Problem dönüştürme örnekleri okek(m, n) sayısını obeb(m, n) ile yardımıyla hesaplama m*n=okek(m,n)*obeb(m,n) Çizgelerde uzunluğu n olan yolların sayısını bulmak için komşuluk matrisinin n. kuvvetini hesapla Maksimum değer problemini minimum değer problemine dönüştürme problem Doğrusal programlama Heapsort
Introduction to Algorithms Chapter 6: Heap Sort
Heap sort İşlem süresi O(nlgn) Yerinde sıralar yani ek bir diziye ihtiyaç yoktur. Heap(Yığın) veri yapısı kullanıyor 2 çeşidi var: max-heap ve min-heap. max-heap algoritmasını anlatacağız.
Heap length[A]: dizinin eleman sayısı Heap neredeyse tam ikili ağaçtır Ağaçta son seviye dışında tüm seviyeler doludur. Pratikte heapler dizi(array)olarak kullanılır A dizisi heapse 2 parametresi vardır : A[1 .. length[A]] length[A]: dizinin eleman sayısı heap-size[A]: heapin A dizisnde tutulan eleman sayısı, burada heap-size[A] ≤ length[A] A = 16 14 10 8 7 9 3 2 4 1
Heap Elemanları Ağacın kökü A[1] i node A[i] Parent(i) Left(i) return i/2 Left(i) return 2*i Right(i) return 2*i + 1 1 2 3 4 5 6 7 8 9 10 16 15 Level: 3 2 1 0
Heap fonksiyonları Aşağıdaki fonksiyonlar vardır: Heapify() , O( lg n) zamanda çalışır, verilen elemandan başlayarak max-heap özelliği yaratır BuildHeap() fonksiyonu, doğrusal zamanda çalışıyor, sırasız bir giriş dizisini max-heap özellikli bir diziye dönüştürür. HeapSort() fonksiyonu, O(n lg n) zamanda diziyi yerinde sıralar.
Heap Özelliği Heaplerin aşağıdaki heap özelliği(max-heap) vardır: A[Parent(i)] A[i] tüm i > 1 için Başka bir deyimle, her nodun değeri en fazla velisinin değerine eşittir En büyük değer (A[1])
Heap Fonksiyonları: Heapify() Heapify(): heap özelliği monte eder Verilen: heapteki i nodunun çocukları L ve R olsun Kökleri L ve R olan sol ve sağ altağaçlar heap olsun Problem: Kökü i olan altağacın heap özelliği olmayabilir A[i] kendi çocuklarından küçük olabilir Yapılacak işlem: If A[i] < A[L] or A[i] < A[R], swap A[i] with the largest of A[L] and A[R] Recurs on that subtree
Heap Fonksiyonları: Heapify() Heapify(A, i) { 1. L left(i) 2. R right(i) 3. if L heap-size[A] and A[L] > A[i] 4. then largest L 5. else largest i 6. if R heap-size[A] and A[R] > A[largest] 7. then largest R 8. if largest i 9. then exchange A[i] A[largest] 10. Heapify(A, largest) }
Heapify() Örnek 16 4 10 14 7 9 3 2 8 1 A = 16 4 10 14 7 9 3 2 8 1
Heapify() Örnek 16 4 10 14 7 9 3 2 8 1 A = 16 4 10 14 7 9 3 2 8 1
Heapify() Örnek 16 4 10 14 7 9 3 2 8 1 A = 16 4 10 14 7 9 3 2 8 1
Heapify() Örnek 16 14 10 4 7 9 3 2 8 1 A = 16 14 10 4 7 9 3 2 8 1
Heapify() Örnek 16 14 10 4 7 9 3 2 8 1 A = 16 14 10 4 7 9 3 2 8 1
Heapify() Örnek 16 14 10 4 7 9 3 2 8 1 A = 16 14 10 4 7 9 3 2 8 1
Heapify() Örnek 16 14 10 8 7 9 3 2 4 1 A = 16 14 10 8 7 9 3 2 4 1
Heapify() Örnek 16 14 10 8 7 9 3 2 4 1 A = 16 14 10 8 7 9 3 2 4 1
Heapify() Örnek 16 14 10 8 7 9 3 2 4 1 A = 16 14 10 8 7 9 3 2 4 1
Heap Yüksekliği Tanım: Ağaçta bir nodun yüksekliği = bu noddan yapraklara kadar olan en uzun yoldaki kiriş sayısı n elemanlı bir heap yüksekliği (yani heap için olan ağacın yüksekliği)= (lg n) Heap bir ikili ağaç olduğundan yükseklik=(lg n) Yani temel işlemler O(lg n) zamanda yapılır
Her seviyedeki nod sayısı n-elemanlı bir heapin k. seviyede en fazla 2h-k nodu vardır, burada h ağacın yüksekliğidir k = h (kök seviyesi) 2h-h = 20 = 1 k = h-1 2h-(h-1) = 21 = 2 k = h-2 2h-(h-2) = 22 = 4 k = h-3 2h-(h-3) = 23 = 8 … k = 1 2h-1 = 2h-1 k = 0 (yaprak seviyesi) 2h-0 = 2h
Heap yüksekliği maximum node sayısı ( h yüksekliktir) n elemanlı heap yüksekliği h = lg n = (lg n) ispat: maximum node sayısı ( h yüksekliktir) 2h + 2h-1 + … + 22 + 21 + 20 = i=0 to h 2i=(2h+1–1)/(2–1) = 2h+1 - 1 minimum node sayısı 1 + 2h-1 + … + 22 + 21 + 20 = i=0 to h-1 2i + 1 = (2h-1+1–1)/(2–1) + 1 = 2h Yani 2h n 2h+1 - 1 h lg n & lg(n+1) – 1 h lg(n+1) – 1 h lg n Yani: h = lg n = (lg n)
Heapify() analizi Heapify işlem zamanı zamaniT(n) = Θ(h) h = heap yüksekliği = lg n T(n) = Θ(lg n)
Heap fonksiyonları: BuildHeap() Heapi aşağıdan yukarıya ardışık olarak Heapify() çağırarak oluşturabiliriz n uzunluklu dizi için, A[n/2 + 1, n/2 + 2 .. n] elemanları artık heap oluştururlar (Neden?) Bu elemanlar hepsi yapraktır Yaprak seviyesinde en fazla 2h node = n/2 node olduğunu biliyoruz Diğer seviyelerde n/2 node var n/2 den 1 e doğru, Heapify() fonksiyonu her nod için çağır.
BuildHeap() BuildHeap(A) 2. for i length[A]/2 downto 1 // sırasız A dizisi veriliyor, A yı heap yapıyor BuildHeap(A) { 1. heap-size[A] length[A] 2. for i length[A]/2 downto 1 3. do Heapify(A, i) }
BuildHeap() Örnek Örnek A = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7} n=10, n/2=5 4 1 3 2 16 9 10 14 8 7 5 6
BuildHeap() Örnek A = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7} 4 1 3 2 16 9
BuildHeap() Örnek A = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7} 4 1 3 2 16 9 5 6 7 2 16 9 10 8 9 10 14 8 7
BuildHeap() Örnek A = {4, 1, 3, 14, 16, 9, 10, 2, 8, 7} 4 1 3 14 16 9 5 6 7 14 16 9 10 8 9 10 2 8 7
BuildHeap() Örnek A = {4, 1, 10, 14, 16, 9, 3, 2, 8, 7} 4 1 10 14 16 9 5 6 7 14 16 9 3 8 9 10 2 8 7
BuildHeap() Örnek A = {4, 16, 10, 14, 7, 9, 3, 2, 8, 1} 4 16 10 14 7 9 5 6 7 14 7 9 3 8 9 10 2 8 1
BuildHeap() Örnek A = {16, 14, 10, 8, 7, 9, 3, 2, 4, 1} 16 14 10 8 7 9 5 6 7 8 7 9 3 8 9 10 2 4 1
BuildHeap() analizi (Kaba hesap) Her Heapify() O(lg n) zaman alır O(n) defa çağrılıyor (tam olarak, n/2 defa) Bu durumda O(n lg n) olur
BuildHeap() analizi: (İnce hesap) Heapify fonksiyonunun yüksekliği k olan node için O(k) zamanda çalışır. BuildHeap için k=0 to h 2h-k O(k) = O(2h k=0 to h k/2k) = O(n k=0 to h k(½)k) Formül: k=0 to ∞ k xk = x/(1-x)2 burada x =1/2 Buradan, k=0 to k/2k = (1/2)/(1 - 1/2)2 = 2 Buradan da, O(n k=0 to h k/2k) = O(n)
Heapsort BuildHeap(), yerinde sıralama algoritmasıdır: Maximum eleman A[1] Bu elemanla A[n] i değiştir heap_size[A] azalt A[n] de doğru değer bulunur A[1] den Heapify()çağır A[1] i A[heap_size(A)] değiştir ve her şeyi tekrarla
Heapsort Heapsort(A) { 1. Build-Heap(A) 2. for i length[A] downto 2 3. do exchange A[1] A[i] 4. heap-size[A] heap-size[A] - 1 5. Heapify(A, 1) }
HeapSort() A = {16, 14, 10, 8, 7, 9, 3, 2, 4, 1} 1 16 2 3 14 10 4 5 6 7 8 7 9 3 8 9 10 2 4 1
HeapSort() A = {14, 8, 10, 4, 7, 9, 3, 2, 1, 16} 1 14 2 3 8 10 4 5 6 7 4 7 9 3 8 9 2 1 16 i = 10
HeapSort() A = {10, 8, 9, 4, 7, 1, 3, 2, 14, 16} 1 10 2 3 8 9 4 5 6 7 4 7 1 3 8 2 14 16 i = 9 10
HeapSort() A = {9, 8, 3, 4, 7, 1, 2, 10, 14, 16} 1 9 2 3 8 3 4 5 6 7 4 7 1 2 10 14 16 i = 8 9 10
HeapSort() A = {8, 7, 3, 4, 2, 1, 9, 10, 14, 16} 1 8 2 3 7 3 4 5 6 4 2 1 9 i = 7 10 14 16 8 9 10
HeapSort() A = {7, 4, 3, 1, 2, 8, 9, 10, 14, 16} 1 7 2 3 4 3 4 5 1 2 8 9 i = 6 7 10 14 16 8 9 10
HeapSort() A = {4, 2, 3, 1, 7, 8, 9, 10, 14, 16} 1 4 2 3 2 3 4 i = 5 1 7 8 9 6 7 10 14 16 8 9 10
HeapSort() A = {3, 2, 1, 4, 7, 8, 9, 10, 14, 16} 1 3 2 3 2 1 i = 4 4 7 8 9 5 6 7 10 14 16 8 9 10
HeapSort() A = {2, 1, 3, 4, 7, 8, 9, 10, 14, 16} 1 2 i = 3 2 1 3 4 4 7 8 9 5 6 7 10 14 16 8 9 10
HeapSort() A = {1, 2, 3, 4, 7, 8, 9, 10, 14, 16} 1 1 i =2 3 2 3 4 4 7 8 9 5 6 7 10 14 16 8 9 10
Heapsort analizi BuildHeap() çalışması O(n) zamanda olur n - 1 defa Heapify() her biri O(lg n) zaman alır HeapSort() = O(n) + (n - 1) O(lg n) = O(n) + O(n lg n) = O(n lg n)
Heapsort analizi Heapsort O(n log n) zamanda çalışır O(n2) zamanda çalışan selection ve insertion sort dan daha iyidir Merge sort zaman açısından aynı olsa da Merge Sort’ dan hafıza bakımından daha iyidir Heap sort yerinde sıralama algoritmasıdır
Heap e yeni eleman ekleme Yeni elemanı en sona ekle. Velisi ile karşılaştır heap özelliği yoksa yer değiştir Heap özelliği oluşana kadar devam e Örnek: 10 ekle Verim: O(log n)