Vai al contenuto

Algoritmi ad hoc

Pubblicato:

Quando un’istanza diventa una classe

Una definizione che mi è stata insegnata durante i miei primissimi giorni di università a Catania, e che è rimasta con me da allora, è quella di algoritmo:

Un algoritmo è una procedura composta da una sequenza ordinata e finita di istruzioni che permette di risolvere una classe di problemi.

Ovviamente si tratta di una definizione molto generica, come generica e potenzialmente illimitata è la categoria di algoritmi (e problemi associati) che sono stati sviluppati e che continueranno ad essere sviluppati in futuro.
In questa istanza, tuttavia, vorrei soffermarmi a soppesare il ruolo della parola “classe” in questa definizione. Anche in questo caso si tratte di un concetto piuttosto vago, ma che in generale si può interpretare come un insieme di problemi che condividono una struttura ed un obiettivo comune, differendo in dettagli parametrici.
Senza nessun motivo particolare, vi propongo di dare un’occhiata a questo codice e provare a capire quale classe di problemi risolve:

def algorithm(data: list) -> list:
    # Il primo e il secondo elemento vengono scambiati
    data[0], data[1] = data[1], data[0]
    # Il terzo e il quinto elemento vengono scambiati
    data[4], data[2] = data[2], data[4]
    return data

L’esempio più classico di algoritmo è quello di ordinamento. L’obiettivo è quello di prendere in input una sequenza di elementi e restituire una sequenza contenente gli stessi elementi ma ordinata secondo un criterio prestabilito.
Ne esistono tante varianti, da quelle più semplici ed intuitive, come Bubble Sort, Selection Sort ed Insertion Sort, a quelle più complesse ed efficienti, come Quick Sort, Merge Sort ed Heap Sort. A prescindere dalla complessità asintotica, che non mi interessa prendere in considerazione al momento, tutte le strategie che ho elencato sono in grado di ordinare qualsiasi sequenza di elementi confrontabili, sebbene il tempo di esecuzione, nella pratica, possa variare di parecchio.

def bubble_sort(data: list) -> list:
    n = len(data)
    for i in range(n):
        for j in range(0, n-i-1):
            if data[j] > data[j+1]:
                data[j], data[j+1] = data[j+1], data[j]
    return data

Potrebbe sembrare incredibile, ma anche il codice che ho mostrato in precedenza rientra nella categoria degli algoritmi di ordinamento. Beh, a patto che la sequenza in input abbia almeno 5 elementi e che sia sufficiente scambiare il primo con il secondo e il terzo con il quinto per ottenere una sequenza ordinata. In questo caso, l’algoritmo risolve una classe molto ristretta di problemi.
Quello che ho appena descritto è un fastidiosissimo fenomeno che mi è capitato fin troppo spesso di incontrare da quando ho iniziato il dottorato. Superando i miei pregiudizi, però, devo riconoscere che si tratta di un approccio più che ragionevole. Se non si ha particolare esperienza, tempo, ed interesse da impiegare per il design di un algoritmo come si deve, e soprattutto se si ha la fretta di produrre un risultato presentabile, allora è normale cercare di mettere insieme qualcosa che funzioni solo per il caso specifico che si ha davanti, facendo leva sulle sue caratteristiche peculiari.
Tuttavia, è facile constatare come i nodi vengono presto al pettine. Se l’istanza del problema cambia anche solo leggermente, bisogna rimettere mano all’implementazione; si potrebbe scoprire come l’approccio non sia generalizzabile, in quanto abbia bisogno delle ipotesi restrittive irrealistiche; soprattutto, non si è scritto del software utile ad altre persone ma solo un prototipo, non molto diverso dallo scrivere il risultato di un problema di matematica senza preoccuparsi di come si è arrivati a quel risultato.
La verità è che, progettare software degno di questo nome richiede tempo. Tanto tempo, che spesso non si ha.
La soluzione è quindi rassegnarsi a lavorare su case study triti e ritriti, limitandosi a mostrare solo quelli che mettono in buona luce il proprio lavoro? Spero vivamente di no, ma l’esperienza mi dice che spesso è proprio così. Tuttavia, fra le migliaia di tool che non vedranno mai un utilizzo serio al di fuori della loro presentazione, ve ne sono moltissimi altri che invece hanno avuto un impatto concreto e che a loro volta sono diventati la base per progetti successivi, sempre caratterizzati da uno sviluppo costante nel corso di anni da parte di un team dedicato e competente.
Sono questi gli esempi virtuosi a cui voglio ispirarmi.