You are on page 1of 57

Algoritma Analizi

Algoritma
• Ayrık matematikte ortaya çıkan birçok genel problem sınıfı vardır.
Örneğin:
• Verilen bir tamsayı dizisi verildiğinde en büyüğünü bulun;
• Bir küme verildiğinde onun tüm alt kümelerini listeleyin;
• Bir dizi tamsayı verildiğinde bunları artan sıraya koyun;
• Bir ağ verildiğinde iki köşe arasındaki en kısa yolu bulun.

• Böyle bir problemle karşılaşıldığında yapılacak ilk şey, problemi


matematiksel bağlama çeviren bir model oluşturmaktır.
Algoritma
• Bu tür modellerde kullanılan ayrık yapılar arasında kümeler, diziler ve
fonksiyonlar ile permütasyonlar, ilişkiler, çizgeler, ağaçlar, ağlar ve sonlu
durum makineleri gibi diğer yapılar yer alır.
• Uygun matematiksel modelin kurulması çözümün yalnızca bir parçasıdır.
• Çözümü tamamlamak için genel sorunu modeli kullanarak çözecek bir
yönteme ihtiyaç vardır.
• İdeal durumda gerekli olan, istenen cevaba götüren bir dizi adımı takip
eden bir prosedürdür. Böyle bir adım dizisine algoritma denir.
• Algoritma, bir hesaplamayı gerçekleştirmek veya bir problemi çözmek
için kullanılan kesin talimatların sonlu bir dizisidir.
Algoritma
• Algoritma terimi, dokuzuncu yüzyılda yaşamış bir matematikçi olan ve
Hindu rakamları üzerine kitabı modern ondalık gösterimin temelini
oluşturan el-Khowarizmi isminden gelmektedir.
• Algorizm kelimesi, on sekizinci yüzyılda algoritma kelimesine dönüştü.
• Bilgisayar makinelerine olan ilginin artmasıyla birlikte, algoritma
kavramına daha genel bir anlam verildi; yalnızca aritmetik yapma
prosedürlerini değil, problem çözmeye yönelik tüm belirli prosedürleri
içerecek şekilde.
Algoritma-Örnek
• Sonlu bir tam sayı dizisinde maksimum (en büyük) değeri bulmak için
bir algoritma tanımlayın.
• Bir dizideki maksimum öğeyi bulma problemi nispeten önemsiz
olmasına rağmen, algoritma kavramının iyi bir örneğini sağlar.
• Bu sorunu çözmek için bir prosedürü çeşitli şekillerde belirleyebiliriz.
Yöntemlerden biri, kullanılan adımların sırasını tanımlamak için doğal
dili kullanmaktır.
Algoritma-Örnek
Aşağıdaki adımları gerçekleştiriyoruz.
1. Geçici maksimumu dizideki ilk tam sayıya eşit olarak ayarlayın. (Geçici
maksimum, prosedürün herhangi bir aşamasında incelenen en büyük
tam sayı olacaktır.)
2. Sıradaki bir sonraki tam sayıyı geçici maksimumla karşılaştırın ve
geçici maksimumdan büyükse, geçici maksimumu bu tam sayıya
eşitleyin.
3. Sırada daha fazla tamsayı varsa önceki adımı tekrarlayın.
4. Sırada hiç tam sayı kalmadığında durun. Bu noktadaki geçici
maksimum, dizideki en büyük tamsayıdır.
Sözde kod
• Bir algoritma bir bilgisayar dili kullanılarak da tanımlanabilir. Ancak bu
yapıldığında yalnızca dilde izin verilen talimatlar kullanılabilir.
• Bu genellikle algoritmanın karmaşık ve anlaşılması zor bir
açıklamasına yol açar.
• Dolayısıyla, algoritmaları belirlemek için belirli bir bilgisayar dili
kullanmak yerine, bir tür sözde kod kullanılacaktır.
• Sözde kod, bir algoritmanın İngilizce dilindeki açıklaması ile bu
algoritmanın bir programlama dilinde uygulanması arasında bir ara
adım sağlar.
Sözde kod
• Algoritmanın adımları, programlama dillerinde kullanılanlara benzer
talimatlar kullanılarak belirtilir.
• Ancak sözde kodda kullanılan talimatlar iyi tanımlanmış herhangi bir
işlemi veya ifadeyi içerebilir.
• Bir bilgisayar programı, başlangıç noktası olarak sözde kod
tanımlaması kullanılarak herhangi bir bilgisayar dilinde üretilebilir.
Sözde kod
• Sonlu bir dizide maksimum öğeyi bulmaya yönelik algoritmanın sözde
kod açıklaması aşağıdadır.
Sözde kod
• Bu algoritma ilk önce dizinin başlangıç terimi olan a1'i max
değişkenine atar.
• Dizinin terimlerini başarılı bir şekilde incelemek için “for” döngüsü
kullanılır.
• Bir terim mevcut max değerinden büyükse, max'ın yeni değeri olarak
atanır. Algoritma tüm terimler incelendikten sonra sonlandırılır.
Sonlandırmadaki max değeri dizideki maksimum öğedir.
Sözde kod
• Bir algoritmanın nasıl çalıştığına dair fikir edinmek için, belirli bir girdi
verildiğinde onun adımlarını gösteren bir iz oluşturmak faydalıdır.
• Örneğin, 8, 4, 11, 3, 10 girişiyle Algoritma 1'in izi, algoritmanın
maksimum değerinin başlangıç teriminin değeri olan 8'e
ayarlanmasıyla başlar.
• Daha sonra ikinci terim olan 4'ü, maksimumun geçerli değeri olan 8 ile
karşılaştırır. 4 ≤ 8 olduğundan maksimum değişmez.
• Daha sonra algoritma üçüncü terim olan 11'i mevcut maksimum değer
olan 8 ile karşılaştırır. 8 < 11 olduğundan, maksimum 11'e eşitlenir.
Algoritmaların Özellikleri
• Algoritmaların genel olarak paylaştığı çeşitli özellikler vardır. Algoritmalar
açıklanırken bunların akılda tutulması faydalıdır. Bu özellikler şunlardır:
• Giriş. Bir algoritmanın belirli bir kümeden giriş değerleri vardır.
• Örneğimizde giriş, bir tamsayı dizisidir.
• Çıkış. Bir algoritma, her girdi değeri kümesinden, belirli bir kümeden çıktı
değerleri üretir. Çıkış değerleri problemin çözümüdür.
• Örneğimizde çıktı, dizideki en büyük tam sayıdır.
• Kesinlik. Bir algoritmanın adımları kesin olarak tanımlanmalıdır.
• Algoritmanın her adımı kesin olarak tanımlanır çünkü yalnızca atamalar, sonlu bir döngü ve
koşullu ifadeler oluşur
• Doğruluk. Bir algoritma, her girdi değeri kümesi için doğru çıktı değerlerini
üretmelidir.
• Algoritmanın doğru olduğunu göstermek için, algoritma sona erdiğinde max değişkeninin
değerinin dizideki terimlerin maksimumuna eşit olduğunu göstermemiz gerekir.
Algoritmaların Özellikleri
• Sonluluk. Bir algoritma, kümedeki herhangi bir girdi için sonlu (ama belki de
çok) sayıda adımdan sonra istenen çıktıyı üretmelidir.
• Algoritma sonlu sayıda adım kullanır çünkü dizideki tüm tamsayılar incelendikten sonra
sona erer.
• Etkililik. Bir algoritmanın her adımını tam olarak ve sınırlı bir sürede
gerçekleştirmek mümkün olmalıdır.
• Algoritma sınırlı bir sürede gerçekleştirilebilir çünkü her adım ya bir karşılaştırma ya da
atamadır, bu adımlardan sonlu sayıda vardır ve bu iki işlemin her biri sınırlı bir zaman alır.
• Genellik. Prosedür yalnızca belirli bir girdi değerleri kümesi için değil, istenen
biçimdeki tüm problemler için geçerli olmalıdır.
• Algoritma geneldir çünkü herhangi bir sonlu tamsayı dizisinin maksimumunu bulmak için
kullanılabilir.
Arama Algoritmaları
• Sıralı bir listede bir öğenin yerini bulma sorunu birçok bağlamda
ortaya çıkar.
• Örneğin, kelimelerin yazılışını kontrol eden bir program, onları yalnızca
sıralı bir kelime listesi olan bir sözlükte arar. Bu tür problemlere arama
problemleri denir.
• Genel arama problemi şu şekilde açıklanabilir: a 1, a2, …, an gibi farklı
öğelerden oluşan bir listede bir x öğesini bulun veya listede olmadığını
belirleyin.
• Bu arama probleminin çözümü, terimin listede x'e eşit olan (yani x = ai
ise çözüm i'dir) konumu ve x listede değilse 0'dır.
Doğrusal Arama
• Doğrusal arama algoritması x ve a1'i karşılaştırarak başlar. Bir eşleşme bulunana
kadar x'i listedeki her terimle art arda karşılaştırarak bu işleme devam edin.
Listenin tamamı x'i bulmadan arandıysa çözüm 0'dır.
• Doğrusal arama algoritmasının sözde kodu:
İkili Arama
• Bu algoritma, listede artan boyut sırasına göre oluşan terimler
bulunduğunda kullanılabilir (örneğin: terimler sayı ise, en küçükten en
büyüğe doğru listelenir; eğer kelime ise, sözlükbilimsel veya alfabetik
sıraya göre listelenir).
• Bulunacak elemanı listenin orta terimiyle karşılaştırarak ilerler.
Bulunacak öğe ile orta terimin karşılaştırılmasına dayalı olarak arama
uygun alt listeyle sınırlandırılarak arama devam eder.
Çalışma zamanı- ikili arama
Çalışma zamanı- ikili arama
Çalışma zamanı- ikili arama
Çalışma zamanı- ikili arama (logn)
Sıralama
• Bilgi işlem kaynaklarının inanılmaz derecede büyük bir yüzdesi şu veya
bu şeyi sıralamaya ayrılmıştır. Bu nedenle sıralama algoritmalarının
geliştirilmesine çok çaba sarf edilmiştir.
• Şaşırtıcı derecede çok sayıda sıralama algoritması farklı stratejiler
kullanılarak geliştirildi ve düzenli olarak yenileri eklendi.
• 100'den fazla sıralama algoritması geliştirildi ve yeni sıralama
algoritmalarının bu kadar sıklıkla geliştirilmesi şaşırtıcı.
Kabarcık Sıralaması (Bubble Sort)
• Kabarcık sıralaması en basit sıralama algoritmalarından biridir ancak
en verimlilerinden biri değildir.
• Bitişik öğeleri art arda karşılaştırarak, yanlış sıradaysa bunları
değiştirerek bir listeyi artan sıraya koyar.
• Kabarcık sıralamasını gerçekleştirmek için, listenin başından
başlayarak daha büyük bir öğeyi onu takip eden daha küçük bir öğeyle
değiştiririz.
Kabarcık Sıralaması (Bubble Sort)
Kabarcık Sıralaması (Bubble Sort)
Seçim Sıralaması (Selection Sort)
Ekleme Sıralaması (Insertion Sort)
• N öğeli bir listeyi sıralamak için ekleme sıralaması ikinci öğeyle başlar.
Ekleme sıralaması, bu ikinci öğeyi birinci öğeyle karşılaştırır ve ilk öğeyi
aşmıyorsa ilk öğenin önüne, birinci öğeyi aşıyorsa ilk öğenin sonrasına
ekler.
Ekleme Sıralaması (Insertion Sort)
Algoritmaların Karmaşıklığı
• Bir algoritma ne zaman bir soruna tatmin edici bir çözüm sağlar?
Öncelikle her zaman doğru cevabı vermelidir. İkincisi, etkili ve verimli
olmalıdır.
• Bir algoritmanın verimliliği nasıl analiz edilebilir?
• Verimliliğin bir ölçüsü, giriş değerleri belirli bir boyutta olduğunda,
bilgisayarın algoritmayı kullanarak bir sorunu çözmek için kullandığı
zamandır.
• İkinci bir ölçü, giriş değerleri belirli bir boyutta olduğunda algoritmayı
uygulamak için gereken bilgisayar belleği miktarıdır.
Algoritmaların Karmaşıklığı
• Belirli büyüklükteki bir problemi çözmek için gereken zamanın analizi,
algoritmanın zaman karmaşıklığını içerir. Gerekli bilgisayar belleğinin
analizi, algoritmanın alan karmaşıklığını içerir.
• Bir algoritmanın bir mikrosaniyede mi, bir dakikada mı yoksa bir
milyar yılda mı cevap üreteceğini bilmek önemlidir.
• Benzer şekilde, bir sorunu çözmek için gerekli belleğin mevcut olması
gerekir, böylece alan karmaşıklığı dikkate alınmalıdır.
Zaman Karmaşıklığı
• Bir algoritmanın zaman karmaşıklığı, girdi belirli bir boyuta sahip
olduğunda algoritma tarafından kullanılan işlem sayısı cinsinden ifade
edilebilir.
• Zaman karmaşıklığını ölçmek için kullanılan işlemler, tam sayıların
karşılaştırılması, tam sayıların toplanması ve diğer herhangi bir temel
işlem olabilir.
• Zaman karmaşıklığı, farklı bilgisayarların temel işlemleri
gerçekleştirmesi için gereken zaman farkından dolayı gerçek bilgisayar
zamanı yerine gereken işlem sayısı cinsinden tanımlanır.
Fonksiyonların Büyümesi
• Bir sorunu çözmek için gereken süre, yalnızca kullandığı işlem
sayısından daha fazlasına bağlıdır.
• Süre aynı zamanda algoritmayı uygulayan programı çalıştırmak için
kullanılan donanım ve yazılıma da bağlıdır.
• Bununla birlikte, bir algoritmayı uygulamak için kullanılan donanım ve
yazılımı değiştirdiğimizde, n boyutunda bir problemi çözmek için
gereken süreyi, önceki süreyi bir sabitle çarparak yaklaşık olarak
tahmin edebiliriz.
Asimptotik Notasyon
Asimptotik Notasyon
Big-O notasyonu
Big-O notasyonu
• Big-O notasyonu, bir algoritmanın girdisi büyüdükçe kullandığı işlem
sayısını tahmin etmek için yaygın olarak kullanılır.
• Büyük-O gösterimini kullanarak, bir algoritmada kullanılan farklı
işlemlerin aynı zaman aldığını varsayabiliriz, bu da analizi oldukça
basitleştirir.
• BigO gösterimini kullanarak, bir algoritmayı uygulamak için kullanılan
donanım ve yazılım hakkında endişelenmemize gerek olmadığı
anlamına gelir.
Big-O notasyonu
• Bu gösterimin yardımıyla, girdi boyutu arttıkça bir sorunu çözmek için
belirli bir algoritma kullanmanın pratik olup olmadığını belirleyebiliriz.
• Ayrıca, büyük-O gösterimini kullanarak, girdi boyutu büyüdükçe
hangisinin daha verimli olduğunu belirlemek için iki algoritmayı
karşılaştırabiliriz.
• Örneğin, bir sorunu çözmek için biri 100n2 + 17n + 4 işlemlerini
kullanan ve diğeri n3 işlemlerini kullanan iki algoritmamız varsa,
büyük-O gösterimi, ilk algoritmanın n büyük olduğunda çok daha az
işlem kullandığını görmemize yardımcı olabilir, hatta ancak n = 10 gibi
küçük n değerleri için daha fazla işlem kullanır.
Big-O notasyonu-Örnek 1
Big-O notasyonu-Örnek 2
Algoritmik Paradigmalar
• Çeşitli algoritmik paradigmalara dayalı olarak birçok farklı problemi
çözmeye yönelik algoritmalar oluşturulabilir.
• Bu paradigmalar, çok çeşitli problemleri çözmek için etkili algoritmalar
oluşturmanın temelini oluşturabilir.
• Daha önce incelediğimiz algoritmalardan bazıları, bu bölümde
anlatacağımız kaba kuvvet olarak bilinen algoritmik bir paradigmaya
dayanmaktadır.
• Diğer algoritmik paradigmalar arasında böl ve yönet algoritmaları, aç
gözlü algoritmalar, dinamik programlama yer almaktadır.
Kaba Kuvvet(Brute Force) Algoritmaları
• Kaba kuvvet önemli ve temel bir algoritmik paradigmadır.
• Bir kaba kuvvet algoritmasında, bir problem, problemin ifadesine ve
terimlerin tanımlarına dayanarak en basit şekilde çözülür.
• Kaba kuvvet algoritmaları, gerekli bilgi işlem kaynaklarına
bakılmaksızın sorunları çözmek için tasarlanmıştır.
• Örneğin, bazı kaba kuvvet algoritmalarında bir problemin çözümü,
mümkün olan her çözümün incelenmesi ve mümkün olan en iyinin
aranması yoluyla bulunur.
Kaba Kuvvet Algoritmaları
• Kaba kuvvet algoritmaları çoğu zaman verimsiz olmasına rağmen
oldukça faydalıdır.
• Bir kaba kuvvet algoritması, özellikle girdi çok büyük olmadığında, bu
algoritmayı daha büyük girdiler için kullanmak pratik olmasa bile
pratik problem örneklerini çözebilir.
• Ayrıca, bir sorunu çözmek için yeni algoritmalar tasarlarken amaç
genellikle kaba kuvvet algoritmasından daha verimli yeni bir algoritma
bulmaktır.
Kaba Kuvvet Algoritmaları
• Bir dizideki maksimum sayıyı bulmaya yönelik algoritmanın bir kaba
kuvvet algoritması olduğuna dikkat edin, çünkü maksimum terimi
bulmak için bir dizideki n sayıdan her birini inceler.
• Kabarcık ve ekleme sıralamaları aynı zamanda kaba kuvvet
algoritmaları olarak kabul edilir; bu sıralama algoritmaları birleştirme
sıralama ve hızlı sıralama gibi diğer sıralama algoritmalarından çok
daha az verimli olan basit yaklaşımlardır.
Kaba Kuvvet Algoritmaları
• Örneğin, düzlemdeki n nokta kümesindeki en yakın nokta çiftini bulmak için bir
kaba kuvvet algoritması oluşturun.
• Bir kaba kuvvet algoritması, n noktanın tüm çiftleri arasındaki mesafeleri
hesaplayıp en küçük mesafeyi belirleyerek bu noktaların en yakın çiftini bulabilir.
Açgözlü Algoritmalar
• Göreceğimiz algoritmaların çoğu optimizasyon problemlerini çözmek için
tasarlanmıştır.
• Bu tür problemlerin amacı, verilen probleme bazı parametrelerin değerini
en aza indirecek veya en üst düzeye çıkaracak bir çözüm bulmaktır.
• En basit yaklaşımlardan biri sıklıkla bir optimizasyon probleminin
çözümüne yol açar.
• Bu yaklaşım, optimal bir çözüme yol açabilecek tüm adım dizilerini
dikkate almak yerine, her adımda en iyi seçeneği seçer.
• Her adımda "en iyi" gibi görünen seçimi yapan algoritmalara açgözlü
algoritmalar denir.
Açgözlü Algoritmalar
• Örnek olarak, bozuk para kullanarak para bozan kasiyer algoritmasını
ele alalım.
• 25 sentler, on sentler, 5 sentler ve 1 sentler ile n sentlik para üstü
verme ve toplamda en az sayıda madeni para kullanma problemini
düşünün.
• Her adımda yerel olarak optimal bir seçim yaparak n sent karşılığında
para üstü vermek için açgözlü bir algoritma tasarlayabiliriz; yani, her
adımda n senti aşmadan bozuk para yığınına ekleyebileceğimiz
mümkün olan en büyük değerdeki madeni parayı seçiyoruz.
Açgözlü Algoritmalar
• Örneğin 67 sente para üstü yapmak için önce bir 25 sent seçiyoruz
(geriye 42 sent kalıyor).
• Daha sonra ikinci 25 senti (17 sent bırakarak) seçiyoruz, ardından 10
sent (7 sent bırakarak), ardından bir 5 sent (2 sent bırakarak),
ardından bir sent (1 sent bırakarak) ve ardından bir sent seçiyoruz.
• 2 tane 25 sent, 1 tane 10 sent, 1 tane 5 sent, 2 tane 1 sent
• Toplam 6 madeni para kullanılıyor
Huffman Kodlama
• Bir dizideki simgelerin frekanslarını (geçme olasılıkları olan) girdi
olarak alan ve çıktı olarak, bu simgeler için mümkün olan tüm ikili
önek kodları arasında mümkün olan en az biti kullanarak diziyi
kodlayan bir önek kodu üreten bir algoritma tanıtıyoruz.
• Bu algoritmanın, her bir sembolün dizede kaç kez geçtiğini zaten
bildiğimizi varsaydığını, dolayısıyla her sembolün sıklığını, bu
sembolün kaç kez tekrarlandığını dizenin uzunluğuna bölerek
hesaplayabiliriz.
Huffman Kodlama
• Huffman kodlaması, bir veri sıkıştırmada temel algoritma, konuyu
bilgiyi temsil etmek için gereken bit sayısını azaltmaya adamıştır.
• Huffman kodlaması, metni temsil eden bit dizilerini sıkıştırmak için
yaygın olarak kullanılır ve aynı zamanda ses ve görüntü dosyalarının
sıkıştırılmasında da önemli bir rol oynar.
• Huffman kodlamasının açgözlü bir algoritma olduğunu unutmayın. Her
adımda iki alt ağacın en küçük ağırlıkla değiştirilmesi, bu sembollere
yönelik hiçbir ikili önek kodunun bu sembolleri daha az bit kullanarak
kodlayamayacağı anlamında en uygun koda yol açar.
Huffman Kodlama
• Aşağıdaki sembolleri listelenen frekanslarla kodlamak için Huffman
kodlamasını kullanın: A: 0,08, B:0,10, C: 0,12, D: 0,15, E: 0,20, F: 0,35.
Bir karakteri kodlamak için kullanılan ortalama bit sayısı nedir?
Huffman Kodlama
Huffman Kodlama
Huffman Kodlama
• Üretilen kodlama A'yı 111, B'yi 110,
C'yi 011, D'yi 010, E'yi 10 ve F'yi 00
olarak kodlar. Bu kodlamayı
kullanarak bir sembolü kodlamak için
kullanılan ortalama bit sayısı şöyledir:
• 3 ⋅ 0.08 + 3 ⋅ 0.10 + 3 ⋅ 0.12 + 3 ⋅ 0.15
+ 2 ⋅ 0.20 + 2 ⋅ 0.35 = 2.45.
Böl ve Fethet Stratejisi
• Bilgisayar biliminde çok önemli bir strateji:
1. Sorunu daha küçük parçalara bölün
2. Parçaları bağımsız olarak çözün
3. Genel bir çözüm elde etmek için bu çözümleri
birleştirin
P

P1 ......................
P2 Pn

P11 P12 ... P1n P21 P22 ... P2n Pn1 Pn2 ... Pnn

.................................................. .................................................. .........................................


..........
Cases
Base

53
P P P P P P P P P .................... P P P
Böl ve Fethet Stratejisi (devam)
/* Solve a problem P */
Solve(P){
/* Base case(s) */ /* Temel durum(lar) */
if P is a base case problem
return the solution immediately

/* Divide P into P1, P2, ..Pn each of smaller scale (n>=2)


*/
/* Solve subproblems recursively */
S1 = Solve(P1); /* Solve P1 recursively to obtain S1 */
S2 = Solve(P2); /* Solve P2 recursively to obtain S2 */

Sn = Solve(Pn); /* Solve Pn recursively to obtain Sn */

/* Merge the solutions to subproblems */


/* to get the solution to the original big problem */
S = Merge(S1, S2, …, Sn);

/* Return the solution */


return S;
} //end-Solve 54
1+2+..+N Özyinelemeli Hesaplama
• 1'den n'ye kadar olan sayıların toplamını hesaplama
problemini düşünün: 1+2+3+..+n

• Yinelemeli olarak nasıl düşünebileceğimiz aşağıda


açıklanmıştır:
• Sum(n) = 1+2+..+n'yi hesaplamak için
• Sum (n-1) = 1+2+..+n-1 (aynı türden daha küçük bir problem)’i hesapla
• Sum(n)'i hesaplamak için Sum(n-1)'e n ekleyin
• yani, Sum(n) = Sum(n-1) + n;
• Ayrıca temel durum(lar)ı da tanımlamamız gerekiyor
• Temel durum, sorunu daha fazla bölmeden kolayca çözülebilen bir alt
sorundur.
• Eğer n = 1 ise Sum(1) = 1;

55
1+2+..+N Özyinelemeli Hesaplama

/* Computes 1+2+3+…+n */ #include <stdio.h>


int Sum(int n){
int partialSum = 0; main(){
int x = 0;
/* Base case */
if (n == 1) return 1; x = Sum(4);
printf(“x: %d\n”, x);
/* Divide and conquer */
partialSum = Sum(n-1); return 0;
} /* end-main */
/* Merge */
return partialSum + n;
} /* end-Sum */

56

You might also like