You are on page 1of 11

ΠΟΛΥΤΕΧΝΕΙΟ ΚΡΗΤΗΣ

ΣΧΟΛΗ ΗΜΜΥ
ΕΡΓΑΣΤΗΡΙΟ ΔΙΑΝΕΜΗΜΕΝΩΝ
ΠΛΗΡΟΦΟΡΙΚΩΝ ΣΥΣΤΗΜΑΤΩΝ ΚΑΙ ΕΦΑΡΜΟΓΩΝ
http://courses.ece.tuc.gr

ΗΜΜΥ ΠΛΗ 101 - ΕΙΣΑΓΩΓΗ ΣΤΟΥΣ ΗΛΕΚΤΡΟΝΙΚΟΥΣ ΥΠΟΛΟΓΙΣΤΕΣ ΚΑΙ ΤΗΝ ΠΛΗΡΟΦΟΡΙΚΗ
5H ΕΡΓΑΣΤΗΡΙΑΚH ΑΣΚΗΣΗ: ΥΠΟΣΤΗΡΙΚΤΙΚΟ ΥΛΙΚΟ
ΥΠΕΥΘΥΝΟΙ ΕΡΓΑΣΤΗΡΙΟΥ: ΑΝΕΣΤΗΣ ΓΕΩΡΓΙΟΣ, ΜΟΥΜΟΥΤΖΗΣ ΝΕΚΤΑΡΙΟΣ, ΜΑΡΑΓΚΟΥΔΑΚΗΣ ΙΩΑΝΝΗΣ

A. Διατάξεις
Μια διάταξη είναι μία συλλογή θέσεων αποθήκευσης δεδομένων, που έχουν τον ίδιο τύπο
δεδομένων και το ίδιο όνομα. Κάθε θέση αποθήκευσης σε μία διάταξη ονομάζεται στοιχείο της
διάταξης. Η διάταξη είναι ένας εύκολος τρόπος για να οργανωθεί ένας μεγάλος αριθμός από
όμοια (δηλαδή ίδιου τύπου) στοιχεία. Μπορείτε να δηλώσετε διατάξεις με κάθε τύπο στοιχείων.
Οι διατάξεις μπορούν να είναι μονοδιάστατες ή πολυδιάστατες.

A.1. Μονοδιάστατες Διατάξεις


Μία μονοδιάστατη διάταξη έχει μόνο ένα δείκτη. Ένας δείκτης είναι ένας αριθμός σε αγκύλες,
που έπεται του ονόματος της διάταξης. Αυτός ο αριθμός μπορεί να προσδιορίζει τον αριθμό των
ξεχωριστών στοιχείων σε μία διάταξη. Για παράδειγμα, για να δηλωθεί μια διάταξη τύπου char
μπορεί να χρησιμοποιηθεί η ακόλουθη σύνταξη:
char str[100];
Η διάταξη ονομάζεται str και περιέχει 100 στοιχεία και συγκεκριμένα 100 χαρακτήρες. Καθένα
των 100 στοιχείων είναι το ακριβές ισοδύναμο μιας απλής μεταβλητής char. Όλοι οι τύποι
δεδομένων της C μπορούν να χρησιμοποιηθούν για τη δήλωση διατάξεων. Τα στοιχεία διάταξης
της C αριθμούνται πάντοτε αρχίζοντας από το 0, έτσι τα 100 στοιχεία της προαναφερθείσας
διάταξης χαρακτήρων αριθμούνται από το 0 έως το 99.
Όταν δηλώνεται μία διάταξη, ο μεταγλωττιστής δεσμεύει ένα μπλοκ μνήμης αρκετά μεγάλο για
να χωρέσει ολόκληρη τη διάταξη. Τα μεμονωμένα στοιχεία της διάταξης αποθηκεύονται σε
ξεχωριστές θέσεις μνήμης όπως δείχνει η εικόνα που ακολουθεί.
char str[100];

str[0] str[1] str[2] ... str[98] str[99]


Εικόνα 1: Τα στοιχεία της διάταξης
αποθηκεύονται σε διαδοχικές θέσεις στη μνήμη
Ένα στοιχείο διάταξης μπορεί να χρησιμοποιηθεί στο πρόγραμμά σας οπουδήποτε θα μπορούσε να
χρησιμοποιηθεί και μία μεταβλητή του ίδιου τύπου. Τα ξεχωριστά στοιχεία της διάταξης
προσπελαύνονται με χρήση του ονόματος της διάταξης, ακολουθούμενου από το δείκτη του
στοιχείου, μέσα σε αγκύλες. Για παράδειγμα, η ακόλουθη πρόταση αποθηκεύει την τιμή 981.25
στο δεύτερο στοιχείο της διάταξης (θυμηθείτε ότι πρώτο στοιχείο της διάταξης είναι το 0 και
όχι το 1):
float salaries[12];
salaries[1] = 981.25;
Ομοίως η πρόταση

1 / 11
salaries[10] = salaries[9];
εκχωρεί ένα αντίγραφο της αποθηκευμένης τιμής στο στοιχείο διάταξης salaries[9] στο στοιχείο
διάταξης salaries[10]. Όταν αναφέρεστε σε ένα στοιχείο διάταξης, ο δείκτης της διάταξης μπορεί
να είναι μία κυριολεκτική σταθερά, όπως στα προηγούμενα παραδείγματα. Ωστόσο, τα
προγράμματά σας μπορεί να χρησιμοποιούν συχνά ένα δείκτη που είναι μια ακέραια μεταβλητή ή
έκφραση της C, ή ακόμα και ένα άλλο στοιχείο διάταξης. Ακολουθούν μερικά παραδείγματα:
float salaries[100]; /* το salaries είναι διάταξη πραγματικών (float) αριθμών */
int a[10]; /* το a[] είναι διάταξη ακεραίων */
int i = 0;
salaries[i] = 100; /* ισοδύναμη με το salaries[0] = 100 */
salaries[i + 1] = 200; /* ισοδύναμη με το salaries[1] = 200 */
salaries[1 + 1] = 300; /* ισοδύναμη με το salaries[2] = 300 */
a[2] = 3;
salaries[a[2]] = 400; /* ισοδύναμη με το salaries[3] = 400 */
Όταν χρησιμοποιείτε διατάξεις, κρατήστε στο νου σας το σχήμα αποθήκευσης στοιχείων: Σε μια
διάταξη με n στοιχεία, η επιτρεπόμενη περιοχή δεικτών είναι από το 0 έως το n-1. Εάν
χρησιμοποιήσετε την τιμή δείκτη n, θα έχετε λογικό σφάλμα στο πρόγραμμά σας. Ο
μεταγλωττιστής της C δεν αναγνωρίζει αν το πρόγραμμα χρησιμοποιεί ένα δείκτη διάταξης
εκτός των επιτρεπτών ορίων. Κατά συνέπεια, το πρόγραμμά σας μεταγλωττίζεται και συνδέεται
κανονικά αλλά οι εκτός ορίων τιμές γενικά δημιουργούν απρόβλεπτα αποτελέσματα και πολλές
φορές οδηγούν στην κατάρρευση του προγράμματος.
Παράδειγμα κώδικα 1: Χρήση μιας μονοδιάστατης διάταξης
#include <stdio.h>
#define MAX_SAL 6

int main() {
/* Δήλωση διάταξης για το μισθό έξι μηνών και μιας μεταβλητής μετρητή */
float salaries[MAX_SAL];
int idx;

/* Εισαγωγή δεδομένων από το πληκτρολόγιο σε μια διάταξη */


for (idx = 0; idx < MAX_SAL; ++idx) {
printf("Enter salary for month %d:", idx + 1);
scanf("%f", &salaries[idx]);
}
/* Εκτύπωση περιεχομένων διάταξης */
for (idx = 0; idx < MAX_SAL; ++idx) {
printf("Month %d: = %.2f euro\n", idx+1, salaries[idx]);
}
return 0;
}

2 / 11
A.2. Πολυδιάστατες Διατάξεις
Μία πολυδιάστατη διάταξη έχει περισσότερους από έναν δείκτες. Μία διάσταση δύο διαστάσεων
έχει δύο δείκτες, μία τρισδιάστατη διάταξη έχει τρεις δείκτες, κοκ. Δεν υπάρχει όριο στον αριθμό
των διαστάσεων που μπορεί να έχει μία διάταξη στην C.
Για παράδειγμα, για να γράψετε ένα πρόγραμμα που παίζει σκάκι θα μπορούσατε να
αναπαραστήσετε τη σκακιέρα σαν μία δισδιάστατη διάταξη με οκτώ γραμμές και οκτώ στήλες,
ως ακολούθως:
int chessboard[8][8];
Η διάταξη που προκύπτει έχει 64 στοιχεία: chessboard[0][0], chessboard[0][1], chessboard[0]
[2], ..., chessboard[7][6], chessboard[7][7]. Η δομή αυτής της δισδιάστατης διάταξης
απεικονίζεται στην εικόνα που ακολουθεί:

...
chessboard[0][0] chessboard[0][1] chessboard[0][7]

...
chessboard[1][0] chessboard[1][1] chessboard[1][7]

...
chessboard[2][0] chessboard[2][1] chessboard[2][7]
. . .
. . .
. . .
...
chessboard[7][0] chessboard[7][1] chessboard[7][7]

Εικόνα 2: Μία δισδιάστατη διάταξη έχει δομή γραμμών και στηλών


Ομοίως, μία τρισδιάστατη διάταξη θα μπορούσε να αναπαρασταθεί ως ένας κύβος. Όλες οι
διατάξεις, χωρίς να έχει σημασία ο αριθμός των διαστάσεών τους, αποθηκεύονται στη μνήμη.
Παράδειγμα κώδικα 2: Χρήση μιας δισδιάστατης διάταξης
#include <stdio.h>

#define ROWS 2
#define COLS 2

void addArray(float a[][COLS], float b[][COLS], float c[][COLS]);


void printArray(float a[][COLS]);

int main(){
float a[ROWS][COLS], b[ROWS][COLS], c[ROWS][COLS];
int i, j;

printf("Enter the elements of 1st matrix\n");


/* Reading two dimensional array with the help of two for loop. If there is
* an array of 'n' dimension, 'n' numbers of loops are needed for inserting
* data to array.*/
for(i = 0; i < ROWS; ++i) {

3 / 11
for(j = 0; j < COLS; ++j){
printf("Enter a%d%d: ", i+1, j+1);
scanf("%f", &a[i][j]);
}
}
printf("Enter the elements of 2nd matrix\n");
for(i = 0; i < ROWS; ++i) {
for(j = 0; j < COLS; ++j){
printf("Enter b%d%d: ", i+1, j+1);
scanf("%f", &b[i][j]);
}
}
/* add arrays */
addArray(a, b, c);
printf("\nSum Of Matrix:\n");
printArray(c);

return 0;
}
void addArray(float a[][COLS], float b[][COLS], float c[][COLS]) {
int i, j;
for(i = 0; i < ROWS; ++i) {
for(j = 0; j < COLS; ++j) {
/* Writing the elements of multidimensional array using loop. */
c[i][j] = a[i][j] + b[i][j]; /* Sum of corresponding elements of two arrays. */
}
}
}
void printArray(float c[][COLS]) {
int i, j;
for(i = 0; i < ROWS; ++i) {
for(j = 0; j < COLS; ++j) {
printf("%.1f\t", c[i][j]);
}
printf("\n");
}
}

A.3. Ονομασία και Δήλωση Διατάξεων


Οι κανόνες για την ονοματοδοσία των διατάξεων είναι ίδιοι με τους κανόνες για τα ονόματα

4 / 11
μεταβλητών. Ένα όνομα διάταξης πρέπει να είναι μοναδικό, δεν μπορεί να χρησιμοποιηθεί για
άλλη διάταξη ή για οποιονδήποτε άλλο προσδιοριστή (μεταβλητή, σταθερά, κοκ.). Οι δηλώσεις
έχουν την ίδια μορφή με τις δηλώσεις των υπολοίπων μεταβλητών, εκτός από το ότι ο αριθμός
των στοιχείων στη διάταξη θα πρέπει να περιβάλλεται από αγκύλες, αμέσως μετά το όνομα της
διάταξης.
Όταν δηλώνετε μία διάταξη, μπορείτε να καθορίσετε τον αριθμό των στοιχείων της με μία
κυριολεκτική σταθερά ή με μία συμβολική σταθερά, με τη ντιρεκτίβα #define. Έτσι, η ακόλουθη
δήλωση:
#define MONTHS 12
int array[MONTHS];
είναι ισοδύναμη με τη δήλωση:
int array[12];

A.4. Απόδοση Αρχικών Τιμών σε Διατάξεις


Μπορείτε να δώσετε αρχικές τιμές σε ολόκληρη ή σε μέρος μιας διάταξης, όταν τη δηλώνετε.
Βάλτε μετά τη δήλωση της διάταξης ένα ίσον και μία λίστα τιμών, περιβεβλημένων σε άγκιστρα
και διαχωρισμένων με κόμματα. Οι παρατιθέμενες τιμές εκχωρούνται με τη σειρά στα στοιχεία
της διάταξης, αρχίζοντας με τον αριθμό 0. Για παράδειγμα ο ακόλουθος κώδικας εκχωρεί την
τιμή 10 στο array[0], 20 στο array[1], 30 στο array[2] και 40 στο array[3]:
int array[4] = {10, 20, 30, 40};
Εάν παραλείψετε το μέγεθος της διάταξης, ο μεταγλωττιστής δημιουργεί μία διάταξη με μέγεθος
ικανό να φέρεις τι αρχικές τιμές. Έτσι η ακόλουθη εντολή θα έχει ακριβώς το ίδιο αποτέλεσμα με
την προηγούμενη:
int array[] = {10, 20, 30, 40};
Ωστόσο μπορείτε να συμπεριλάβετε πολύ λίγες αρχικές τιμές, όπως στο παράδειγμα:
int array[10] = {10, 20, 30, 40};
Εάν δεν δώσετε σαφείς αρχικές τιμές σε κάποια στοιχεία διάταξης, δεν μπορείτε να είστε βέβαιοι
για την τιμή που έχουν όταν εκτελείται το πρόγραμμα.

A.4.1 Απόδοση Αρχικών Τιμών Πολυδιάστατων Διατάξεων


Και οι πολυδιάστατες διατάξεις μπορούν να δεχτούν αρχικές τιμές. Η λίστα των αρχικών τιμών
εκχωρείται στα στοιχεία της διάταξης με τη σειρά. Για παράδειγμα:
int array[3][2] = {1, 2, 3, 4, 5, 6};
καταλήγει στις ακόλουθες εκχωρήσεις:
array[0][0] ισούται με 1
array[0][1] ισούται με 2
array[1][0] ισούται με 3
array[1][1] ισούται με 4
array[2][0] ισούται με 5
array[2][1] ισούται με 6
Όταν δίνετε αρχικές τιμές σε πολυδιάστατες διατάξεις, μπορείτε να κάνετε σαφέστερο τον
πηγαίο κώδικά σας, χρησιμοποιώντας επιπλέον άγκιστρα, για να ομαδοποιήσετε τις αρχικές τιμές
και επίσης διαμοιράζοντας τις τιμές σε περισσότερες από μία γραμμές. Η ακόλουθη απόδοση

5 / 11
αρχικών τιμών είναι ισοδύναμη με την προηγούμενη:
int array[3][2] = {{1, 2}, {3, 4}, {5, 6}};

Β. Αναδρομή
Ο όρος αναδρομή αναφέρεται σε μία κατάσταση στην οποία μία συνάρτηση καλεί τον ευατό της,
είτε άμεσα, είτε έμμεσα. Η έμμεση αναδρομή λαμβάνει χώρα όταν μία συνάρτηση καλεί μία άλλη
συνάρτηση η οποία κατόπιν καλεί την πρώτη συνάρτηση. Η C επιτρέπει τις αναδρομικές
συναρτήσεις οι οποίες οποίες μπορεί να είναι χρήσιμες σε περιπτώσεις κατά τις οποίες ένα
σύνθετο πρόβλημα μπορεί να διαχωριστεί σε πιο απλά υποπροβλήματα, κατόπιν να λυθούν αυτά
τα υποπροβήματα ξεχωριστά και στη συνέχεια να συνδυαστούν οι (μερικές) λύσεις των
υποπροβλημάτων για να προκύψει η τελική λύση.
Επειδή η αναδρομή είναι στην ουσία μία επαναληπτική διαδικασία (φανταστείτε την ως ένα βρόχο
επανάληψης) θα πρέπει να εξασφαλίζουμε ότι υπάρχει πάντα εντός της αναδρομικής συνάρτησης
μία συνθήκη τερματισμού, δηλαδή εξόδου από την επαναληπτική της κλήση, διαφορετικά θα
προκύψει ατέρμων βρόχος.

Β.1. Χρήση Αναδρομής


Αρχικά εξετάζουμε πως μπορεί ένα πρόβλημα να “σπάσει” σε απλούστερα υποπροβλήματα με την
ίδια όμως ακριβώς δομή. Στην αναδρομή θα πρέπει πάντα η παράμετρος που διαφοροποιεί το
πρόβλημα από το υποπρόβλημα να είναι παράμετρος/όρισμα της αναδρομικής συνάρτησης.
Παράδειγμα κώδικα 3: Εύρεση αθροίσματος στοιχειών διάταξης ακεραίων με χρήση αναδρομής
#include <stdio.h>
#define ARRAY_SIZE 5

int rSum(int a[], int lastElmIdx );


void printArray(int[], int);

int main() {

int i, array[ARRAY_SIZE];
for (i = 0; i < ARRAY_SIZE; ++i) {
printf("Give a[%d]:", i+1);
scanf("%d", &array[i]);
}
printf("\n The array is:\n");
printArray(array, ARRAY_SIZE);
printf("\nThe sum of its elements is: %d", rSum(array, ARRAY_SIZE - 1) );
}

void printArray(int a[], int size) {


int i;
for (i = 0; i < size; ++i) {

6 / 11
printf("a[%d] = %d\n", i, a[i]);
}
}

int rSum(int a[], int lastElmIdx) {


if (lastElmIdx < 0 )
return 0;
else
return rSum(a, lastElmIdx-1) + a[lastElmIdx];
}

Αναφορές
1. Εγχειρίδιο της C, Peter Aitken & Bradley L. Jones.

7 / 11
Εκφώνηση Εργαστηριακής Άσκησης
Κατασκευάστε ένα πρόγραμμα το οποίο στη συνάρτηση main() θα δηλώνεται μια διάταξη δέκα
(10) ακεραίων και στη συνέχεια θα διαβάζει από το πληκτρολόγιο και θα εισάγει στη διάταξη
τους ακεραίους. Αφού ολοκληρωθεί η εισαγωγή των ακεραίων εκτυπώστε τα στοιχεία της
διάταξης. Στη συνέχεια γράψτε μία ΑΝΑΔΡΟΜΙΚΗ συνάρτηση που να ελέγχει εάν σε μία διάταξη
δέκα (10) ακεραίων υπάρχει τετράδα συνεχόμενων στοιχείων με ακεραίους Α, Β, Γ και Δ έτσι
ώστε να ισχύει
Α+Δ=Β+Γ
Για παράδειγμα, η διάταξη με τους ακέραιους 5 10 20 21 31 4 3 9 0 1 περιέχει την τριάδα 10 20
21 31 όπου ικανοποιείται η απαιτούμενη συνθήκη.
Η αναδρομική σας συνάρτηση να τυπώνει ένα μήνυμα για το αν η διάταξη εισόδου περιέχει μία
κατάλληλη τετράδα αριθμών, τη θέση του πρώτου στοιχείου της τετράδας καθώς και την
τετράδα αυτή. Η αναδρομική σας συνάρτηση να επιστρέφει τη θέση του πρώτου στοιχείου της
τετράδας. Σε περίπτωση αποτυχίας, να τυπώνεται κατάλληλο μήνυμα και η συνάρτηση να
επιστρέφει -1.
Η συνάρτηση που θα γράψετε θα είναι η:
τιμή_επιστροφής quadruple(ορίσματα)ορίσματα)
Για την υλοποίηση της αναδρομικής συνάρτησης και μόνο, δεν επιτρέπεται η χρήση
βρόχων επανάληψης καθώς και η χρήση καθολικών και static μεταβλητών.

Περαιτέρω μελέτη για την κατασκευή αναδρομικών συναρτήσεων


Η ακολουθία Fibonacci ορίζεται αναδρομικά για κάθε μη αρνητικό ακέραιο n ως εξής:
F(n) = F(n-1) + F(n-2), n>1 και F(0)=0, F(1)=1
Με βάση αυτό τον ορισμό, ο υπολογισμός της τιμής της ακολουθίας για δοσμένο n μπορεί να
υλοποιηθεί είτε με μια αναδρομική συνάρτηση, σύμφωνα με τον παραπάνω ορισμό, είτε με μη
αναδρομική συνάρτηση που ξεκινά τον υπολογισμό από τις μικρότερες τιμές του n και προχωρά
μέχρι την τιμή για το δοσμένο n κρατώντας σε κάθε βήμα τις δύο τελευταίες τιμές της
ακολουθίας που έχουν υπολογιστεί μέχρι στιγμής.
ΑΝΑΠΤΥΞΗ ΑΡΧΙΚΟΥ ΠΡΟΓΡΑΜΜΑΤΟΣ - Αναπτύξτε ένα πρόγραμμα το οποίο θα υλοποιεί και τις
δύο μεθόδους υπολογισμού της ακολουθίας Fibonacci μέσω δύο συναρτήσεων: μίας αναδρομικής
και μίας μη αναδρομικής. Το πρόγραμμα θα ζητά στην αρχή από το χρήστη να εισάγει ένα θετικό
ακέραιο και στη συνέχεια θα υπολογίζει την τιμή της ακολουθίας καλώντας διαδοχικά τις δύο
συναρτήσεις που υλοποιήσατε και τυπώνοντας το τελικό αποτέλεσμα που δίνει καθεμία
αναφέροντας ποια συνάρτηση έδωσε το κάθε αποτέλεσμα.
ΠΡΩΤΗ ΕΠΕΚΤΑΣΗ - Στη συνέχεια καλείστε να επεκτείνετε το πρόγραμμα που φτιάξατε ώστε κάθε
φορά που ξεκινά η κλήση μίας συνάρτησης να τυπώνει ένα κατάλληλο μήνυμα του τύπου:
Computing fib(...)
όπου αντί αποσιωπητικών θα εμφανίζει την είσοδο. Όταν τελειώνει η εκτέλεση θα τυπώνει
μήνυμα του τύπου:
fib(...) finished with result ...
Στην περίπτωση αναδρομικής συνάρτησης θα πρέπει να εμφανίζονται τα ανωτέρω μηνύματα
για κάθε αναδρομική κλήση δίνοντας τη δυνατότητα στο χρήστη να δει τον τρόπο με τον
οποίο λειτουργεί η αναδρομή. Αλλάξτε το όνομα της συνάρτησης (ορίσματα)fib) στα ανωτέρω ενδεικτικά) στα ανωτέρω ενδεικτικά
μηνύματα ώστε να ανταποκρίνεται στα ονόματα που έχετε χρησιμοποιήσει εσείς για τις δύο
συνάρτησης που υλοποιήσατε.
ΔΕΥΤΕΡΗ ΕΠΕΚΤΑΣΗ - Επεκτείνετε το πρόγραμμα που προέκυψε μετά την πρώτη επέκταση ώστε
να υπολογίζει το σύνολο των αριθμητικών πράξεων (προσθέσεις) που εκτελούνται κατά τη
διάρκεια κάθε κλήσης συνάρτησης. Σε περίπτωση αναδρομικής συνάρτησης ο υπολογισμός του

8 / 11
πλήθους για κάθε κλήση θα λαμβάνει υπόψη και τις πράξεων που έγιναν από επιμέρους
αναδρομικές κλήσεις. Μπορείτε να χρησιμοποιήσετε μια καθολική μεταβλητή για το σκοπό αυτό
που θα την αρχικοποιείτε και θα την αυξάνετε κατάλληλα κάθε φορά. Τι παρατηρείτε; Ποια
από τις δύο συναρτήσεις είναι πιο οικονομική από πλευράς πλήθους εκτελούμενων
προσθέσεων;
Παρακάτω δίνουμε ενδεικτική έξοδο από το πρόγραμμα αυτό. Το όνομα της αναδρομικής
συνάρτησης είναι rfib και της μη αναδρομικής fib.
Please give a number to compute the corresponding value of the fibonacci sequence:
6
Computing rfib(6)
Computing rfib(5)
Computing rfib(4)
Computing rfib(3)
Computing rfib(2)
Computing rfib(1)
rfib(1) finished with result 1
Computing rfib(0)
rfib(0) finished with result 0
rfib(2) finished with result 1
Computing rfib(1)
rfib(1) finished with result 1
rfib(3) finished with result 2
Computing rfib(2)
Computing rfib(1)
rfib(1) finished with result 1
Computing rfib(0)
rfib(0) finished with result 0
rfib(2) finished with result 1
rfib(4) finished with result 3
Computing rfib(3)
Computing rfib(2)
Computing rfib(1)
rfib(1) finished with result 1
Computing rfib(0)
rfib(0) finished with result 0
rfib(2) finished with result 1
Computing rfib(1)
rfib(1) finished with result 1
rfib(3) finished with result 2
rfib(5) finished with result 5
Computing rfib(4)
Computing rfib(3)
Computing rfib(2)
Computing rfib(1)
rfib(1) finished with result 1
Computing rfib(0)
rfib(0) finished with result 0
rfib(2) finished with result 1
Computing rfib(1)
rfib(1) finished with result 1
rfib(3) finished with result 2
Computing rfib(2)
Computing rfib(1)
rfib(1) finished with result 1
Computing rfib(0)
rfib(0) finished with result 0
rfib(2) finished with result 1
rfib(4) finished with result 3
rfib(6) finished with result 8
60%

9 / 11
Value computed by recursive fibonacci function: 8, 12 additions performed

Computing fib(6)
fib(6) finished with result 8

Value computed by non-recursive fibonacci function: 8, 5 additions performed

ΤΡΙΤΗ ΕΠΕΚΤΑΣΗ - Όπως παρατηρήσατε στο πρόγραμμά σας και όπως φαίνεται και από την
προηγούμενη ενδεικτική εκτέλεση, το πλήθος των πράξεων της αναδρομικής συνάρτησης είναι
γενικά μεγαλύτερο από το πλήθος πράξεων της μη αναδρομικής. Μάλιστα η μεταξύ τους διαφορά
αυξάνει όσο αυξάνει και η τιμή εισόδου. Για να αντιμετωπίσουμε το πρόβλημα της ραγδαίας
αύξησης των επιπρόσθετων πράξεων που χρειάζεται να κάνουν οι αναδρομικές εκδοχές
συναρτήσεων όπως αυτή που μελετούμε μπορούμε να επιστρατεύσουμε την τεχνική του
δυναμικού προγραμματισμού (dynamic programming). Η τεχνική αυτή συνίσταται στην
αποθήκευση τιμών της συνάρτησης που έχουν ήδη υπολογιστεί ώστε όταν τις
ξαναχρειαστούμε να μην υπολογίζονται εκ νέου. Για παράδειγμα, στο επόμενο σχήμα
απεικονίζεται ο σταδιακός αναδρομικός υπολογισμός της ακολουθίας Fibonacci για αρχική είσοδο
6:

Βλέπουμε από το παραπάνω σχήμα ότι ο υπολογισμός για fib(2) γίνεται 5 φορές για fib(3) 3
φορές, και για fib(4) 2 φορές. Εφαρμόζοντας την τεχνική του δυναμικού προγραμματισμού, οι
περιττοί επανυπολογισμοί εξοικονομούνται, όπως απεικονίζει το επόμενο σχήμα:

Την τελευταία αυτή επέκταση, ζητείται να υλοποιήσετε μια νέα εκδοχή της αναδρομικής
ακολουθίας Fibonacci η οποία θα χρησιμοποιεί την τεχνική του δυναμικού προγραμματισμού. Για
το σκοπό αυτό θα πρέπει να ορίσετε μια διάταξη μέσα στη νέα συνάρτηση που θα κατασκευάσετε
η οποία θα αποθηκεύει τις τιμές που έχουν υπολογιστεί. Όταν καλείται η νέα αυτή συνάρτηση,
αρχικά θα ελέγχει αν η είσοδος που της δόθηκε αντιστοιχεί σε τιμή που έχει ήδη υπολογίσει. Αν
ναι, θα επιστρέφει την τιμή αυτή. Αν όχι, θα υπολογίζει αναδρομικά την τιμή, θα την αποθηκεύει
στη διάταξη των υπολογισμένων τιμών και στη συνέχεια θα την επιστρέφει. Σημειώστε ότι αν
ορίσετε μια διάταξη ως τοπική μεταβλητή μέσα στη νέα συνάρτηση, οι τιμές της δεν θα
διατηρούνται μεταξύ αναδρομικών κλήσεων. Εμείς, αντιθέτως, θέλουμε θέλουμε να διατηρούνται
για να έχουμε πρόσβαση στις τιμές που υπολογίστηκαν από προηγούμενες κλήσεις. Για να το
πετύχετε αυτό η διάταξη θα πρέπει να οριστεί ως static. Μπορείτε να θεωρήσετε ένα μέγιστο
μέγεθος ΜΑΧ_Ν που θα καθορίσετε στο πρόγραμμά σας με μια συμβολική σταθερά. Αυτό σημαίνει
ότι θα μπορείτε να υπολογίσετε τιμές μέχρι MAX_N για την ακολουθία Fibonacci. Επίσης, μπορείτε
να αρχικοποιήσετε τις τιμές αυτής της διάταξης σε 0 ώστε να μπορείτε να ελέγχετε εύκολα αν
μια τιμή εισόδου (μεγαλύτερη του 1) έχει υπολογιστεί ή όχι. Αν δεν έχει υπολογιστεί η αντίστοιχη
τιμή της διάταξης θα είναι 0.

10 / 11
Μάθετε να χρησιμοποιείτε τον debugger!
Σας θυμίζουμε για άλλη μία φορά ότι είναι πολύ σημαντικό να μάθετε να χρησιμοποιείτε τον
debugger για να διορθώνετε τα λογικά λάθη που κάνετε όταν κατασκευάζετε ένα πρόγραμμα. Για
σχετικές οδηγίες ανατρέξτε στις πέντε τελευταίες σελίδες στο “Εισαγωγικό Εγχειρίδιο
Code::Blocks” που σας έχουμε ήδη δώσει από τη δεύτερη εβδομάδα του μαθήματος. Υπάρχει για
να το κατεβάσετε στις εργαστηριακές σημειώσεις του μαθήματος. Τονίζουμε στο σημείο αυτό ότι
η χρήση του debugger θα σας βοηθήσει πολύ στην καλύτερη κατανόηση της αναδρομής καθώς θα
σας δώσει τη δυνατότητα να βλέπετε με τρόπο ιδιαίτερα παραστατικό πώς γίνονται οι κλήσεις
των αναδρομικών συναρτήσεων. Συνιστούμε ισχυρά να κάνετε αυτή τη μελέτη για τις
ασκήσεις αυτού του εργαστηρίου.

11 / 11

You might also like