Salta al contenuto
Note per Studenti Ricerca informata - greedy best-first, A* ed euristiche

Ricerca informata - greedy best-first, A∗ ed euristiche

In questa pagina 8

Le strategie di Ricerca nello spazio degli stati - formulazione dei problemi e ricerca non informataUn agente risolutore di problemi è un agente basato su obiettivi con rappresentazione atomica: formula un problema (stato iniziale, azioni, modello di transizione, test dell'obiettivo, costo dei passi) e cerca una sequenza di azioni che porta a uno stato obiettivo costruendo un albero di ricerca. Le strategie non informate usano solo la definizione del problema e si distinguono per l'ordine di espansione della frontiera: ampiezza (coda FIFO), costo uniforme (coda con priorità sul costo g), profondità (pila LIFO), profondità limitata, approfondimento iterativo e ricerca bidirezionale. Si confrontano per completezza, ottimalità, tempo e spazio in funzione di b, d, m; tutte sono casi di best-first search con una diversa funzione di valutazione f.Ricerca nello spazio degli stati - formulazione dei problemi e ricerca non informata → non sanno quanto sia lontano l'obiettivo. Qui si aggiunge quell'informazione sotto forma di una funzione euristica, e si ottiene una ricerca che si "orienta" verso l'obiettivo. Poi si passa ai problemi in cui il cammino non conta, risolti con la ricerca locale. Fonte: slide del corso di Intelligenza Artificiale, Ing. dell'Informazione UniPD, con il laboratorio 3.

Ricerca informata e funzione euristica

L'idea: associare a ogni nodo nn una funzione euristica h(n)h(n), la stima del costo del cammino più economico da nn a un obiettivo, ed espandere il nodo più "desiderabile". L'implementazione è la stessa dell'ampiezza e del costo uniforme: la frontiera è una coda con priorità ordinata per desiderabilità decrescente. Due casi speciali: greedy best-first e A∗.

Esempio che ritorna in tutta la nota: la mappa della Romania con obiettivo Bucarest e come euristica la distanza in linea retta (straight-line distance) da Bucarest:

città hSLDh_{SLD} città hSLDh_{SLD}
Arad 366 Mehadia 241
Bucarest 0 Neamt 234
Craiova 160 Oradea 380
Drobeta 242 Pitesti 100
Eforie 161 Rimnicu Vilcea 193
Fagaras 176 Sibiu 253
Giurgiu 77 Timisoara 329
Hirsova 151 Urziceni 80
Iasi 226 Vaslui 199
Lugoj 244 Zerind 374

Definizione. La ricerca greedy best-first espande il nodo che sembra più vicino all'obiettivo: f(n)=h(n)f(n) = h(n), con h(n)h(n) stima del costo da nn all'obiettivo più vicino.

Esempio: da Arad a Bucarest con hSLDh_{SLD} (grafo con tabella dei raggiunti):

passo si espande frontiera dopo l'espansione (città, hh)
1 Arad (366) Sibiu 253, Timisoara 329, Zerind 374
2 Sibiu (253) Fagaras 176, Rimnicu Vilcea 193, Timisoara 329, Zerind 374, Oradea 380
3 Fagaras (176) Bucarest 0, Rimnicu Vilcea 193, Timisoara 329, Zerind 374, Oradea 380
4 Bucarest (0) obiettivo

Cammino Arad →\to Sibiu →\to Fagaras →\to Bucarest, costo 140+99+211=450140 + 99 + 211 = 450, con 4 espansioni. Il cammino ottimo costa 418418 (via Rimnicu Vilcea e Pitesti): greedy è veloce ma sceglie la strada che "sembra" più corta, senza guardare il costo già speso.

Proprietà:

  • Completa: solo in spazi finiti e senza cicli (con la tabella dei raggiunti in spazi finiti sì; su alberi può girare in tondo).
  • Tempo: O(bm)O(b^m) nel caso peggiore, ma una buona euristica lo migliora molto.
  • Spazio: O(bm)O(b^m), tiene in memoria tutti i nodi.
  • Non ottima.

Definizione. A∗ usa la funzione di valutazione f(n)=g(n)+h(n),f(n) = g(n) + h(n), dove g(n)g(n) è il costo finora speso per arrivare a nn, h(n)h(n) la stima del costo restante e f(n)f(n) la stima del costo totale di un cammino che passa per nn. Idea: evitare di espandere cammini già costosi.

Con h=0h = 0 si ottiene il costo uniforme; con f=hf = h il greedy.

Esempio: da Arad a Bucarest con hSLDh_{SLD}. Ogni nodo è scritto come g+h=fg + h = f; la frontiera è ordinata per ff.

passo si espande (ff) nodi generati (g+h=fg + h = f)
1 Arad (0+366=3660 + 366 = 366) Sibiu 140+253=393140 + 253 = 393; Timisoara 118+329=447118 + 329 = 447; Zerind 75+374=44975 + 374 = 449
2 Sibiu (393) Rimnicu Vilcea 220+193=413220 + 193 = 413; Fagaras 239+176=415239 + 176 = 415; Oradea 291+380=671291 + 380 = 671 (Arad è già raggiunta con g=0g = 0 e non rientra; nella versione ad albero rientrerebbe con 280+366=646280 + 366 = 646)
3 Rimnicu Vilcea (413) Pitesti 317+100=417317 + 100 = 417; Craiova 366+160=526366 + 160 = 526
4 Fagaras (415) Bucarest 450+0=450450 + 0 = 450
5 Pitesti (417) Bucarest 418+0=418418 + 0 = 418, sostituisce quello da 450450
6 Bucarest (418) obiettivo

I costi gg vengono dai passi: Rimnicu Vilcea 140+80=220140 + 80 = 220, Fagaras 140+99=239140 + 99 = 239, Pitesti 220+97=317220 + 97 = 317, Bucarest via Fagaras 239+211=450239 + 211 = 450, via Pitesti 317+101=418317 + 101 = 418. Soluzione Arad →\to Sibiu →\to Rimnicu Vilcea →\to Pitesti →\to Bucarest, costo 418, ottima, con 6 espansioni (il costo uniforme ne fa 13, il greedy 4 ma sbaglia). Al passo 4 A∗ espande Fagaras perché 415<417415 < 417 e genera Bucarest con f=450f = 450; non lo sceglie subito perché Pitesti (417417) e poi Bucarest via Pitesti (418418) hanno ff minore.

Il codice del laboratorio è la best_first_search della nota precedente con f = g + h:

python
def astar_search(problem, h=None):
    """Search nodes with minimum f(n) = g(n) + h(n)."""
    h = h or problem.h
    return best_first_search(problem, f=lambda n: g(n) + h(n))

Euristica ammissibile e consistente

Definizione. Un'euristica hh è ammissibile se h(n)≥0h(n) \ge 0, h(G)=0h(G) = 0 per ogni obiettivo GG e non sovrastima mai il costo vero: h(n)≤h∗(n)h(n) \le h^\ast(n), con h∗(n)h^\ast(n) costo del cammino ottimo da nn all'obiettivo. È consistente (o monotona) se per ogni nodo nn, ogni azione aa e successore n′n' h(n)≤c(n,a,n′)+h(n′).h(n) \le c(n, a, n') + h(n').

(È una disuguaglianza triangolare: stimare da nn non può costare più che fare un passo e poi stimare da n′n'.) Consistente ⇒\Rightarrow ammissibile: lungo un cammino ottimo n=n0,n1,…,nk=Gn = n_0, n_1, \dots, n_k = G, con i passi di costo c1,…,ckc_1, \dots, c_k, si applica la consistenza a ogni passo e si somma: h(n0)≤c1+h(n1)≤c1+c2+h(n2)≤⋯≤c1+⋯+ck+h(G)=h∗(n),h(n_0) \le c_1 + h(n_1) \le c_1 + c_2 + h(n_2) \le \dots \le c_1 + \dots + c_k + h(G) = h^\ast(n), perché h(G)=0h(G) = 0. Non vale il viceversa in generale.

La distanza in linea retta è ammissibile: nessuna strada è più corta della retta. È anche consistente: per esempio Arad →\to Sibiu: h(Arad)=366≤140+253=393=c+h(Sibiu)h(\text{Arad}) = 366 \le 140 + 253 = 393 = c + h(\text{Sibiu}); Sibiu →\to Rimnicu Vilcea: 253≤80+193=273253 \le 80 + 193 = 273. Su tutte le 23 strade della mappa, nei due versi, la disuguaglianza è verificata con un programma.

Ottimalità di A∗

Teorema. Se hh è ammissibile, A∗ (versione ad albero) è ottima. Se hh è consistente, anche la versione su grafo con tabella dei raggiunti è ottima.

Dimostrazione (versione ad albero). Sia GsG_s un obiettivo non ottimo già generato, e nn un nodo non espanso su un cammino ottimo verso l'obiettivo ottimo GoG_o. Allora f(Gs)=g(Gs)percheˊ h(Gs)=0>g(Go)percheˊ Gs non eˋ ottimo=g(n)+h∗(n)(h∗ eˋ il costo vero da n)≥g(n)+h(n)percheˊ h eˋ ammissibile=f(n).\begin{aligned} f(G_s) &= g(G_s) && \text{perché } h(G_s) = 0\\ &> g(G_o) && \text{perché } G_s \text{ non è ottimo}\\ &= g(n) + h^\ast(n) && \text{(} h^\ast \text{ è il costo vero da } n \text{)}\\ &\ge g(n) + h(n) && \text{perché } h \text{ è ammissibile}\\ &= f(n). \end{aligned} Quindi f(Gs)>f(n)f(G_s) > f(n): A∗ non seleziona mai GsG_s finché esiste un nodo come nn sul cammino ottimo, dunque il primo obiettivo estratto è ottimo. □\square

Con la consistenza, inoltre, ff non decresce mai lungo un cammino: f(n′)=g(n)+c+h(n′)≥g(n)+h(n)=f(n)f(n') = g(n) + c + h(n') \ge g(n) + h(n) = f(n). A∗ espande quindi i nodi in ordine non decrescente di ff e, quando un nodo è espanso per la prima volta, ha già il costo gg minimo: la tabella dei raggiunti non perde soluzioni ottime. Le espansioni formano i contorni ff: il contorno ii contiene i nodi con f=fif = f_i, fi<fi+1f_i < f_{i+1}. A differenza dei contorni dell'ampiezza (cerchi attorno alla partenza), quelli di A∗ si allungano verso l'obiettivo; più hh è accurata, più sono stretti.

Proprietà:

  • Completa: sì, a meno di infiniti nodi con f≤f(G)f \le f(G).
  • Tempo: O(bd)O(b^d) nel caso peggiore, ma dipende da quanto è buona hh.
  • Spazio: O(bd)O(b^d), tiene in memoria tutti i nodi.
  • Ottima: sì, non si passa al contorno fi+1f_{i+1} prima di aver finito fif_i.

A∗ pesato

Un'euristica che sovrastima (non ammissibile) può far perdere la soluzione ottima, ma può espandere molti meno nodi. A∗ pesato (weighted A∗): f(n)=g(n)+W⋅h(n),1<W<∞.f(n) = g(n) + W \cdot h(n), \qquad 1 < W < \infty.

È una generalizzazione di tutte le strategie viste:

WW f(n)f(n) strategia
00 g(n)g(n) costo uniforme (Dijkstra)
11 g(n)+h(n)g(n) + h(n) A∗
1<W<∞1 < W < \infty g(n)+Wh(n)g(n) + W h(n) A∗ pesato
W→∞W \to \infty h(n)h(n) greedy best-first

Nelle slide, in un esempio di pianificazione di percorso, con W=2W = 2 lo spazio di ricerca si riduce di un fattore 7 con un costo finale maggiore del 5%. Sul puzzle dell'8 dell'esempio sotto (soluzione ottima di 26 mosse), con h2h_2: W=1W = 1 genera 6130 nodi; W=1,4W = 1{,}4 (valore predefinito del laboratorio) trova ancora 26 mosse con 1202 nodi; W=2W = 2 trova 30 mosse con 638 nodi, cioè circa 10 volte meno nodi con il 15% di cammino in più. Sulla Romania da Arad a Bucarest, già con W=1,4W = 1{,}4 il cammino diventa quello del greedy (costo 450 invece di 418).

Qualità delle euristiche

Fattore di diramazione effettivo

Un modo di caratterizzare la qualità di un'euristica è il fattore di diramazione effettivo b∗b^\ast (effective branching factor). Se A∗ genera in totale NN nodi per un problema con soluzione a profondità dd, b∗b^\ast è il fattore di diramazione di un albero uniforme di profondità dd con N+1N + 1 nodi: N+1=1+b∗+(b∗)2+⋯+(b∗)d.N + 1 = 1 + b^\ast + (b^\ast)^2 + \dots + (b^\ast)^d . Si misura sperimentalmente su un campione di problemi, e un'euristica ben progettata ha b∗b^\ast vicino a 1. L'equazione si risolve numericamente (non ha formula chiusa per dd grande).

Esempio. N=12N = 12, d=2d = 2: 13=1+b∗+(b∗)213 = 1 + b^\ast + (b^\ast)^2, cioè (b∗)2+b∗−12=0(b^\ast)^2 + b^\ast - 12 = 0, da cui b∗=3b^\ast = 3. Con N=24N = 24 e d=6d = 6 la risoluzione numerica dà b∗≈1,42b^\ast \approx 1{,}42 (riga d=6d = 6 della tabella delle slide, più sotto).

Il puzzle dell'8

Due euristiche per il puzzle dell'8:

  • h1(n)h_1(n) = numero di tessere fuori posto;
  • h2(n)h_2(n) = distanza di Manhattan totale: per ogni tessera, il numero di caselle (righe più colonne) che la separano dalla posizione finale.

Lo stato SS delle slide ha righe 7 2 4 / 5 _ 6 / 8 3 17\,2\,4 \,/\, 5\,\_\,6 \,/\, 8\,3\,1 e l'obiettivo è _ 1 2 / 3 4 5 / 6 7 8\_\,1\,2 \,/\, 3\,4\,5 \,/\, 6\,7\,8. Con (riga, colonna) da (0,0)(0,0) in alto a sinistra:

tessera 1 2 3 4 5 6 7 8
posizione in SS (2,2)(2,2) (0,1)(0,1) (2,1)(2,1) (0,2)(0,2) (1,0)(1,0) (1,2)(1,2) (0,0)(0,0) (2,0)(2,0)
posizione finale (0,1)(0,1) (0,2)(0,2) (1,0)(1,0) (1,1)(1,1) (1,2)(1,2) (2,0)(2,0) (2,1)(2,1) (2,2)(2,2)
distanza 2+1=32 + 1 = 3 0+1=10 + 1 = 1 1+1=21 + 1 = 2 1+1=21 + 1 = 2 0+2=20 + 2 = 2 1+2=31 + 2 = 3 2+1=32 + 1 = 3 0+2=20 + 2 = 2

Tutte le 8 tessere sono fuori posto: h1(S)=8h_1(S) = 8. E h2(S)=3+1+2+2+2+3+3+2=18.h_2(S) = 3 + 1 + 2 + 2 + 2 + 3 + 3 + 2 = 18. Entrambe sono ammissibili: ogni mossa sposta una tessera di una sola casella, quindi servono almeno h2h_2 mosse (e almeno h1h_1).

Dati medi delle slide (100 puzzle per ogni lunghezza dd della soluzione):

dd nodi BFS nodi A∗(h1h_1) nodi A∗(h2h_2) b∗b^\ast BFS b∗b^\ast A∗(h1h_1) b∗b^\ast A∗(h2h_2)
6 128 24 19 2,01 1,42 1,34
10 1033 116 48 1,85 1,43 1,27
14 6783 678 174 1,77 1,47 1,31
20 91493 9905 1318 1,69 1,50 1,34
28 463234 202565 22055 1,53 1,49 1,36

Sullo stato SS (soluzione ottima di 26 mosse), con un programma che genera i nodi con la tabella dei raggiunti: ampiezza 177 809 nodi (b∗≈1,53b^\ast \approx 1{,}53), A∗(h1h_1) 62 570 (b∗≈1,46b^\ast \approx 1{,}46), A∗(h2h_2) 6 130 (b∗≈1,33b^\ast \approx 1{,}33).

Dominanza. Se h2(n)≥h1(n)h_2(n) \ge h_1(n) per ogni nn (entrambe ammissibili), h2h_2 domina h1h_1 ed è migliore: A∗ con h2h_2 espande al più i nodi che espande con h1h_1. Perché A∗ espande ogni nodo con f(n)<C∗f(n) < C^\ast, cioè con h(n)<C∗−g(n)h(n) < C^\ast - g(n): con hh più grande la condizione è soddisfatta da meno nodi.

Qui h2≥h1h_2 \ge h_1 perché una tessera fuori posto dista almeno una casella. Un'euristica ammissibile si può ottenere dal costo esatto di un problema rilassato (con meno vincoli): h1h_1 è il costo esatto se ogni tessera può saltare dovunque in una mossa, h2h_2 se può muoversi di una casella anche su una casella occupata. Tra più euristiche ammissibili conviene il massimo (ammissibile e dominante su tutte).

Il pancake sorting del laboratorio e l'euristica del gap

Nel laboratorio 3 (extra), Esercizio - Route finding sulla Romania e pancake sorting con euristica del gap (laboratorio 3), si ordina una pila di frittelle con una spatola che inverte le prime ii frittelle (stato = tupla delle taglie dall'alto in basso). L'euristica del gap conta le coppie di frittelle vicine con taglie che differiscono per più di 1: sono coppie che andranno separate. h(s)=∣{ i:∣si−si−1∣>1 }∣.h(s) = \bigl|\{\,i : |s_i - s_{i-1}| > 1\,\}\bigr| . Per (2,1,4,6,3,5)(2, 1, 4, 6, 3, 5): coppie (2,1)(2,1) no, (1,4)(1,4) sì, (4,6)(4,6) sì, (6,3)(6,3) sì, (3,5)(3,5) sì: h=4h = 4. A∗ trova una soluzione ottima di 5 inversioni. Ogni inversione cambia al più un'adiacenza, quindi hh non sovrastima; con una ricerca esaustiva (ampiezza all'indietro dall'obiettivo) risulta ammissibile e consistente per tutte le permutazioni con n≤7n \le 7.

Ricerca locale e problemi di ottimizzazione

In molti problemi il cammino è irrilevante: la soluzione è lo stato obiettivo stesso. Lo spazio degli stati è l'insieme delle configurazioni "complete" e si cerca la configurazione ottima (come nel TSP) o che soddisfa dei vincoli (un orario). Si usano algoritmi di miglioramento iterativo: si tiene un solo stato "corrente" e si prova a migliorarlo.

Gli algoritmi di ricerca locale partono da uno stato e passano a stati vicini, senza tenere traccia dei cammini né degli stati già raggiunti. Usano poca memoria, ma possono non esplorare mai la parte dello spazio in cui sta la soluzione. Esempi:

  • TSP: si parte da un giro completo qualsiasi e si fanno scambi a coppie di lati; i metodi locali arrivano entro l'1% dall'ottimo molto in fretta anche con migliaia di città;
  • nn regine: nn regine su una scacchiera n×nn \times n senza due sulla stessa riga, colonna o diagonale; si sposta una regina alla volta per ridurre i conflitti; si risolve quasi istantaneamente anche per nn molto grandi.

Paesaggio degli stati e hill climbing

Conviene pensare al paesaggio dello spazio degli stati (state space landscape): ogni stato ha un valore (da massimizzare), e ricerca locale significa salire sul paesaggio.

Grafico interattivo: Paesaggio con più massimi locali: da x = 0,5 la salita si ferma in x = 0,74 (valore 1,18); il massimo globale è in x = 8,76 (valore 2,39)

Definizione (hill climbing). L'ascesa del colle (hill climbing, anche greedy local search) parte da uno stato e passa sempre al vicino di valore più alto; si ferma quando nessun vicino è migliore e restituisce lo stato corrente, un massimo locale.

function Hill-Climbing(problem) returns a state that is a local maximum
    current <- Make-Node(Initial-State[problem])
    loop do
        neighbor <- a highest-valued successor of current
        if Value[neighbor] <= Value[current] then return State[current]
        current <- neighbor

Problemi: si ferma sui massimi locali (stati migliori di tutti i vicini ma non il migliore in assoluto) e sui plateau (zone piatte: se sono un massimo locale piatto la ricerca non sa dove andare, se sono una spalla si può uscire). Rimedi:

  • mosse laterali casuali (sideways moves): permettono di uscire dalle spalle, ma possono girare all'infinito sui massimi piatti, quindi servono con un limite;
  • hill climbing con riavvii casuali (random restart): si rilancia da stati iniziali casuali; è banalmente completo, perché prima o poi parte vicino alla soluzione.

Esempio: 8 regine. Stato = una regina per colonna (la riga di ciascuna), valore −h-h con hh = numero di coppie di regine che si attaccano; vicini = muovere una regina dentro la sua colonna (8×7=568 \times 7 = 56 vicini). Lo stato della slide ha le regine nelle righe (4,5,6,3,4,5,6,5)(4, 5, 6, 3, 4, 5, 6, 5) (colonne da sinistra, righe dall'alto a partire da 0) e h=17h = 17. La slide mostra hh per ognuno dei vicini: il minimo è h=12h = 12, raggiunto da 8 mosse; l'ascesa ne sceglie una. Una configurazione come quella con le regine nelle righe (7,2,6,3,1,4,0,5)(7, 2, 6, 3, 1, 4, 0, 5) ha h=1h = 1 (l'unica coppia che si attacca è quella cerchiata nella slide) ma tutti i suoi vicini hanno h≥2h \ge 2: è un minimo locale in cui la ricerca si blocca.

I numeri dell'esempio si verificano con questo codice:

python
import random, math

def h(q):                      # q[c] = riga della regina nella colonna c; coppie di regine che si attaccano
    return sum(q[i] == q[j] or abs(q[i] - q[j]) == j - i
               for i in range(8) for j in range(i + 1, 8))

def successori(q):             # muovere una regina dentro la sua colonna: 8 * 7 = 56 vicini
    return [q[:c] + [r] + q[c + 1:] for c in range(8) for r in range(8) if r != q[c]]

def hill_climbing(q):
    passi = 0
    while True:
        migliore = min(h(n) for n in successori(q))
        if migliore >= h(q):                      # nessun vicino migliore: minimo locale
            return q, passi
        q = random.choice([n for n in successori(q) if h(n) == migliore])
        passi += 1

q = [4, 5, 6, 3, 4, 5, 6, 5]                      # stato della slide
print(h(q), min(h(n) for n in successori(q)), sum(h(n) == 12 for n in successori(q)))   # 17 12 8

Da 2000 stati iniziali casuali (random.seed(0)) l'ascesa più ripida arriva a h=0h = 0 nel 13,8% dei casi, in circa 4,1 passi; nel resto dei casi si blocca. Con i riavvii servono in media 1/0,138≈71 / 0{,}138 \approx 7 lanci, e lo spazio di 88≈1,7⋅1078^8 \approx 1{,}7 \cdot 10^7 stati si risolve in un attimo.

Simulated annealing

Idea. Uscire dai massimi locali ammettendo qualche mossa "cattiva", con una dimensione e una frequenza che calano gradualmente. Come l'hill climbing, ma si sceglie un successore casuale e lo si accetta se migliora; se peggiora lo si accetta con una probabilità che decresce con il peggioramento ∣ΔE∣\lvert\Delta E\rvert e con una temperatura TT decrescente.

Con ΔE=Value[next]−Value[current]\Delta E = \text{Value}[next] - \text{Value}[current], quindi ΔE<0\Delta E < 0 per una mossa peggiorativa, la probabilità di accettarla è p=eΔE/T=e−∣ΔE∣/T∈(0,1).p = e^{\Delta E / T} = e^{-\lvert\Delta E\rvert/T} \in (0, 1) .

(Nel pseudocodice delle slide compare e−ΔE/Te^{-\Delta E/T} con ΔE\Delta E = nuovo meno vecchio: per una mossa peggiorativa darebbe un valore maggiore di 1. Nella formula p∝e−ΔE(x)/Tp \propto e^{-\Delta E(x)/T} la ΔE(x)\Delta E(x) è invece il peggioramento preso positivo. Il segno corretto è quello scritto qui sopra.)

function Simulated-Annealing(problem, schedule) returns a solution state
    current <- Make-Node(Initial-State[problem])
    for t <- 1 to infinity do
        T <- schedule[t]
        if T = 0 then return current
        next <- a randomly selected successor of current
        DE <- Value[next] - Value[current]
        if DE > 0 then current <- next
        else current <- next only with probability e^(DE/T)

Come si comporta la probabilità p=e−∣ΔE∣/Tp = e^{-\lvert\Delta E\rvert/T}:

peggioramento ∣ΔE∣\lvert\Delta E\rvert T=2T = 2 T=1T = 1 T=0,5T = 0{,}5 T=0,1T = 0{,}1
1 0,6070{,}607 0,3680{,}368 0,1350{,}135 ≈4,5⋅10−5\approx 4{,}5 \cdot 10^{-5}
3 0,2230{,}223 0,0500{,}050 0,00250{,}0025 ≈10−13\approx 10^{-13}

(Per esempio e−1/2=0,607e^{-1/2} = 0{,}607 e e−3/0,5=e−6=0,0025e^{-3/0{,}5} = e^{-6} = 0{,}0025.) A temperatura alta quasi ogni mossa è accettata (esplorazione, come una passeggiata casuale); a temperatura bassa solo i miglioramenti (come hill climbing). Se TT cala abbastanza lentamente, l'algoritmo raggiunge lo stato migliore con probabilità →1\to 1. È usato nel disegno di circuiti VLSI, nella pianificazione di voli, ecc.

Grafico interattivo: Probabilità di accettare una mossa peggiorativa di entità x: con T = 2 passano anche peggioramenti grandi, con T = 0,5 quasi solo quelli piccoli

python
def simulated_annealing(q, T=2.0, alpha=0.995, Tmin=1e-3):
    while T > Tmin:
        if h(q) == 0: return q
        c, r = random.randrange(8), random.randrange(8)
        n = q[:c] + [r] + q[c + 1:]
        dE = h(q) - h(n)                          # valore = -h: dE > 0 se n è migliore
        if dE > 0 or random.random() < math.exp(dE / T):
            q = n
        T *= alpha
    return q

Qui il piano di raffreddamento è Tt=2⋅0,995tT_t = 2 \cdot 0{,}995^t (circa 1500 passi fino a T<10−3T < 10^{-3}). Su 500 partenze casuali (random.seed(0)) trova una soluzione delle 8 regine nell'81,6% dei casi, contro il 13,8% dell'hill climbing senza riavvii.

Si parte da kk stati invece di uno e a ogni passo si tengono i kk migliori successori di tutti. Non equivale a kk riavvii indipendenti: ogni selezione influenza la successiva, e le ricerche che trovano stati buoni "reclutano" le altre. Problema: spesso tutti i kk stati finiscono sulla stessa collina. Rimedio: stochastic beam search, che sceglie i kk successori a caso con probabilità maggiore per i migliori (analogia con la selezione naturale).

Algoritmi genetici

Simili alla stochastic beam search, ma i successori si generano da coppie di stati. Ispirati alla selezione naturale: una popolazione di individui (stati) in cui i più adatti (fitness più alta) producono prole (stati successori) che popola la generazione successiva, con un processo detto ricombinazione. Gli stati devono essere codificati come stringhe. Tre passi:

  • selezione: si scelgono i genitori con probabilità proporzionale alla fitness;
  • ricombinazione (crossover): si sceglie a caso un punto di taglio, si spezzano le due stringhe genitori e si ricombinano le parti per formare due figli;
  • mutazione: si cambia a caso qualche elemento dei figli, con una certa probabilità (tasso di mutazione).

Esempio: 8 regine. Individuo = stringa di 8 cifre (la riga di ogni colonna). Fitness =28−h= 28 - h = coppie che non si attaccano (28=(82)28 = \binom{8}{2} è il massimo). Popolazione di 4: (4,5,6,3,4,5,6,5)(4,5,6,3,4,5,6,5) con fitness 1111; (7,2,6,3,1,4,0,5)(7,2,6,3,1,4,0,5) con 2727; (0,2,4,6,1,3,5,7)(0,2,4,6,1,3,5,7) con 2727; (3,3,3,3,3,3,3,3)(3,3,3,3,3,3,3,3) con 00. Somma 6565: probabilità di selezione 0,1690{,}169, 0,4150{,}415, 0,4150{,}415, 00. Crossover tra il secondo e il primo genitore al punto 3: (7,2,6∣3,1,4,0,5)(7,2,6 \mid 3,1,4,0,5) e (4,5,6∣3,4,5,6,5)(4,5,6 \mid 3,4,5,6,5) danno i figli (7,2,6,3,4,5,6,5)(7,2,6,3,4,5,6,5) con fitness 1818 e (4,5,6,3,1,4,0,5)(4,5,6,3,1,4,0,5) con fitness 2020.

Qui i figli non sono migliori del miglior genitore: l'algoritmo funziona perché, su molte generazioni, selezione e ricombinazione combinano blocchi buoni e la mutazione mantiene la diversità. Varianti: strategie evolutive, programmazione genetica.

Ricerca avversaria (oltre le slide)

Le slide del corso non trattano i giochi a due giocatori; si dà qui il minimo di base. In un gioco a somma zero con due giocatori che si alternano, MAX (che vuole il valore più alto) e MIN (il più basso), il valore minimax di un nodo è l'utilità del nodo se è terminale, il massimo dei valori dei figli se tocca a MAX, il minimo se tocca a MIN. La mossa di MAX è quella verso il figlio di valore massimo.

Esempio. Radice MAX con tre figli MIN di foglie (4,9,6)(4, 9, 6), (7,5,8)(7, 5, 8), (3,1,10)(3, 1, 10). I valori dei figli sono min⁡=4\min = 4, 55, 11; la radice vale max⁡(4,5,1)=5\max(4, 5, 1) = 5: MAX gioca verso il secondo figlio.

Potatura alfa-beta: si tengono α\alpha (miglior valore garantito a MAX finora) e β\beta (il migliore per MIN) e si smette di esplorare un nodo quando non può cambiare la decisione. Nell'esempio: il primo figlio dà 44, α=4\alpha = 4; il secondo, con le foglie 77, 55, 88, vale 55, α=5\alpha = 5; nel terzo la prima foglia è 3≤α=53 \le \alpha = 5, quindi il figlio vale al più 33 e non può battere 55: le foglie 11 e 1010 non si valutano. Il risultato (radice =5= 5) è lo stesso con 77 foglie valutate su 99 (verificato con un programma).

Domande d'esame

  1. Da Arad a Bucarest con hSLDh_{SLD}: quali città espande A∗ e in quale ordine? Che costo ha la soluzione? Arad (366), Sibiu (393), Rimnicu Vilcea (413), Fagaras (415), Pitesti (417), Bucarest (418). Soluzione Arad, Sibiu, Rimnicu Vilcea, Pitesti, Bucarest, costo 418.
  2. Per lo stato SS del puzzle dell'8 delle slide calcolare h1h_1 e h2h_2. h1=8h_1 = 8, h2=3+1+2+2+2+3+3+2=18h_2 = 3 + 1 + 2 + 2 + 2 + 3 + 3 + 2 = 18.
  3. A∗ genera N=12N = 12 nodi per una soluzione a profondità d=2d = 2: quanto vale b∗b^\ast? 13=1+b∗+(b∗)213 = 1 + b^\ast + (b^\ast)^2, b∗=3b^\ast = 3.
  4. Perché un'euristica consistente è ammissibile? Vale il contrario? Si sommano le disuguaglianze h(ni)≤ci+1+h(ni+1)h(n_i) \le c_{i+1} + h(n_{i+1}) lungo il cammino ottimo fino all'obiettivo, dove h=0h = 0: h(n)≤h∗(n)h(n) \le h^\ast(n). Il contrario non vale in generale.
  5. Quale valore di WW rende A∗ pesato uguale al greedy? E al costo uniforme? W→∞W \to \infty (cioè f=hf = h); W=0W = 0 (cioè f=gf = g).
  6. Probabilità di accettare con il simulated annealing una mossa con ΔE=−2\Delta E = -2 a T=1T = 1 e a T=4T = 4. e−2≈0,135e^{-2} \approx 0{,}135 e e−1/2≈0,607e^{-1/2} \approx 0{,}607.
  7. Perché l'hill climbing può non trovare una soluzione e come si rimedia? Si blocca su massimi locali e plateau; si rimedia con riavvii casuali, mosse laterali o simulated annealing.

Versione ripasso

  • Funzione euristica h(n)h(n): stima del costo da nn all'obiettivo; la frontiera è una coda con priorità per desiderabilità (Ricerca nello spazio degli stati - formulazione dei problemi e ricerca non informataUn agente risolutore di problemi è un agente basato su obiettivi con rappresentazione atomica: formula un problema (stato iniziale, azioni, modello di transizione, test dell'obiettivo, costo dei passi) e cerca una sequenza di azioni che porta a uno stato obiettivo costruendo un albero di ricerca. Le strategie non informate usano solo la definizione del problema e si distinguono per l'ordine di espansione della frontiera: ampiezza (coda FIFO), costo uniforme (coda con priorità sul costo g), profondità (pila LIFO), profondità limitata, approfondimento iterativo e ricerca bidirezionale. Si confrontano per completezza, ottimalità, tempo e spazio in funzione di b, d, m; tutte sono casi di best-first search con una diversa funzione di valutazione f.Ricerca nello spazio degli stati - formulazione dei problemi e ricerca non informata →). Romania: hSLDh_{SLD} (distanza in linea retta da Bucarest): Arad 366, Sibiu 253, Fagaras 176, Rimnicu Vilcea 193, Pitesti 100, Timisoara 329, Zerind 374, Oradea 380.
  • Greedy best-first: f=hf = h. Arad →\to Bucarest: Arad, Sibiu, Fagaras, Bucarest, costo 450 (4 espansioni, ottimo 418). Completa solo in spazi finiti senza cicli, tempo e spazio O(bm)O(b^m), non ottima.
  • A∗: f=g+hf = g + h (gg costo speso, hh stima restante, ff stima totale per nn). Arad →\to Bucarest: Arad 366; Sibiu 140+253=393140 + 253 = 393; Rimnicu Vilcea 220+193=413220 + 193 = 413; Fagaras 239+176=415239 + 176 = 415; Pitesti 317+100=417317 + 100 = 417; Bucarest via Fagaras 450450, poi via Pitesti 418418 lo sostituisce. Costo 418, 6 espansioni (UCS ne fa 13). Codice: best_first_search(problem, f=lambda n: g(n) + h(n)).
  • Ammissibile: h≥0h \ge 0, h(G)=0h(G) = 0, h(n)≤h∗(n)h(n) \le h^\ast(n) (non sovrastima). Consistente: h(n)≤c(n,a,n′)+h(n′)h(n) \le c(n, a, n') + h(n') (disuguaglianza triangolare); consistente ⇒\Rightarrow ammissibile (si somma lungo il cammino ottimo, h(G)=0h(G) = 0), non viceversa. hSLDh_{SLD} è consistente: 366≤140+253366 \le 140 + 253, 253≤80+193253 \le 80 + 193.
  • Ottimalità di A∗ (ammissibile, albero): f(Gs)=g(Gs)>g(Go)=g(n)+h∗(n)≥g(n)+h(n)=f(n)f(G_s) = g(G_s) > g(G_o) = g(n) + h^\ast(n) \ge g(n) + h(n) = f(n), quindi GsG_s non viene mai scelto. Con hh consistente ff non decresce lungo i cammini, A∗ espande in ordine di ff (contorni che si allungano verso l'obiettivo) e il primo gg di un nodo è il minimo. Completa (se non ci sono infiniti nodi con f≤f(G)f \le f(G)), tempo e spazio O(bd)O(b^d), ottima.
  • A∗ pesato: f=g+Whf = g + W h, 1<W<∞1 < W < \infty: può perdere l'ottimo ma espande meno nodi. W=0W = 0 costo uniforme, W=1W = 1 A∗, W→∞W \to \infty greedy. Slide: W=2W = 2 riduce lo spazio di un fattore 7 con il 5% di costo in più. Puzzle dell'8 (stato SS, h2h_2): W=1W = 1 dà 26 mosse e 6130 nodi, W=1,4W = 1{,}4 dà 26 mosse e 1202 nodi, W=2W = 2 dà 30 mosse e 638 nodi.
  • Fattore di diramazione effettivo: N+1=1+b∗+(b∗)2+⋯+(b∗)dN + 1 = 1 + b^\ast + (b^\ast)^2 + \dots + (b^\ast)^d (NN nodi generati, dd profondità della soluzione); vicino a 1 è buono. N=12N = 12, d=2d = 2: b∗=3b^\ast = 3.
  • Puzzle dell'8: h1h_1 tessere fuori posto, h2h_2 distanza di Manhattan; SS (7 2 4 / 5 _ 6 / 8 3 17\,2\,4 \,/\, 5\,\_\,6 \,/\, 8\,3\,1): h1=8h_1 = 8, h2=3+1+2+2+2+3+3+2=18h_2 = 3 + 1 + 2 + 2 + 2 + 3 + 3 + 2 = 18 (tessere 1..8). Dominanza: h2≥h1h_2 \ge h_1 implica che A∗(h2h_2) espande al più i nodi di A∗(h1h_1) (espande f<C∗f < C^\ast). Su SS: BFS 177 809, A∗(h1h_1) 62 570, A∗(h2h_2) 6 130 nodi (b∗b^\ast 1,53; 1,46; 1,33). Euristiche da problemi rilassati; tra più ammissibili si prende il massimo. Tabella delle slide: per d=6d = 6 BFS 128, A∗(h1h_1) 24, A∗(h2h_2) 19 nodi.
  • Pancake, euristica del gap: coppie vicine con taglie che differiscono per più di 1; (2,1,4,6,3,5)(2,1,4,6,3,5) ha h=4h = 4, ottimo 5 inversioni; ammissibile e consistente per n≤7n \le 7 (Esercizio - Route finding sulla Romania e pancake sorting con euristica del gap (laboratorio 3)).
  • Ricerca locale: conta lo stato, non il cammino; un solo stato corrente, poca memoria, ma può non esplorare la soluzione; paesaggio con massimi locali, plateau e spalle. TSP con scambi a coppie, nn regine spostando una regina per volta.
  • Hill climbing: passa al vicino migliore, si ferma in un massimo locale. Rimedi: mosse laterali (con un limite), riavvii casuali (banalmente completo). 8 regine: (4,5,6,3,4,5,6,5)(4,5,6,3,4,5,6,5) ha h=17h = 17, 56 vicini, minimo h=12h = 12 in 8 mosse; minimo locale h=1h = 1 con vicini h≥2h \ge 2; da stati casuali risolve il 13,8% in circa 4,1 passi, quindi circa 7 riavvii.
  • Simulated annealing: successore casuale, accettato se migliora, altrimenti con probabilità eΔE/T=e−∣ΔE∣/Te^{\Delta E / T} = e^{-\lvert\Delta E\rvert/T} (ΔE<0\Delta E < 0); TT cala con un piano di raffreddamento; con TT che scende abbastanza piano raggiunge l'ottimo con probabilità →1\to 1. Temperatura alta: esplora; bassa: hill climbing. e−1/2=0,607e^{-1/2} = 0{,}607, e−1=0,368e^{-1} = 0{,}368, e−6=0,0025e^{-6} = 0{,}0025. Sulle 8 regine (Tt=2⋅0,995tT_t = 2 \cdot 0{,}995^t) risolve l'81,6% dei casi. Il segno nel pseudocodice delle slide va letto così.
  • Local beam search: kk stati, si tengono i kk migliori successori comuni (le ricerche si "reclutano"); rischio: tutti sulla stessa collina; stochastic beam search sceglie a caso con bias verso i migliori.
  • Algoritmi genetici: popolazione di stringhe, selezione con probabilità proporzionale alla fitness, crossover a un punto di taglio, mutazione. 8 regine: fitness 28−h28 - h; (7,2,6,3,1,4,0,5)(7,2,6,3,1,4,0,5) e (4,5,6,3,4,5,6,5)(4,5,6,3,4,5,6,5) al taglio 3 danno figli con fitness 18 e 20.
  • Minimax e alfa-beta (oltre le slide): MAX prende il massimo, MIN il minimo; foglie (4,9,6)(4,9,6), (7,5,8)(7,5,8), (3,1,10)(3,1,10) danno 4,5,14, 5, 1 e radice 5; con la potatura le foglie 11 e 1010 non si valutano (77 foglie su 99).
  • Greedy e A∗ a confronto sulla Romania (Arad →\to Bucarest): greedy 4 espansioni, costo 450; A∗ 6 espansioni, costo 418; costo uniforme 13 espansioni, costo 418; con W=1,4W = 1{,}4 o 22 il cammino torna quello del greedy (450).
  • Schemi di codice: hill_climbing: migliore = min(h(n) for n in successori(q)), se migliore >= h(q) si ferma, altrimenti si sceglie a caso tra i migliori. simulated_annealing: n successore casuale, dE = h(q) - h(n) (valore =−h= -h), si accetta se dE > 0 or random.random() < math.exp(dE / T), poi T *= alpha (qui T0=2T_0 = 2, α=0,995\alpha = 0{,}995).
  • Accettazione nel simulated annealing: peggioramento 1: 0,6070{,}607 (T=2T = 2), 0,3680{,}368 (T=1T = 1), 0,1350{,}135 (T=0,5T = 0{,}5); peggioramento 3: 0,2230{,}223, 0,0500{,}050, 0,00250{,}0025. A TT bassa la probabilità di una mossa cattiva diventa trascurabile.
  • Ricerca avversaria: valore minimax di un nodo terminale = utilità, di un nodo MAX = massimo dei figli, di un nodo MIN = minimo dei figli; alfa-beta tiene α\alpha (miglior valore garantito a MAX) e β\beta (a MIN) e taglia quando v≤αv \le \alpha in un nodo MIN o v≥βv \ge \beta in un nodo MAX.
  • Errori tipici: usare f=hf = h e aspettarsi l'ottimo; dimenticare che A∗ chiede hh ammissibile (e consistente per la ricerca su grafo); confondere b∗b^\ast con il fattore di diramazione massimo; scrivere il segno sbagliato nella probabilità del simulated annealing; credere che l'hill climbing sia completo.

Esercizi su questo argomento

Teoria collegata