EuraStudy
Un algoritmo è una sequenza finita e non ambigua di passi che, a partire da dati di ingresso, produce in un numero finito di operazioni la soluzione di un problema. Questo appunto guida dall'analisi del problema alla sua formalizzazione, presentando le proprietà fondamentali degli algoritmi, le strategie di problem solving (analisi, scomposizione top-down, raffinamenti successivi), le tre strutture di controllo della programmazione strutturata (sequenza, selezione, iterazione) garantite dal teorema di Böhm-Jacopini, e i due linguaggi di descrizione — pseudocodice e diagrammi di flusso. Si chiude con i primi cenni a correttezza ed efficienza, valutata contando le operazioni eseguite.
5 sezioni~24 min di lettura3 competenzeLivello Base 1 · Standard 3 · Approfondimento 1Verificato · 07/2026
livello base
È richiesto saper analizzare un problema, scomporlo e descriverne la soluzione con le tre strutture di controllo, in pseudocodice o con un diagramma di flusso corretto.
livello avanzato
Nell'opzione Scienze Applicate si aggiungono la dimostrazione informale di correttezza (invarianti, casi limite) e la valutazione dell'efficienza tramite il conteggio delle operazioni e il confronto fra algoritmi alternativi.
Profondità di lettura: Approfondimento
Dimensione del testo: Standard
Il processo di problem solving: dal problema all'algoritmo
Analizza il problema «calcolare la media aritmetica dei voti di uno studente»: individua i dati di ingresso e di uscita, esprimi la relazione che li lega e scomponi il problema in sottoproblemi elementari.
Il risultato richiesto è un solo valore: la media aritmetica dei voti, un numero reale.
Servono i voti dello studente, cioè una sequenza di n valori numerici, e implicitamente la loro quantità n.
La media è la somma dei voti divisa per il loro numero.
Sottoproblema 1: leggere i voti e contarli (determinare n). Sottoproblema 2: calcolare la somma S dei voti. Sottoproblema 3: dividere S per n e restituire il risultato (con la condizione n > 0 per evitare la divisione per zero).
Risultato: Output: media M; input: i voli v_1, ..., v_n e il loro numero n; relazione M = S/n; il problema si scompone in «leggi e conta», «somma», «dividi e restituisci».
Errori frequenti
Approfondimento
Sul piano teorico un problema si formalizza come una funzione (o relazione) che a ogni istanza dei dati di ingresso associa i dati di uscita ammissibili: il dominio è l'insieme degli input validi, il codominio l'insieme delle possibili risposte, e risolvere il problema significa realizzare quella funzione con un procedimento effettivo. Questa lettura permette di distinguere tre grandi famiglie che ricorrono in tutto il programma: i problemi di decisione, la cui uscita è un semplice «sì/no» (un numero è primo?); i problemi di ricerca, che chiedono di esibire un oggetto con una data proprietà (trovare la posizione di un valore in un elenco); i problemi di ottimizzazione, che fra tutte le soluzioni ammissibili cercano la migliore secondo un criterio (il cammino più breve, la spesa minima). Un problema è ben posto quando la specifica è completa e non contraddittoria, cioè per ogni input ammissibile l'output atteso esiste ed è univocamente determinato. La fase di analisi è l'esercizio concreto del pensiero computazionale (computational thinking, J. Wing), che si articola in quattro operazioni intrecciate: la decomposizione (spezzare il problema in parti), il riconoscimento di schemi ricorrenti (pattern), l'astrazione (costruire il modello trascurando i dettagli irrilevanti) e la progettazione dell'algoritmo. È bene sapere fin d'ora che non tutti i problemi ben posti ammettono un algoritmo risolutivo: esistono problemi indecidibili, di cui la teoria della computabilità dimostra l'insolubilità meccanica.
Ripasso attivo
Dato il problema «calcolare la media aritmetica dei voti di uno studente», svolgi l'analisi: indica i dati di ingresso e di uscita, scrivi la relazione che li lega e scomponi il problema top-down in almeno tre sottoproblemi elementari.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) — Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Le quattro proprietà fondamentali di un algoritmo
È dato il seguente procedimento: «parti da x = 1; finché x è diverso da 0, aumenta x di 1; quando x = 0 stampa x». Stabilisci se è un algoritmo, verificando le quattro proprietà fondamentali.
Ogni passo (assegnazione, confronto, incremento, stampa) è un'operazione elementare effettivamente compibile: la proprietà è soddisfatta.
Ogni passo è definito in modo univoco, senza ambiguità: la proprietà è soddisfatta.
Partendo da x = 1 e incrementando, x assume i valori 2, 3, 4, ... e non vale mai 0: la condizione di uscita non si verifica mai e il ciclo non termina.
Poiché la finitezza è violata (ciclo infinito), il procedimento NON è un algoritmo, pur sembrando ben definito.
Risultato: Non è un algoritmo: viola la proprietà di finitezza perché la condizione di terminazione non è mai raggiunta.
Errori frequenti
Approfondimento
Conviene separare due nozioni di finitezza che spesso si confondono: la finitezza della descrizione (l'algoritmo è un testo di lunghezza finita, un numero finito di istruzioni) e la finitezza dell'esecuzione (per ogni input il calcolo termina dopo un numero finito di passi). Entrambe sono necessarie: un testo finito può descrivere un ciclo che non termina mai. Un procedimento che rispetta tutte le proprietà tranne la terminazione si dice semi-algoritmo o procedura, e non è un algoritmo. Il determinismo qui richiesto è quello degli algoritmi deterministici, in cui lo stato successivo è univocamente determinato dallo stato corrente; esistono però gli algoritmi randomizzati (probabilistici), che compiono scelte pseudo-casuali e la cui uscita o efficienza dipende dal caso — utilissimi in pratica, per esempio nel metodo Monte Carlo, ma da tenere distinti dal modello deterministico classico. La proprietà di eseguibilità si comprende appieno solo fissando l'esecutore e il suo repertorio finito di istruzioni elementari: ciò che è «passo elementare» per un calcolatore non lo è per una persona, e viceversa. Questa idea trova la sua formulazione rigorosa nella tesi di Church-Turing: la nozione intuitiva di «procedimento effettivo» coincide con ciò che è calcolabile da una macchina di Turing. È proprio tale tesi a fornire la definizione rigorosa di algoritmo — quella intuitiva delle quattro proprietà ne è la versione operativa — e sarà ripresa nel capitolo sulla teoria della computazione.
Ripasso attivo
Per ciascuna delle quattro proprietà fondamentali (finitezza, determinismo, generalità, eseguibilità), proponi un esempio di procedimento che la viola e spiega perché, di conseguenza, non può essere considerato un algoritmo.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) — Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Le tre strutture di controllo: sequenza, selezione, iterazione
Somma dei primi n interi
La somma accumulata dal ciclo enumerativo coincide con la formula chiusa di Gauss: un utile controllo di correttezza del risultato dell'iterazione.
Scrivi in pseudocodice un algoritmo che, dato un intero n > 0, calcoli e stampi la somma 1 + 2 + ... + n usando un ciclo enumerativo, e indica la grandezza che garantisce la terminazione.
Si pone una variabile accumulatore S a 0 prima del ciclo.
Per i da 1 a n: S <- S + i. Il contatore i parte da 1 e cresce a ogni iterazione fino a n.
La grandezza che garantisce la terminazione è il contatore i: poiché aumenta di 1 a ogni passo e n è finito, dopo esattamente n iterazioni si ha i > n e il ciclo termina.
Per n = 5 il ciclo accumula 1+2+3+4+5 = 15; la formula chiusa conferma 5*6/2 = 15.
Risultato: leggi n; S <- 0; per i da 1 a n esegui S <- S + i; stampa S. Per n = 5 il risultato è 15, in accordo con n(n+1)/2.
Errori frequenti
Approfondimento
Il fondamento della verifica di correttezza di un ciclo è l'invariante di ciclo: una proprietà che è vera immediatamente prima del ciclo e che, se vale all'inizio di un'iterazione, resta vera al termine della stessa. All'uscita, l'invariante combinato con la negazione della condizione di controllo fornisce la tesi da dimostrare. Per «S <- 0; per i da 1 a n: S <- S + i» l'invariante è «all'inizio dell'iterazione con contatore i, S contiene la somma 1+2+...+(i−1)»: è vero per i = 1 (S = 0, somma vuota), si conserva perché il corpo aggiunge proprio i, e all'uscita (i = n+1) dà «S = 1+2+...+n», cioè la correttezza cercata. Il teorema di Böhm-Jacopini non è solo un enunciato di esistenza ma è costruttivo: fornisce un procedimento per riscrivere qualunque diagramma di flusso, anche pieno di salti «goto», impiegando le sole tre strutture, eventualmente al prezzo di introdurre variabili di stato ausiliarie (flag booleane) che ricordano «a che punto siamo». È questo il motivo teorico per cui la programmazione strutturata non perde potere espressivo pur rinunciando al goto, salto che E. Dijkstra additò come fonte di codice illeggibile. Infine iterazione e ricorsione hanno lo stesso potere computazionale: ogni ciclo può essere riscritto come funzione ricorsiva e viceversa (quest'ultima con l'aiuto di una pila che simula i cicli annidati), un'equivalenza che sarà ripresa parlando di sottoprogrammi e ricorsione.
Ripasso attivo
Scrivi in pseudocodice un algoritmo che, dato un intero n > 0, calcoli e stampi la somma 1 + 2 + ... + n utilizzando un ciclo enumerativo; indica esplicitamente quale grandezza garantisce la terminazione.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) — Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Diagramma di flusso: numero pari o dispari
Scrivi lo pseudocodice di un algoritmo che legge un intero n e stampa «pari» se n è divisibile per 2, «dispari» altrimenti, coerente con il diagramma di flusso della Fig. 4.
leggi n (il dato di ingresso è un numero intero).
Si calcola il resto della divisione intera di n per 2 con l'operatore modulo: la condizione è n mod 2 = 0.
se n mod 2 = 0 allora scrivi «pari» altrimenti scrivi «dispari».
Per n = 7: 7 mod 2 = 1, diverso da 0, quindi si esegue il ramo altrimenti e si stampa «dispari» (corretto).
Risultato: leggi n; se n mod 2 = 0 allora scrivi «pari» altrimenti scrivi «dispari». Per n = 7 stampa «dispari».
Errori frequenti
Approfondimento
Lo strumento principe per verificare a mano un algoritmo, prima ancora di eseguirlo su un calcolatore, è la tabella di traccia (desk check o dry run): si predispone una colonna per ogni variabile e una riga per ogni passo, e si aggiornano i valori istruzione per istruzione, simulando l'esecutore. Così si scoprono gli errori logici — un contatore che parte dal valore sbagliato, una condizione invertita — che nessun controllo puramente sintattico rivelerebbe. La distinzione fra sintassi e semantica è qui cruciale: la sintassi è l'insieme delle regole di forma (come si scrive un'istruzione), la semantica è il significato, cioè l'effetto sull'esecutore. L'assegnazione «x <- espressione» ha semantica di effetto sullo stato: prima valuta il membro destro nello stato corrente, poi vi memorizza il risultato distruggendo il valore precedente; per questo «x <- x + 1» non è l'uguaglianza impossibile «x = x + 1», ma l'istruzione «incrementa x di 1». Un formalismo alternativo al diagramma di flusso è il diagramma strutturato di Nassi-Shneiderman, che rappresenta l'algoritmo come blocchi annidati anziché come nodi collegati da frecce: poiché non esistono frecce libere, i salti sono impossibili per costruzione e la struttura a blocchi della programmazione strutturata risulta immediatamente leggibile. Ogni buona pseudocodifica rispetta inoltre la regola dell'unico ingresso e unica uscita per ciascun blocco, che rende l'algoritmo componibile e verificabile pezzo per pezzo.
Ripasso attivo
Disegna il diagramma di flusso e scrivi lo pseudocodice di un algoritmo che legge un numero intero e stampa la parola «pari» se è divisibile per 2, «dispari» altrimenti.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) — Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Crescita del numero di confronti: ricerca lineare (~n) e ricerca binaria (~log2 n)
Ricerca lineare (caso peggiore)
Nel caso peggiore (elemento assente o in ultima posizione) la ricerca lineare confronta x con tutti gli n elementi: il costo cresce linearmente con n.
Ricerca binaria (dati ordinati)
Dimezzando a ogni passo lo spazio di ricerca, il numero di confronti cresce come il logaritmo in base 2 di n: per n = 1000 sono circa 10 confronti contro i 1000 della ricerca lineare.
Scrivi la ricerca lineare di un valore x in una sequenza di n elementi, determina il numero di confronti nel caso peggiore e confrontalo con la ricerca binaria su dati ordinati per n = 1000.
trovato <- falso; per i da 1 a n esegui: se A[i] = x allora trovato <- vero. Si scorre l'intera sequenza confrontando ogni elemento con x.
Se x non è presente (o è l'ultimo elemento), si eseguono n confronti: il costo è proporzionale a n.
Su dati ordinati la ricerca binaria dimezza lo spazio a ogni passo: il numero di confronti è circa il logaritmo in base 2 di n.
Lineare: fino a 1000 confronti. Binaria: log2(1000) ~ 9.97, quindi circa 10 confronti. La binaria è circa cento volte più conveniente, ma richiede dati ordinati.
Risultato: La ricerca lineare costa fino a n = 1000 confronti nel caso peggiore; la ricerca binaria ne richiede circa 10 (~log2 1000), risultando nettamente più efficiente sui dati ordinati.
Errori frequenti
Approfondimento
La nozione intuitiva di «ordine di grandezza» si formalizza con la notazione O-grande (big-O), che descrive il comportamento asintotico del costo per n grande. Si scrive «f(n) = O(g(n))» quando esistono una costante «c > 0» e una soglia «n₀» tali che «f(n) ≤ c·g(n)» per ogni «n ≥ n₀»: g(n) è allora un limite superiore alla crescita di f, a meno di un fattore costante. Da questa definizione discende perché si trascurino le costanti moltiplicative e i termini di ordine inferiore: «3n² + 5n + 7» è «O(n²)», perché per n grande il termine quadratico domina. Accanto a O (limite superiore) si usano «Ω» (limite inferiore) e «Θ» (ordine esatto, quando O e Ω coincidono). La gerarchia degli ordini più comuni è «O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ)», e passare da un ordine al successivo cambia radicalmente la scalabilità del programma. Il costo logaritmico della ricerca binaria si ricava dalla sua equazione di ricorrenza: a ogni passo il problema si dimezza al prezzo di un confronto, cioè «T(n) = T(n/2) + O(1)», che sviluppata dà «T(n) = O(log₂ n)» — dopo circa «log₂ n» dimezzamenti l'intervallo si riduce a un solo elemento. Resta il compromesso di fondo: la ricerca binaria è velocissima ma esige dati ordinati, e ordinare costa a sua volta almeno «O(n log n)»; conviene quindi solo quando la stessa sequenza, ordinata una volta, viene poi interrogata molte volte.
Ripasso attivo
Per una sequenza di n elementi, scrivi lo pseudocodice della ricerca lineare di un valore x, conta il numero di confronti nel caso peggiore e confrontalo con il costo della ricerca binaria su dati ordinati per n = 1000.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) — Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Riferimenti e fonti
Ministero dell'Istruzione e del Merito (MIM)