Σημειώσεις

Προηγούμενο

Επόμενο

Συναρτήσεις - Αναδρομική κλήση

Αναδρομή

Παράδειγμα (Παραγοντικό).

Το παραγοντικό ακεραίου $n$ ορίζεται ως $n! = n\cdot (n-1) \cdot \ldots \cdot 1$. Θα το υπολογίσουμε κάνοντας διαδοχικούς πολλαπλασιασμούς.


def factorialIter(n):
    """Assumes n is int > 0
    returns n!"""
    nfact = 1
    while n > 1:
        nfact = n*nfact
        n -= 1
    return nfact

Είναι όμως συχνά πιο απλό να σκεφτούμε ότι το παραγοντικό $(n+1)!$ μπορεί να υπολογιστεί άμεσα αν είναι γνωστό το $n!$, αφού $(n+1)! = (n+1)\cdot n!$. Αυτό ορίζει μία αναδρομική σχέση (το $n!$ υπολογίζεται αν γνωρίζουμε το $(n-1)!$, κλπ) και μας επιτρέπει να αναπτύξουμε μία επαγωγική μέθοδο. Η διαδικασία τερματίζεται αν γνωρίζουμε ότι $1!=1$ (βασική περίπτωση).

Για να γράψουμε έναν κώδικα ο οποίος θα εφαρμόζει την αναδρομική μέθοδο μπορούμε να γράψουμε μία συνάρτηση η οποία θα εφαρμόζει την αναδρομική σχέση για κάθε ακέραιο $n$.


def factorialRecur(n):
    if n == 1:
        return n
    else:
        return = n*factorialRecur(n-1)

Παρατήρηση.

Η συνάρτηση factorialRecur χρησιμοποιεί την ίδια τη συνάρτηση (δηλαδή, αντίγραφο του εαυτού της) για να μπορέσει να εφαρμόσει την αναδρομική σχέση. Επίσης, υπάρχει μία βασική περίπτωση (n==1) για την οποία οι διαδοχικές κλήσεις της συνάρτησης τερματίζονται, αλλιώς θα είχαμε διαδοχικές κλήσεις της συνάρτησης άπειρες φορές.

Παράδειγμα (Ακολουθία Fibonacci).

Αν ο πληθυσμός (κάποιου βιολογικού είδους) παρασταθεί με $F_i$ σε κάθε χρονιά $i$, τότε έχει υποστηριχθεί ότι η αύξηση του πληθυσμού κάποιων ζώων μπορεί να παρασταθεί από την ακολουθία $F_i = F_{i-1} + F_{i-2}$, δηλαδή εξαρτάται από τον πληθυσμό τα δύο προηγούμενα χρόνια. Αυτή είναι μία αναδρομική σχέση. Για την εφαρμογή ενός υπολογισμού χρειαζόμαστε μία βασική περίπτωση: θα υποθέσουμε ότι αρχικά είχαμε μόνο ένα υποκείμενο του βιολογικού είδους, δηλαδή $F_0 = 1, F_1 = 1$.


def fib(n):
    if n == 0 or n == 1:
        return 1
    else:
        return = fib(n-1) + fib(n-2)

Θα θέλαμε να μετρήσουμε πόσες κλήσεις γίνονται στη συνάρτηση fib (η οποία υπολογίζει με τον αναδρομικό αλγόριθμο έναν αριθμό Fibonacci) μέσω της αναδρομικής κλήσης της.


def fib(n):
    global numFibCalls
    numFibCalls += 1
    if n == 0 or n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

def testFib(n):
    global numFibCalls
    numFibCalls = 0
    print("Fibonacci number for",n,' = ',fib(n))
    print("Function fib was called",numFibCalls,"times")

Ερώτηση.

Τρέξτε το παραπάνω πρόγραμμα (αφού προσθέσετε λίγες γραμμές που καλούν τις συναρτήσεις) και δείτε ότι ο αριθμός των κλήσεων της fib(n) είναι αρκετά μεγάλος. Γιατί συμβαίνει αυτό; Θα μπορούσαμε να γράψουμε διαφορετικά την fib(n) ώστε να χρειάζεται μικρότερος αριθμός κλήσεων;

Παράδειγμα.

Μία λίστα λέγεται παλινδρομική αν η σειρά των στοιχείων της είναι η ίδια είτε διαβάζεται από αριστερά προς τα δεξιά είτε αντίστροφα. Ας ελέγξουμε αν μία λίστα είναι παλινδρομική χρησιμοποιώντας αναδρομική κλήση συνάρτησης.


def isPalindrome(L):
    if len(L) <= 1:
        return True
    return L[0] == L[-1] and isPalindrome(L[1:-1])

Παράδειγμα.

Έστω L μια λίστα τα στοιχεία της οποίας μπορεί να είναι με τη σειρά τους λίστες. Για παράδειγμα, θα μπορούσαμε να έχουμε L = [1, 2, [2, 3], [4], 6]. Γράψτε μια συνάρτηση η οποία κατασκευάζει μια «μονοδιάστατη» λίστα, αφαιρεί δηλαδή τυχόν εσωτερικές λίστες.


def flatten(L):
    M = []
    for item in L:
        if isinstance(item, list):
            for internalItem in item:
                M.append(internalItem)
        else:
            M.append(item)
    return M

Έλγχος τύπου μεταβλητής (isinstance()).

Η εντολή isinstance(item,list) ελέγχει αν η μεταβλητή item είναι τύπου list. Δίνει αποτέλεσμα True/False.

Χρησιμοποιώντας αναδρομή μπορούμε να γράψουμε μία πιο κομψή μορφή του προηγουμένου προγράμματος. Θα χρειαστούμε την μέθοδο M.extend(item) η οποία προσθέτει τα στοιχεία μίας λίστας item μετά το τελευταίο στοιχείο της λίστας L.


def flatten(L):
    M = []
    for item in L:
        if isinstance(item, list):
            M.extend(flatten(item))
    else:
        M.append(item)
    return M

Η μορφή αυτή του προγράμματος περιλαμβάνει και την περίπτωση για την οποία μία λίστα περιέχει λίστες οι οποίες επίσης περιέχουν λίστες, π.χ., μπορεί να κάνει μονοδιάστατη την λίστα L = [1, 2, [2, [3,5]], [4], 6].

Μελέτη

Βιβλιογραφία

  1. Σημειώσεις Μ. Πλεξουσάκη.