Algoritmi e complessità: domande di esercitazione con spiegazioni

Domande a risposta multipla gratuite su Algoritmi e complessità, con suggerimenti e spiegazioni, su tre livelli: termini e fatti chiave, applicare i concetti, casi e analisi.

Esercizio gratuitoFacile

Quale classe di complessità cresce più rapidamente per n grande?

Esercizio successivo →

Su cosa ti eserciti

  1. Faciletermini e fatti chiave
  2. Mediaapplicare i concetti
  3. Difficilecasi e analisi

Impara Algoritmi e complessità da zero

1Che cosa misura la complessità

Un algoritmo è un elenco finito di passi precisi che risolve un problema, come ordinare una lista o trovare un nome in un elenco telefonico.

Due algoritmi possono risolvere lo stesso problema ma impiegare tempi molto diversi. La complessità descrive come il lavoro cresce al crescere dell’input.

La dimensione dell’input si chiama n: il numero di elementi da ordinare, la lunghezza di una lista, il numero di nodi di un grafo.

Pensa di cercare un nome in un elenco telefonico di 1000 pagine. Leggere ogni pagina richiede fino a 1000 passi. Aprirlo a metà e dimezzare ogni volta richiede circa 10 passi.

La complessità temporale conta i passi. La complessità spaziale conta la memoria aggiuntiva. Entrambe si scrivono come funzione di n.

Di solito si considera il caso peggiore: il lavoro massimo su tutti gli input di dimensione n. A volte si chiede invece il caso medio o il caso migliore.

Autoverifica Un algoritmo il cui lavoro cresce come n² impiega 1 secondo per n = 1000. Circa quanto impiega per n = 2000?
  1. 1 secondo
  2. 2 secondi
  3. 4 secondi
  4. 8 secondi

Risposta: 4 secondi. Raddoppiare n moltiplica n² per 2² = 4, quindi il tempo passa da circa 1 a circa 4 secondi. 2 secondi sarebbe la risposta per una crescita lineare, e 8 per n³.

2Notazione O grande e classi di crescita

La notazione O grande (Big O) conserva solo ciò che conta per n grande. Si eliminano i fattori costanti e si tiene solo il termine che cresce più in fretta.

3n² + 5n + 2 ⇒ O(n²)

Per n = 1000 il termine n² vale 3 000 000, mentre 5n è solo 5000. I termini più piccoli cambiano appena il totale.

Queste classi compaiono continuamente, dalla crescita più lenta alla più veloce:

  • O(1), costante: leggere un elemento di un array tramite il suo indice.
  • O(log n), logaritmica: la ricerca binaria, che dimezza il problema a ogni passo.
  • O(n), lineare: un solo passaggio su tutti gli elementi, come trovare il valore massimo.
  • O(n log n): buoni algoritmi di ordinamento come il merge sort.
  • O(n²), quadratica: due cicli annidati sugli elementi, come il bubble sort.
  • O(2ⁿ), esponenziale: provare ogni sottoinsieme. Diventa rapidamente inutilizzabile.
ntlog nnn log nn²2ⁿ

O grande è un limite superiore: il lavoro cresce al più così in fretta. Ω (Omega grande) è un limite inferiore, e Θ (Theta) è un limite stretto, sia superiore sia inferiore.

La base di un logaritmo non conta nella notazione O grande, perché i logaritmi con basi diverse differiscono solo per un fattore costante.

Autoverifica Qual è l’O grande di 4n + 2n² + 100?
  1. O(100)
  2. O(2n²)
  3. O(n)
  4. O(n²)

Risposta: O(n²). Per n grande il termine n² domina, e il fattore costante 2 si elimina. O(n) tiene il termine sbagliato, e O(2n²) dimentica di eliminare la costante.

3Ricerca, ordinamento e ricorsione

Alcuni algoritmi ricorrono in quasi ogni corso. Impara le loro complessità come dati di fatto.

  • La ricerca lineare controlla gli elementi uno a uno: O(n). La ricerca binaria richiede un array ordinato e dimezza l’intervallo a ogni passo: O(log n).
  • Il bubble sort, l’insertion sort e il selection sort confrontano e scambiano elementi in cicli annidati: O(n²). L’insertion sort è O(n) su un array già ordinato.
  • Il merge sort divide l’array in metà, le ordina e le fonde: O(n log n) in ogni caso, con O(n) di memoria aggiuntiva.
  • Il quicksort divide attorno a un pivot: O(n log n) in media, ma O(n²) nel caso peggiore, per esempio un array ordinato con il primo elemento come pivot.
  • Nessun ordinamento basato solo su confronti può fare meglio di Ω(n log n) nel caso peggiore. Il counting sort è più veloce perché non confronta.

Una funzione ricorsiva richiama se stessa su un input più piccolo finché raggiunge un caso base a cui risponde direttamente. Il suo tempo di esecuzione si scrive come una relazione di ricorrenza:

  • T(n) = T(n/2) + O(1) dà O(log n), come la ricerca binaria.
  • T(n) = 2T(n/2) + O(n) dà O(n log n), come il merge sort.
  • T(n) = T(n − 1) + O(n) dà O(n²).
  • T(n) = 2T(n − 1) + O(1) dà O(2ⁿ).

Vale la pena conoscere tre idee di progettazione. Divide et impera divide, risolve e combina. Il metodo greedy (goloso) sceglie a ogni passo l’opzione localmente migliore. La programmazione dinamica memorizza le risposte ai sottoproblemi sovrapposti così che vengano calcolate una sola volta.

Autoverifica Qual è la complessità temporale nel caso peggiore del quicksort?
  1. O(log n)
  2. O(n log n)
  3. O(n)
  4. O(n²)

Risposta: O(n²). Con pivot sbagliati ogni divisione lascia n − 1 elementi da un lato, il che dà O(n²). O(n log n) è il suo caso medio, e il caso peggiore del merge sort.

4Il metodo passo dopo passo

Per trovare la complessità temporale di un frammento di codice:

  1. Decidi che cos’è n: la dimensione dell’input.
  2. Conta quante volte viene eseguito ogni ciclo. Un ciclo che somma 1 a ogni passo viene eseguito circa n volte. Un ciclo che raddoppia o dimezza viene eseguito circa log₂ n volte.
  3. I cicli uno dentro l’altro si moltiplicano. I cicli uno dopo l’altro si sommano.
  4. Per una funzione ricorsiva, scrivi la ricorrenza: quante chiamate, su quale dimensione, più il lavoro per chiamata.
  5. Tieni il termine che cresce più in fretta ed elimina le costanti.
  6. Controlla con un numero piccolo, per esempio n = 8 o n = 1024.

Per una domanda teorica, nomina prima l’algoritmo o l’idea che descrive, poi richiama la sua complessità e le sue condizioni (input ordinato, pesi non negativi, sottoproblemi sovrapposti).

5Un esempio svolto

Segui ogni passaggio. Prova a prevedere il successivo prima di aprirlo.

Esempio svolto

Qual è la complessità temporale di questo codice? for i = 1 to n: (j = 1; while j < n: j = j × 2)

1

Indica la dimensione dell’input

n è il numero fino al quale vanno i cicli.

2

Conta il ciclo esterno

i va da 1 a n con passo 1, quindi il ciclo esterno viene eseguito n volte.

3

Conta il ciclo interno

j assume i valori 1, 2, 4, 8, … e si ferma quando raggiunge n. Dopo k giri j = 2k, quindi il ciclo si ferma dopo circa log₂ n giri.

4

Combina i cicli

Il ciclo interno sta dentro il ciclo esterno, quindi i conteggi si moltiplicano.

n · log₂ n ⇒ O(n log n)
5

Controlla con un numero

Per n = 1024 il ciclo interno viene eseguito log₂ 1024 = 10 volte, quindi il corpo viene eseguito 1024 · 10 = 10 240 volte. È molto meno di n² = 1 048 576.

Risposta

O(n log n)

6Errori comuni

  • Moltiplicare cicli che vengono eseguiti uno dopo l’altro. n + n = 2n è ancora O(n).
  • Tenere le costanti, come in O(2n) o O(n/2). Entrambe sono O(n).
  • Contare un ciclo che raddoppia come n giri. Viene eseguito circa log₂ n volte.
  • Usare la ricerca binaria su dati non ordinati, o su una lista concatenata dove il punto centrale non si raggiunge in un solo passo.
  • Dare il caso medio del quicksort quando viene chiesto il caso peggiore.
  • Pensare che O grande significhi “esattamente”. O(n²) è un limite superiore; Θ(n²) è la crescita esatta.
Autoverifica Una funzione esegue un ciclo di n iterazioni e poi, separatamente, un altro ciclo di n iterazioni. Qual è la sua complessità temporale?
  1. O(2n)
  2. O(log n)
  3. O(n)
  4. O(n²)

Risposta: O(n). I cicli uno dopo l’altro si sommano: n + n = 2n, e la costante si elimina, quindi O(n). O(n²) sarebbe corretto solo se un ciclo fosse dentro l’altro.

7Come sono fatte le domande d’esame

  • Facile: termini e fatti chiave. La complessità della ricerca binaria, del merge sort o dell’accesso a un array, il significato di O, Ω e Θ, che cos’è la ricorsione.
  • Medio: applicare i concetti. La complessità di un frammento di codice, una ricorrenza da risolvere, come cambia il tempo quando n raddoppia, quale algoritmo si adatta a un compito.
  • Difficile: casi e analisi. Il teorema master, i casi peggiori del quicksort, greedy contro programmazione dinamica, e le classi P, NP e NP-completo.

P contiene i problemi risolvibili in tempo polinomiale. NP contiene i problemi le cui soluzioni possono essere verificate in tempo polinomiale. Ogni problema in P è in NP; se P = NP è ancora sconosciuto.

Impara passo dopo passo nell’appImpara passo dopo passo nell’app

Domande di esempio

Esempio 1Facile

Quale notazione descrive un limite superiore della crescita?

  1. AOmega grande (Big Omega)
  2. BOmega piccolo (little omega)
  3. CO grande (Big O)
  4. DTheta grande (Big Theta)
Mostra i suggerimenti
  1. «Al massimo».
  2. Ora due risposte sbagliate sono barrate. Una delle due rimaste è giusta.
Mostra la risposta e la spiegazione

Perché è questa la risposta

O fornisce un limite superiore, Ω un limite inferiore e Θ un limite stretto.

Risposta

C · O grande (Big O)
Esempio 2Media

In un array ordinato cerchi due elementi la cui somma sia un valore dato muovendo un puntatore da ciascuna estremità verso l'interno. Qual è la complessità temporale?

  1. AO(n)
  2. BO(n log n)
  3. CO(n²)
  4. DO(log n)
Mostra i suggerimenti
  1. Ogni passo sposta un puntatore di una posizione.
  2. Ora due risposte sbagliate sono barrate. Una delle due rimaste è giusta.
Mostra la risposta e la spiegazione

Perché è questa la risposta

Se la somma è troppo piccola il puntatore sinistro si sposta a destra, se è troppo grande il puntatore destro si sposta a sinistra, quindi ci sono al massimo n passi.

Risposta

A · O(n)
Esempio 3Difficile

L'algoritmo di Strassen moltiplica matrici usando 7 invece di 8 moltiplicazioni ricorsive di blocchi di metà dimensione. Qual è il suo tempo di esecuzione?

  1. AΘ(n² log n)
  2. BΘ(n2)
  3. CΘ(n³)
  4. DΘ(nlog₂ 7), circa n2,81
Mostra i suggerimenti
  1. La ricorrenza è T(n) = 7T(n/2) + O(n²).
  2. Ora due risposte sbagliate sono barrate. Una delle due rimaste è giusta.
Mostra la risposta e la spiegazione

Perché è questa la risposta

Con a = 7 e b = 2, nlog₂ 7 domina il lavoro di addizione O(n²), da cui circa n2,81.

Risposta

D · Θ(nlog₂ 7), circa n2,81

Domande e risposte

Algoritmi e complessità: di cosa si tratta?

Un algoritmo è un elenco finito di passi precisi che risolve un problema, come ordinare una lista o trovare un nome in un elenco telefonico.

Due algoritmi possono risolvere lo stesso problema ma impiegare tempi molto diversi. La complessità descrive come il lavoro cresce al crescere dell’input.

Algoritmi e complessità: come si impara passo per passo?
  1. Che cosa misura la complessità
  2. Notazione O grande e classi di crescita
  3. Ricerca, ordinamento e ricorsione
  4. Il metodo passo dopo passo
  5. Un esempio svolto
  6. Errori comuni
  7. Come sono fatte le domande d’esame
Algoritmi e complessità: che cosa si esercita?
  • Facile: termini e fatti chiave
  • Media: applicare i concetti
  • Difficile: casi e analisi

Ogni domanda ha quattro opzioni e solo una è corretta. Bloccato? Apri prima un piccolo suggerimento e la spiegazione completa solo quando ti serve.

Come funziona l’esercitazione

  • Ogni domanda ha quattro opzioni e solo una è corretta.
  • Bloccato? Apri prima un piccolo suggerimento e la spiegazione completa solo quando ti serve.
  • I tuoi progressi, i tuoi errori e gli esercizi salvati restano memorizzati, così puoi ripassare prima dell’esame.

Argomenti prima e dopo questo

Poi continua con

Corsi con questo argomento

Altri argomenti di Informatica

Scheda di esercizi stampabile