Ricerca lineare e binaria
In questa pagina 5
In questa pagina 4
Problema: data una sequenza v e un valore x, trovare un indice i con v[i] == x, o segnalare che non c'è (convenzione: restituire -1).
Ricerca lineare
Definizione (ricerca lineare). Funziona su qualunque sequenza, anche non ordinata.
def ricerca_lineare(v, x): for i in range(len(v)): if v[i] == x: return i return -1
- Caso migliore , caso peggiore (elemento assente).
- Restituisce la prima occorrenza.
- È ciò che fanno
x in vev.index(x).
Esempio. ricerca_lineare([4, 7, 7, 1], 7) restituisce 1 (la prima occorrenza) e ricerca_lineare([4, 7], 5) restituisce -1.
Ricerca binaria
Definizione (ricerca binaria). Richiede
vordinata. Si confrontaxcon l'elemento centrale e si scarta metà dell'intervallo a ogni passo.def ricerca_binaria(v, x): lo, hi = 0, len(v) - 1 # intervallo di ricerca [lo, hi], estremi inclusi while lo <= hi: m = (lo + hi) // 2 if v[m] == x: return m elif v[m] < x: lo = m + 1 # x, se c'è, sta a destra di m else: hi = m - 1 # x, se c'è, sta a sinistra di m return -1Invariante: se
xè inv, allora si trova inv[lo..hi]. Il ciclo termina perché a ogni giro l'intervallo si riduce di almeno un elemento (m + 1,m - 1); quandolo > hil'intervallo è vuoto exnon c'è.Costo: dopo iterazioni restano circa elementi, quindi al più iterazioni: . Con bastano 20 confronti.
Esempio. ricerca_binaria([2, 5, 8, 12, 16, 23, 38], 23) restituisce 5; con bastano al più iterazioni.
Esempio su v = [2, 5, 8, 12, 16, 23, 38], x = 23:
| lo | hi | m | v[m] | decisione |
|---|---|---|---|---|
| 0 | 6 | 3 | 12 | 12 < 23, lo = 4 |
| 4 | 6 | 5 | 23 | trovato |
Versione ricorsiva
def ricerca_binaria_ric(v, x, lo=0, hi=None):
if hi is None:
hi = len(v) - 1
if lo > hi:
return -1
m = (lo + hi) // 2
if v[m] == x:
return m
if v[m] < x:
return ricerca_binaria_ric(v, x, m + 1, hi)
return ricerca_binaria_ric(v, x, lo, m - 1)Equazione del costo (vedi RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →, Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →). Passare gli indici e non v[:m]: lo slicing costerebbe a ogni chiamata.
Quale usare
- Una sola ricerca su dati non ordinati: lineare (); ordinare costa già .
- Molte ricerche sugli stessi dati: ordinare una volta (Algoritmi di ordinamentoSelection sort, insertion sort e bubble sort (quadratici), merge sort (n log n, divide et impera); stabilità, ordinamento sul posto, costo nei vari casi; sort e sorted con key.Algoritmi di ordinamento →) e poi ricerca binaria. Oppure un insieme o dizionario (Dizionari e insiemi in PythonADT mappa e insieme; dict con chiavi hashable, accesso, get, iterazione, conteggi e raggruppamenti; set e operazioni insiemistiche; tabelle hash e costo O(1) medio; Counter e defaultdict.Dizionari e insiemi in Python →), in media, se non serve l'ordine.
Modulo bisect
Definizione (modulo bisect)
import bisect v = [2, 5, 8, 8, 12] bisect.bisect_left(v, 8) # 2: primo indice dove inserire 8 mantenendo l'ordine bisect.bisect_right(v, 8) # 4 bisect.insort(v, 7) # inserimento ordinato: [2, 5, 7, 8, 8, 12]
bisect_left(v, x)restituisce un indiceiconv[i] == xsexc'è (ei < len(v)).
Esempio. Con v = [2, 5, 8, 8, 12], bisect_left(v, 8) restituisce 2 e bisect_right(v, 8) restituisce 4.
Errori tipici
- Ricerca binaria su una lista non ordinata: risultati sbagliati senza errori.
while lo < hicon estremi inclusi: l'ultimo elemento candidato non viene controllato.lo = mohi = minvece dim + 1/m - 1: ciclo infinito quando l'intervallo ha due elementi.returndentro il ciclo lineare nel ramoelse(si esce dopo il primo confronto).
Versione ripasso
Ricerca lineare
Funziona su qualunque sequenza, anche non ordinata.
- Caso migliore , caso peggiore .
- Restituisce la prima occorrenza (
x in v,v.index(x)).
Ricerca binaria
Richiede v ordinataSelection sort, insertion sort e bubble sort (quadratici), merge sort (n log n, divide et impera); stabilità, ordinamento sul posto, costo nei vari casi; sort e sorted con key.Algoritmi di ordinamento →. A ogni passo si scarta metà intervallo.
def ricerca_binaria(v, x):
lo, hi = 0, len(v) - 1
while lo <= hi:
m = (lo + hi) // 2
if v[m] == x: return m
elif v[m] < x: lo = m + 1
else: hi = m - 1
return -1- Invariante: se
xè inv, si trova inv[lo..hi]. - Costo: (al più iterazioni).
Versione ricorsiva
def ricerca_binaria_ric(v, x, lo=0, hi=None):
if hi is None: hi = len(v) - 1
if lo > hi: return -1
m = (lo + hi) // 2
if v[m] == x: return m
if v[m] < x: return ricerca_binaria_ric(v, x, m + 1, hi)
return ricerca_binaria_ric(v, x, lo, m - 1)Costo (RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →, Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →). Passare gli indici, non lo slicing .
Modulo bisect
import bisect
bisect.bisect_left(v, 8) # primo indice per inserire 8 mantenendo l'ordine
bisect.bisect_right(v, 8)
bisect.insort(v, 7) # inserimento ordinatoErrori tipici
- Ricerca binaria su lista non ordinata.
while lo < hicon estremi inclusi (ultimo elemento non controllato).lo = mohi = m(rischio ciclo infinito).