Download as pdf or txt
Download as pdf or txt
You are on page 1of 47

Ανάλυση Αλγορίθμων 2
Οι αποτελεσματικότεροι αναδρομικοί αλγόριθμοι υλοποιούν την αρχή
του διαίρει και βασίλευε
• Διαιρούμε το πρόβλημα σε υποπροβλήματα που είναι μικρότερα
στιγμιότυπα του ίδιου προβλήματος
• Κυριεύουμε τα υποπροβλήματα, επιλύοντάς τα αναδρομικά
• Συνδυάζουμε τις λύσεις των υποπροβλημάτων για να συνθέσουμε μια λύση
του αρχικού προβλήματος

Όταν τα προβλήματα θα γίνουν αρκετά μικρά ώστε να μην επιλύονται


αναδρομικά, τότε η αναδρομή εξαντλείται και έχουμε αναχθεί στη στοιχειώδη
περίπτωση.

Ανάλυση Αλγορίθμων 4
Γενικός κανόνας: Η διατήρηση μιας ισορροπίας, ή και ισότητας, μεταξύ
των μεγεθών 𝑛1 , 𝑛2 , … , 𝑛𝑘 των υποπροβλημάτων Π1 , Π2 , … , Π𝑘 που
προκύπτουν από τη διάσπαση ενός προβλήματος Π μεγέθους n.

𝑇(𝑛) Π
𝑛 𝑇 𝑇′
𝑛1 , 𝑛2 Π1
Π2 Π 𝑛 = 𝑛1 + 𝑛2
Π1 Π2
𝑛
𝑛1 = 𝑛2 =
2
Ανάλυση Αλγορίθμων 5
Π 𝑘 𝑘=
2 Π1 , Π2 , … , Π𝑘
Π

Π1 , Π2 , … , Π𝑘

Π1 , Π2 , … , Π𝑘 Π

Ανάλυση Αλγορίθμων 6
• 𝑘 Π1 , Π2 , … , Π𝑘
𝑛
𝐷(𝑛) το πλήθος βημάτων της divide
𝑆(𝑛) το πλήθος βημάτων της conquer
𝐶(𝑛) το πλήθος βημάτων της combine

Τότε
𝑘

𝑇 𝑛 = 𝐷 𝑛 + ෍ 𝑆(𝑛𝑖 ) + 𝐶(𝑛)
𝑖=1
Ανάλυση Αλγορίθμων 7
Διάσπαση
Πρόβλημα μεγέθους 𝑛 προβλήματος

Υποπρόβλημα μεγέθους Υποπρόβλημα μεγέθους


𝑘 𝑛−𝑘

Ανάλυση Αλγορίθμων 8
Διάσπαση
Πρόβλημα μεγέθους 𝑛 προβλήματος

Υποπρόβλημα μεγέθους Υποπρόβλημα μεγέθους


𝑘 𝑛−𝑘

Ανάλυση Αλγορίθμων 9
Συνδυασμός
Πρόβλημα μεγέθους 𝑛 λύσεων

Υποπρόβλημα μεγέθους Υποπρόβλημα μεγέθους


𝑘 𝑛−𝑘

Ανάλυση Αλγορίθμων 10
Πρόβλημα μεγέθους 𝑛

Υποπρόβλημα μεγέθους Υποπρόβλημα μεγέθους


𝑘 𝑛−𝑘

Χρόνος Εκτέλεσης
𝑇 𝑛 = 𝑇 𝑘 + 𝑇 𝑛 − 𝑘 + 𝐷 𝑛 + 𝐶(𝑛)

Χρόνος διάσπασης Χρόνος σύνθεσης


Ανάλυση Αλγορίθμων 11
12
• τον εκθέτη 𝑛 σε 𝑛/2
• τον υπολογισμό της δύναμης αναδρομικά
• τις δύο υπολογισθείσες τιμές ώστε να
υπολογιστεί η σωστή δύναμη
int power(int base, int n) {
if (n != 0)
return (base * power(base, n - 1));
else
return 1;
}

𝒪(1), 𝛼𝜈 𝑛 = 0
𝑇 𝑛 =ቊ
𝑇 𝑛 − 1 + 𝒪(1), 𝛿𝜄𝛼𝜑𝜊𝜌𝜀𝜏𝜄𝜅𝛼

𝒪(𝑛)

Ανάλυση Αλγορίθμων 14
int power(int base, int n)
{
if (n == 0)
return 1;
else if (n%2 == 0)
return power(base, n/2)*power(base, n/2);
else
return base*power(base, n/2)*power(base, n/2);
}

𝒪(1), 𝛼𝜈 𝑛 = 0
𝑇 𝑛 =ቐ 𝑛
2𝑇 + 𝒪(1), 𝛿𝜄𝛼𝜑𝜊𝜌𝜀𝜏𝜄𝜅𝛼
2

𝒪(𝑛)

Ανάλυση Αλγορίθμων 15
int power(int base, int n)
{ 𝒪(1), 𝛼𝜈 𝑛 = 0
int temp; 𝑇 𝑛 =ቐ 𝑛
𝑇 + 𝒪(1), 𝛿𝜄𝛼𝜑𝜊𝜌𝜀𝜏𝜄𝜅𝛼
if(n == 0) 2
return 1;
temp = power(base, n/2); 𝒪(lg 𝑛)
if (n%2 == 0)
return temp*temp;
else
return base*temp*temp;
}

Ανάλυση Αλγορίθμων 16
17
την ακολουθία n στοιχείων σε δύο υπακολουθίες 𝒏/𝟐
στοιχείων
τις δύο υπακολουθίες αναδρομικά
τις δύο ταξινομημένες υπακολουθίες
ώστε να σχηματιστεί η τελική ταξινομημένη ακολουθία

• Η αναδρομή τερματίζει όταν η ταξινομητέα ακολουθία έχει μήκος 1.


9 4 5 1 0 7 3 2

9 4 5 1 0 7 3 2

9 4 5 1 0 7 3 2

9 4 5 1 0 7 3 2

4 9 1 5 0 7 3 2

1 4 5 9 0 2 3 7

0 1 2 3 4 5 7 9
Ανάλυση Αλγορίθμων 19
else {
// Συγχώνευση δυο υποπινάκων του arr[]: arr[k] = R[j];
// arr[l..m] και arr[m+1..r] j++;
void merge(int arr[], int l, int m, int r) }
{ k++;
int i, j, k; }
int n1 = m - l + 1;
int n2 = r - m; // Αντιγραφή των στοιχείων του L[] που περίσσεψαν
while (i < n1) {
/* Βοηθητικοί πίνακες */ arr[k] = L[i];
int L[n1], R[n2]; i++;
k++;
/* Αντιγραφή τιμών στους L[] και R[] */ }
for (i = 0; i < n1; i++)
L[i] = arr[l + i]; // Αντιγραφή των στοιχείων του R[] που περίσσεψαν
for (j = 0; j < n2; j++) while (j < n2) {
R[j] = arr[m + 1 + j]; arr[k] = R[j];
j++;
/* Συγχώνευση των L και M στον[l..r]*/ k++;
i = 0; // Δείκτης για τον πίνακα L }
j = 0; // Δείκτης για τον πίνακα R }
k = l; // Δείκτης για τον πίνακα arr
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++; Πολυπλοκότητα
}
Ανάλυση Αλγορίθμων 20
else {
// Συγχώνευση δυο υποπινάκων του arr[]: arr[k] = R[j];
// arr[l..m] και arr[m+1..r] j++;
void merge(int arr[], int l, int m, int r) }
{ k++;
int i, j, k; }
int n1 = m - l + 1;
int n2 = r - m; // Αντιγραφή των στοιχείων του L[] που περίσσεψαν
while (i < n1) { n1 επαναλήψεις
/* Βοηθητικοί πίνακες */ arr[k] = L[i];
int L[n1], R[n2]; i++;
k++;
/* Αντιγραφή τιμών στους L[] και R[] */ }
for (i = 0; i < n1; i++)
L[i] = arr[l + i]; // Αντιγραφή των στοιχείων του R[] που περίσσεψαν
for (j = 0; j < n2; j++) while (j < n2) { n2 επαναλήψεις
R[j] = arr[m + 1 + j]; arr[k] = R[j];
j++;
/* Συγχώνευση των L και M στον[l..r]*/ k++;
i = 0; // Δείκτης για τον πίνακα L }
j = 0; // Δείκτης για τον πίνακα R }
k = l; // Δείκτης για τον πίνακα arr
while (i < n1 && j < n2) { n1 ή n2 επαναλήψεις
if (L[i] <= R[j]) {
arr[k] = L[i];
1 σύγκριση
i++;
𝓞 𝒏𝟏 + 𝒏𝟐 = 𝓞(𝒏)
}
Ανάλυση Αλγορίθμων 21
/* l είναι ο αριστερός δείκτης και r ο δεξιός δείκτης του υποπίνακα του arr */
void mergeSort(int arr[], int l, int r)
{
if (l < r) {
int m = (r + l) / 2;

// Ταξινόμησε αναδρομικά τον πρώτο και δεύτερο υποπίνακα


mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);

merge(arr, l, m, r);
}
}

Πολυπλοκότητα

Ανάλυση Αλγορίθμων 22
/* l είναι ο αριστερός δείκτης και r ο δεξιός δείκτης του υποπίνακα του arr */
void mergeSort(int arr[], int l, int r)
{
if (l < r) {
int m = (r + l) / 2;

// Ταξινόμησε αναδρομικά τον πρώτο και δεύτερο υποπίνακα


mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);

merge(arr, l, m, r);
}
}
1, 𝛼𝜈 𝑛 = 1
𝑇 𝑛 =ቐ 𝑛
2𝑇 + 𝑛, 𝛿𝜄𝛼𝜑𝜊𝜌𝜀𝜏𝜄𝜅𝛼
2

𝒪(𝑛lg𝑛)

Ανάλυση Αλγορίθμων 23
• 𝒪(𝑛lg𝑛)

• 𝒪(𝑛)

Ανάλυση Αλγορίθμων 24
25
1. Διαίρει (divide): Διάλεξε ένα στοιχείο pivot (άξονα) του
πίνακα. Με βάση αυτό, χώρισε στα δύο τον πίνακα:
στον αριστερό υποπίνακα βάλε όλα τα μικρότερα
στοιχεία και στο δεξιό όλα τα μεγαλύτερα στοιχεία από
το pivot. S

2. Κατάκτησε (conquer): Αναδρομικά κάνε το ίδιο και


στους δύο υποπίνακες.
3. Συνδύασε (combine): Βάλε στη σειρά τον αριστερό
υποπίνακα, το pivot και τον δεξιό υποπίνακα. S1 S2
void quickSort(int a[], int left, int right)
{
int j;
if (left < right) {
// divide and conquer
j = partition(a, left, right);
quickSort(a, left, j-1);
quickSort(a, j+1, right);
}
}

Ανάλυση Αλγορίθμων 27
int partition(int a[], int left, int right)
{
int pivot, i, j, t;
pivot = a[left];
i = left; j = right+1;
while (1){
do ++i;
while (a[i]<=pivot && i<j);
do --j;
while (a[j] > pivot);
if(i >= j) break;
t=a[i]; a[i]=a[j]; a[j]=t;
}
t=a[left]; a[left]=a[j]; a[j]=t;
return j;
}

Ανάλυση Αλγορίθμων 28
23 42 10 100 8 4 12 56 57 50

int partition(int a[], int left, int right)


{
pivot i j
int pivot, i, j, t;
pivot = a[left]; 23 42 10 100 8 4 12 56 57 50
i = left; j = right+1;
while (1){
pivot i j
do ++i;
while (a[i]<=pivot && i<j);
23 12 10 100 8 4 42 56 57 50
do --j;
while (a[j] > pivot);
if(i >= j) break; pivot i j
t=a[i]; a[i]=a[j]; a[j]=t;
} 23 12 10 100 8 4 42 56 57 50
t=a[left]; a[left]=a[j]; a[j]=t;
return j;
}
pivot i j
23 12 10 4 8 100 42 56 57 50

pivot i j
Ανάλυση Αλγορίθμων 29
23 12 10 4 8 100 42 56 57 50
int partition(int a[], int left, int right)
{
int pivot, i, j, t; pivot i j
pivot = a[left];
i = left; j = right+1;
23 12 10 4 8 100 42 56 57 50
while (1){
do ++i;
while (a[i]<=pivot && i<j); pivot j i
do --j;
while (a[j] > pivot);
8 12 10 4 23 100 42 56 57 50
if(i >= j) break;
t=a[i]; a[i]=a[j]; a[j]=t;
}
t=a[left]; a[left]=a[j]; a[j]=t; quickSort(a, left, j-1); quickSort(a, j+1, right);
return j;
}

Ανάλυση Αλγορίθμων 30
8 12 10 4

int partition(int a[], int left, int right) pivot i j


{
int pivot, i, j, t; 8 12 10 4
pivot = a[left];
i = left; j = right+1;
while (1){ pivot i j
do ++i;
while (a[i]<=pivot && i<j); 8 4 10 12
do --j;
while (a[j] > pivot);
pivot i j
if(i >= j) break;
t=a[i]; a[i]=a[j]; a[j]=t;
} 8 4 10 12
t=a[left]; a[left]=a[j]; a[j]=t;
return j;
pivot j i
}
4 8 10 12

Ανάλυση Αλγορίθμων 31
int partition(int a[], int left, int right)
• διαδικασία partition {
int pivot, i, j, t;
pivot pivot = a[left];
i = left; j = right+1;
while (1){
do ++i;
• 𝑛+1 while (a[i]<=pivot && i<j);
do --j;
while (a[j] > pivot);
if(i >= j) break;
t=a[i]; a[i]=a[j]; a[j]=t;
}
t=a[left]; a[left]=a[j]; a[j]=t;
return j;
}

Ανάλυση Αλγορίθμων 32

• Εάν το pivot=a[left] είναι το μικρότερο στοιχείο, τότε η λίστα θα
διαχωριστεί στο 1ο της στοιχείο (η 1η αναδρομή δεν θα εκτελεστεί)
void quickSort(int a[], int left, int right)
1 2 3 4 8 9
{
int j;
1 2 3 4 8 9 if (left < right) {
// divide and conquer
j = partition(a, left, right);
2 3 4 8 9 quickSort(a, left, j-1);
quickSort(a, j+1, right);
}
3 4 8 9
}

Ανάλυση Αλγορίθμων 33

• Εάν το pivot=a[left] είναι το μεγαλύτερο στοιχείο, τότε η λίστα θα
διαχωριστεί στο 1ο της στοιχείο (η 2η αναδρομή δεν θα εκτελεστεί)
void quickSort(int a[], int left, int right)
9 8 4 3 2 1
{
int j;
8 4 3 2 1 9 if (left < right) {
// divide and conquer
j = partition(a, left, right);
4 3 2 1 8 quickSort(a, left, j-1);
quickSort(a, j+1, right);
}
3 2 1 4 }


Ανάλυση Αλγορίθμων 34

1, 𝛼𝜈 𝑛 = 2
𝑇 𝑛 =ቊ
𝑇 𝑛 − 1 + (𝑛 + 1), 𝛿𝜄𝛼𝜑𝜊𝜌𝜀𝜏𝜄𝜅𝛼

𝑇 𝑛 =𝑇 𝑛−1 + 𝑛+1 =𝑇 𝑛−2 + 𝑛−2 + 𝑛+1 =


(𝑛−2)(𝑛−2)
⋯ = 1 + 2 + ⋯+ 𝑛 − 2 + 𝑛 + 1 = + 𝑛+1
2

• 𝓞 𝒏𝟐

Ανάλυση Αλγορίθμων 35

• Συμβαίνει όταν ως pivot επιλέγεται το στοιχείο που υποδιαιρεί
𝑛 𝑛
τη λίστα σε δύο λίστες μεγέθους (έστω η πρώτη λίστα έχει
2 2
𝑛
στοιχεία και η δεύτερη − 1 στοιχεία).
2
• Τότε 1, 𝛼𝜈 𝑛 = 2
𝑇 𝑛 =ቐ 𝑛
2𝑇 + 𝒪(𝑛), 𝛿𝜄𝛼𝜑𝜊𝜌𝜀𝜏𝜄𝜅𝛼
2
• Από κύριο θεώρημα, έχουμε 𝑎 = 2, 𝑏 = 2 και 𝑓 𝑛 = 𝑛.
Έχουμε 𝑛𝑙𝑜𝑔𝑏 𝛼 = 𝑛𝑙𝑜𝑔2 2 = 𝑛, άρα είμαστε στη 2η περίπτωση
με 𝑻 𝒏 = 𝚯(𝒏 𝐥𝐠 𝒏)
Ανάλυση Αλγορίθμων 36
38
𝓞 𝒏𝟐
𝑥𝑚𝑖𝑑

𝑑𝐿𝑚𝑖𝑛 𝑑𝑅𝑚𝑖𝑛 𝑑 = min(𝑑𝐿𝑚𝑖𝑛 , 𝑑𝑅𝑚𝑖𝑛 )

𝑥𝑚𝑖𝑑 𝑥𝑚𝑖𝑑 𝑑
𝑑𝐿𝑅𝑚𝑖𝑛

𝑥𝑚𝑖𝑑
𝑑𝐿𝑚𝑖𝑛 , 𝑑𝑅𝑚𝑖𝑛 𝑑𝐿𝑅𝑚𝑖𝑛
Ανάλυση Αλγορίθμων 40
0 0 0
3 3 3
2 2 2
1 1 1
5 8 8 8
4 4 5 4 5
6 7 7 6 7
6
9 10 12 10 12 9 10 12
11 9 11 11
13 13 13

𝑥𝑚𝑖𝑑 𝑥𝑚𝑖𝑑
arr[]={1,9,6,4,11,0,5,2,10,7,3,8,12,13}
PL[]={1,9,6,4,11,0,5} PL[]={1,9,6}
PR[]={2,10,7,3,8,12,13} PR[]={4,11,0,5}

0 0 0
2 3 2 3 2 3
1 1 1
5 8 8 5 8
4 4 5 4
6 7 6 7 6 7
10 12 10 12 10 12
9 d 11 9 dL 11 dR 9 d 11
13 13 13

𝑥𝑚𝑖𝑑 𝑥𝑚𝑖𝑑
arr[]={1,9,6}

Ανάλυση Αλγορίθμων 41
d d

0 0
2 3 3
1 2
8 1
4 5 5 8
dLRmin 7 4
6 6 7
10 12 10 12
9 d 11 9 11
d
13 13
𝑥𝑚𝑖𝑑 𝑥𝑚𝑖𝑑

Ανάλυση Αλγορίθμων 42
float nearestFunc(Point arr[], int n) {// Έστω ότι ο arr είναι ταξινομημένος ως προς x
if (n <= 3) // Εάν έχουμε μόνο 2 ή 3 σημεία χρησιμοποίησε τη μέθοδο ωμής βίας
return bruteForce(arr, n);
int mid = n/2; // Εύρεση μεσαίου σημείου Xmid
Point midPoint = arr[mid];
// Υπολογισμός του dl στην αριστερή πλευρά του μεσαίου σημείου και του dr στη δεξιά
float dl = nearestFunc(arr, mid); // Αναδρομική κλήση της nearestFunc
float dr = nearestFunc(arr + mid, n - mid); // Αναδρομική κλήση της nearestFunc
float d = min(dl, dr); // Εύρεση της μικρότερης απόστασης μεταξύ των 2 (dl, dr)
// Δημιουργία ενός πίνακα που περιέχει τα σημεία που απέχουν λιγότερο από d από τη γραμμή
// που διέρχεται από το Xmid
Point strip[n]; int j = 0;
for (int i = 0; i < n; i++)
Η stripNearest υπολογίζει την
if (abs(arr[i].x - midPoint.x) < d) { ελάχιστη απόσταση μεταξύ των
strip[j] = arr[i]; j++; στοιχείων των δύο λωρίδων
} πλάτους d. Με αλγόριθμο ωμής
return min(d, stripNearest(strip, j, d) );
βίας αυτό θα απαιτούσε 𝓞 𝒏𝟐
}
συγκρίσεις.

Ανάλυση Αλγορίθμων 43
d d
• Ταξινομούμε όλα τα στοιχεία ως προς y.
• Τα πιθανά σημεία που μπορούν να έχουν από το σημείο p
μικρότερη απόσταση από d, βρίσκονται μέσα στο
παραλληλόγραμμο. d/2
• Το κάθε τετράγωνο μπορεί να έχει το πολύ 1 σημείο (γιατί?).
• Επομένως, θα πρέπει να ελέγξουμε το πολύ 7 σημεία. p
p
• Συνεχίζουμε την ίδια διαδικασία για τα επόμενα σημεία.
Γιατί το κάθε τετράγωνο μπορεί να περιέχει το πολύ 1 σημείο?
d/2 Έστω ότι περιέχει 2 σημεία. Η μέγιστη απόσταση που μπορούν να
έχουν αυτά τα σημεία, είναι η απόσταση της διαγωνίου του
d
τετραγώνου, δηλαδή . Αυτό είναι άτοπο, διότι το κάθε τετράγωνο
d/ 2 2
βρίσκεται σε μία μόνο λωρίδα, το οποίο σημαίνει ότι η μέγιστη
d
απόσταση μεταξύ δύο σημείων είναι d και προφανώς d> .
2

Ανάλυση Αλγορίθμων 44
d d d d d d

Έστω ότι έχουμε 𝒎(≤ 𝒏)


στοιχεία στις δύο λωρίδες.
Τότε θα χρειαστούμε συνολικά
το πολύ
𝑚

… ෍ 6 = 6𝑚 = 𝓞 𝒏
𝑖=1
συγκρίσεις και
𝑚

෍ 7 = 7𝑚 = 𝓞 𝒏
𝑖=1
υπολογισμούς απόστασης

1ο Βήμα 2ο Βήμα 3ο Βήμα


7 υπολογισμοί 7 υπολογισμοί 7 υπολογισμοί
απόστασης και 6 απόστασης και 6 απόστασης και 6
συγκρίσεις συγκρίσεις συγκρίσεις

Ανάλυση Αλγορίθμων 45
float nearestFunc(Point arr[], int n) {// Έστω ότι ο arr είναι ταξινομημένος ως προς x
if (n <= 3) // Εάν έχουμε μόνο 2 ή 3 σημεία χρησιμοποίησε τη μέθοδο ωμής βίας
1 σύγκρισηreturn bruteForce(arr, n); 9 πολλαπλασιασμοί και 2 συγκρίσεις
int mid = n/2; // Εύρεση μεσαίου σημείου Xmid
Point midPoint = arr[mid];
// Υπολογισμός του dl στην αριστερή πλευρά του μεσαίου σημείου και του dr στη δεξιά
float dl = nearestFunc(arr, mid); // Αναδρομική κλήση της nearestFunc
float dr = nearestFunc(arr + mid, n - mid); // Αναδρομική κλήση της nearestFunc
float d = min(dl, dr); // Εύρεση της μικρότερης απόστασης μεταξύ των 2 (dl, dr)
// Δημιουργία ενός πίνακα που περιέχει τα σημεία που απέχουν λιγότερο από d από τη γραμμή
// που διέρχεται από το Xmid
Point strip[n]; int j = 0;
for (int i = 0; i < n; i++) Χρόνος εκτέλεσης (συγκρίσεις)
if (abs(arr[i].x - midPoint.x) < d) { 1 σύγκριση 𝑇 𝑛 =ቐ 𝑛
3, 𝛼𝜈 𝑛 = 3
strip[j] = arr[i]; j++; 2𝑇 + 𝒪(𝑛), 𝛿𝜄𝛼𝜑𝜊𝜌𝜀𝜏𝜄𝜅𝛼
} 2
Από το κύριο θεώρημα, προκύπτει εύκολα ότι η
return min(d, stripNearest(strip, j, d) );
πολυπλοκότητα είναι
} 𝓞(𝒏)+1 συγκρίσεις 𝓞(𝒏 𝐥𝐠𝒏)

Ανάλυση Αλγορίθμων 46
𝒏 𝐥𝐠 𝒏 𝒏𝟐 𝒏 𝐥𝐠𝒏
16 4 256 64
1,024 10 1,048,576 10,240
2,048 11 4,194,304 22,528
4,096 12 16,777,216 49,152
1,048,576 20 1,099,511,627,776 20,971,520
33,554,432 25 1,125,899,906,842,624 838,860,800

Ανάλυση Αλγορίθμων 47

You might also like