Les dues lliçons anteriors cercaven i ordenaven valors en col·leccions. Aquesta lliçó tanca el mòdul amb una cerca d'una altra naturalesa: trobar una seqüència de decisions que porti d'una situació inicial a un objectiu — la ruta d'una furgoneta de Rutalia per una quadrícula amb carrers tallats, la planificació d'un repartiment amb restriccions. El marc formal es diu espai d'estats, i és el punt on conflueixen diverses idees que ja tens: els grafs implícits de 03-01, el BFS/DFS de 03-02, el Dijkstra de 03-03 i els arbres d'exploració del backtracking de 02-03. Aquí els donarem un sostre comú i hi afegirem la peça promesa des del mòdul 3: A*, el "Dijkstra amb brúixola", que usa una heurística per expandir moltíssims menys nodes. Compararem experimentalment Dijkstra contra A* i aclarirem el mapa mental complet de tècniques d'exploració del curs.

Contingut

  1. Què és un espai d'estats: estat, operadors, objectiu, cost
  2. Espais d'estats vs. grafs explícits: l'implícit i el gegant
  3. BFS i DFS sobre espais implícits: la quadrícula de 01-03 revisitada
  4. Cerca de cost uniforme: Dijkstra sense graf
  5. A*: heurístiques, admissibilitat i consistència
  6. Implementació completa: ruta de repartiment en quadrícula amb obstacles
  7. Comparació experimental: nodes expandits amb brúixola i sense
  8. Heurístiques no admissibles, A* ponderat i IDA* (breu)
  9. El mapa mental: cerca de camins vs. optimització exacta (02-03)

Què és un espai d'estats

Un espai d'estats és una manera de plantejar un problema com a exploració. Es defineix amb quatre peces:

Peça Definició Exemple Rutalia (repartiment en quadrícula)
Estat Una "foto" completa de la situació en un instant La casella (fila, col) on és la furgoneta
Operadors Accions que transformen un estat en d'altres (funció successors) Moure's N/S/E/O a una casella adjacent no bloquejada
Estat inicial i prova d'objectiu D'on partim i com reconèixer que hi hem arribat El dipòsit (0, 0); som al client (9, 9)?
Cost El que costa aplicar cada operador 1 minut per casella (o més si el carrer està congestionat)

Una solució és un camí d'operadors de l'estat inicial a un estat objectiu; una solució òptima és la de cost mínim. Fixa't que l'estat pot ser molt més ric que una posició: si la furgoneta ha de recollir i lliurar, l'estat podria ser (posició, paquets_a_bord); si hi ha finestres horàries, (posició, temps). Triar quina informació entra a l'estat és la decisió de disseny més important: tot el que afecti els moviments futurs ha de ser-hi dins; tot el que no, fora (o l'espai explota sense necessitat).

Espais d'estats vs. grafs explícits

Un espai d'estats és un graf: els estats són vèrtexs i els operadors, arestes ponderades. Llavors, què hi ha de nou respecte al mòdul 3? La diferència és de representació i d'escala, i ja la vam anticipar a 03-01 en parlar de grafs implícits:

  • Graf explícit (la xarxa de 9 zones de Rutalia): cap sencer en memòria com a llista d'adjacència; podem iterar-lo, precalcular Floyd-Warshall 9×9, dibuixar-lo.
  • Graf implícit (un espai d'estats): només existeix la funció successors(estat). El graf complet pot tenir 10²⁰ estats — mai no el materialitzarem. L'única cosa que podem fer és generar-lo sobre la marxa a partir de l'estat inicial, i la pregunta central passa a ser: quants estats necessitem tocar abans de trobar l'objectiu?

Aquesta pregunta — nodes generats i expandits, no vèrtexs totals — és la mètrica d'aquesta lliçó. Amb un factor de ramificació b (successors per estat) i una solució a profunditat d, una exploració cega toca de l'ordre de b^d estats. Tot el que segueix és la lluita per reduir aquest nombre.

graph LR
    subgraph "Graf explicit (modul 3)"
        A((ALM)) --- B((MER)) --- C((CEN))
        A --- C
    end
    subgraph "Espai d'estats (implicit)"
        S["estat inicial"] --> S1["successor 1"]
        S --> S2["successor 2"]
        S1 --> D1["..."]
        S2 --> D2["... b^d estats que MAI no es materialitzen sencers"]
    end

BFS i DFS sobre espais implícits

Els algorismes de 03-02 funcionen sense canvis: mai no van exigir tenir el graf sencer, només demanar veïns. La quadrícula del cost mínim de 01-03 — que a 03-01 ja vam reconèixer com un graf — és el nostre espai d'estats de joguina: la vam resoldre amb programació dinàmica perquè només es podia anar a la dreta i avall; ara la furgoneta es mou en les quatre direccions i hi ha obstacles, així que la PD per escombratge ja no s'aplica i toca cercar.

from collections import deque

def successors(estat, quadricula):
    """Operadors de l'espai d'estats: N, S, E, O a caselles lliures."""
    files, cols = len(quadricula), len(quadricula[0])
    f, c = estat
    for df, dc in [(-1, 0), (1, 0), (0, 1), (0, -1)]:
        nf, nc = f + df, c + dc
        if 0 <= nf < files and 0 <= nc < cols and quadricula[nf][nc] != "#":
            yield (nf, nc)

def bfs_estats(inicial, es_objectiu, successors_fn):
    """BFS generic sobre un espai implicit. Optim si tots els passos costen igual."""
    frontera = deque([inicial])
    pare = {inicial: None}               # visitats + reconstruccio, com a 03-02
    while frontera:
        estat = frontera.popleft()
        if es_objectiu(estat):
            cami = []
            while estat is not None:    # reconstruccio amb `pare`, igual que a BFS/Dijkstra
                cami.append(estat)
                estat = pare[estat]
            return cami[::-1]
        for suc in successors_fn(estat):
            if suc not in pare:          # el conjunt de visitats es OBLIGATORI
                pare[suc] = estat
                frontera.append(suc)
    return None                          # espai esgotat sense trobar l'objectiu

Observa la signatura: bfs_estats no rep un graf, rep funcions (es_objectiu, successors_fn). Aquest és el canvi de mentalitat de la lliçó — l'algorisme és el mateix de 03-02.

Sobre quan usar cada exploració cega:

Estratègia Memòria Troba el camí més curt (en passos)? Risc
BFS O(b^d) — enorme Es queda sense memòria abans que sense temps
DFS O(d) — mínima No S'enfonsa per branques infinites/inútils
Aprofundiment iteratiu (DFS amb límit creixent) O(d) Repeteix feina (assumible: el darrer nivell domina)

El conjunt de visitats (pare fa aquest doble paper) mereix èmfasi: en un espai d'estats els camins es creuen constantment (a la casella (3, 4) s'hi arriba per milers de rutes). Sense visitats, el mateix estat es re-explora una vegada i una altra i el cost passa de b^d a molt pitjor. L'excepció és quan l'espai és un arbre de debò — com al backtracking de 02-03, on cada node tenia un únic camí des de l'arrel; hi tornarem sobre aquesta distinció al final.

Cerca de cost uniforme: Dijkstra sense graf

Si els operadors tenen costos diferents (creuar una avinguda congestionada costa 3 minuts; un carrer buit, 1), BFS deixa de ser òptim: minimitza passos, no cost. La solució ja la coneixes de 03-03: expandir sempre l'estat de menor cost acumulat amb un heap. Aplicat a un espai implícit, l'algorisme es diu cerca de cost uniforme (UCS), però és literalment Dijkstra amb successors generats al vol — mateix invariant ("el que surt del heap és definitiu"), mateix esborrat mandrós.

No l'implementem per separat: al codi d'A* de la secció següent, UCS és el cas particular h = 0. Aquesta és exactament la relació entre tots dos.

A*: heurístiques, admissibilitat i consistència

Dijkstra/UCS explora en cercles concèntrics de cost creixent al voltant de l'inici: és exhaustiu i cec, gasta el mateix esforç cap a l'objectiu que en direcció contrària. Però en molts espais d'estats sabem alguna cosa sobre on és l'objectiu. Aquest alguna cosa es formalitza com una heurística:

h(n) = estimació del cost restant des de l'estat n fins a l'objectiu.

A* ordena el heap no pel cost acumulat g(n), sinó per

f(n) = g(n) + h(n) — cost ja pagat + cost estimat pendent = estimació del cost total passant per n.

Intuïció: entre dos estats amb el mateix cost acumulat, A* expandeix abans el que sembla més a prop de l'objectiu. És Dijkstra amb brúixola: l'exploració deixa de ser un cercle i es converteix en una el·lipse estirada cap a l'objectiu.

Les garanties depenen de la qualitat de h:

  • Admissibilitat: h mai no sobreestima el cost real restant (h(n) ≤ h*(n) per a tot n). Amb h admissible, A* troba la solució òptima. La intuïció de la prova: si A* hagués de retornar un camí subòptim, el camí òptim tindria a la frontera algun estat amb f menor (perquè la seva h no sobreestima), i aquell estat s'hauria expandit abans.
  • Consistència (o monotonia): h(n) ≤ cost(n → n') + h(n') per a tota aresta — una desigualtat triangular. Implica admissibilitat i alguna cosa més: els valors f mai no decreixen per un camí, així que la primera vegada que un estat surt del heap, la seva g és definitiva — exactament l'invariant de Dijkstra, que es conserva. Amb h consistent n'hi ha prou amb el mateix conjunt "tancat" de sempre; amb h admissible però inconsistent, un estat tancat pot haver de reobrir-se.
  • Casos extrems que situen A* al mapa: amb h = 0 (admissible i consistent trivialment), A* és UCS/Dijkstra; amb h = h* (l'heurística perfecta), A* va directe a l'objectiu sense expandir res de superflu. Tota heurística real viu entre totes dues: com més "informada" (més gran sense passar-se), menys nodes expandeix.

Per moure's en quadrícula amb passos de cost ≥ 1, l'heurística canònica és la distància de Manhattan: |f1 − f2| + |c1 − c2|. És admissible (cap camí real no pot ser més curt que ignorar tots els obstacles) i consistent (moure's una casella canvia l'estimació en ±1 com a molt, i el pas costa almenys 1).

Implementació completa: ruta de repartiment amb obstacles

La furgoneta de Rutalia surt del dipòsit D i ha d'arribar al client C en un barri en obres (# = carrer tallat). Implementem A* complet, amb comptador de nodes expandits per a la comparació experimental posterior.

import heapq

MAPA = [
    "D....#....",
    ".##..#.##.",
    ".#...#..#.",
    ".#.###..#.",
    ".#......#.",
    ".#####.##.",
    "......##..",
    ".####.....",
    "...#..###.",
    "...#.....C",
]

def trobar(mapa, simbol):
    for f, fila in enumerate(mapa):
        if simbol in fila:
            return (f, fila.index(simbol))

def manhattan(a, b):
    return abs(a[0] - b[0]) + abs(a[1] - b[1])

def a_estrella(mapa, h):
    """A* sobre la quadricula. Amb h = lambda n, obj: 0 es converteix en UCS/Dijkstra."""
    inici, objectiu = trobar(mapa, "D"), trobar(mapa, "C")
    heap = [(h(inici, objectiu), 0, inici)]        # (f, g, estat)
    pare = {inici: None}
    g_millor = {inici: 0}
    expandits = 0
    while heap:
        f, g, estat = heapq.heappop(heap)
        if g > g_millor.get(estat, float("inf")):  # esborrat mandros, com a 03-03
            continue
        expandits += 1
        if estat == objectiu:                      # objectiu! reconstruir i sortir
            cami = []
            while estat is not None:
                cami.append(estat)
                estat = pare[estat]
            return cami[::-1], expandits
        for suc in successors(estat, mapa):        # la funcio de la seccio de BFS
            g_nou = g + 1                          # cost 1 per casella (facil de variar)
            if g_nou < g_millor.get(suc, float("inf")):
                g_millor[suc] = g_nou
                pare[suc] = estat
                heapq.heappush(heap, (g_nou + h(suc, objectiu), g_nou, suc))
    return None, expandits

cami, exp = a_estrella(MAPA, manhattan)
print(f"Longitud del cami: {len(cami) - 1} passos, nodes expandits: {exp}")

Punts fins del codi, un per un:

  • La prova d'objectiu es fa en EXTREURE del heap, no en generar el successor. És un error clàssic fer-la en generar: podries trobar l'objectiu per un camí car mentre un de barat espera al heap. En extreure, la consistència de h garanteix que la seva g ja és l'òptima (mateix argument que l'invariant de Dijkstra a 03-03).
  • g_millor + esborrat mandrós: patró idèntic al del nostre Dijkstra del mòdul 3 — es permeten entrades obsoletes al heap i es descarten en sortir comparant amb la millor g coneguda.
  • La tupla del heap és (f, g, estat): s'ordena per f; incloure g com a segon criteri desempata a favor d'estats més avançats, un detall pràctic que sol reduir expansions.
  • UCS gratis: passar h = lambda n, obj: 0 converteix la funció en Dijkstra implícit, sense tocar res més. És la millor demostració que A* generalitza Dijkstra.

Comparació experimental: el valor de la brúixola

Executem les tres variants sobre el mateix mapa i comptem expansions:

h_zero = lambda n, obj: 0
cami_d, exp_dijkstra = a_estrella(MAPA, h_zero)      # UCS / Dijkstra
cami_a, exp_astar    = a_estrella(MAPA, manhattan)   # A*

print(f"Dijkstra/UCS : cami de {len(cami_d)-1} passos, {exp_dijkstra} nodes expandits")
print(f"A* Manhattan : cami de {len(cami_a)-1} passos, {exp_astar} nodes expandits")
# Resultat tipic en aquest mapa de 10x10 amb 66 caselles lliures:
# Dijkstra/UCS : cami de 18 passos, expandeix gairebe les 66 caselles (tot el mapa)
# A* Manhattan : cami de 18 passos, expandeix al voltant de la meitat (s'estira cap a C)

Tots dos retornen un camí de la mateixa longitud — tots dos són òptims —, però A* expandeix força menys nodes. En un mapa 10×10 la diferència és modesta; la bretxa creix amb la mida de l'espai: en quadrícules grans amb obstacles dispersos, A* amb Manhattan expandeix ordres de magnitud menys que Dijkstra, perquè aquest darrer omple el cercle complet de radi 18 mentre A* excava un passadís cap a l'objectiu. Et proposo l'experiment com a hàbit: sempre que implementis A*, instrumenta el comptador d'expansions i compara'l amb h = 0. És la manera honesta de saber quant val la teva heurística (i de detectar heurístiques trencades: si A* expandeix més que Dijkstra, alguna cosa va malament — probablement h no és consistent o està mal escalada).

Heurístiques no admissibles, A* ponderat i IDA* (breu)

I si renunciem a l'optimalitat a canvi de velocitat? Si h sobreestima, A* pot retornar camins subòptims, però sol expandir molts menys nodes. La versió controlada d'aquesta idea és l'A* ponderat: usar f(n) = g(n) + w·h(n) amb w > 1. Es demostra que el camí retornat costa com a molt w vegades l'òptim — un compromís explícit i ajustable. Per a la furgoneta de Rutalia, un w = 1,2 ("accepto rutes fins a un 20 % més llargues") pot reduir el temps de planificació dràsticament en mapes grans; és el mateix esperit que les metaheurístiques del mòdul 2 (02-04, 02-05): canviar garantia per temps, però aquí amb una fita de qualitat demostrable.

L'altre límit pràctic d'A* és la memòria: el heap i g_millor creixen amb els nodes generats. IDA* (iterative deepening A*) aplica la idea de l'aprofundiment iteratiu: DFS amb un llindar sobre f que s'incrementa a cada passada. Memòria O(d) mantenint l'optimalitat, a canvi de repetir feina. És l'algorisme clàssic per a puzles d'espai gegantí (el 15-puzzle es va resoldre òptimament amb IDA*); el deixem en menció.

El mapa mental: cerca de camins vs. optimització exacta

A 02-03, el backtracking i el branch and bound també exploraven arbres d'estats — de fet el B&B millor-primer usava un heap de nodes amb fites optimistes, sospitosament semblant a A*. Convé tancar el curs d'exploració amb el mapa complet, perquè la confusió entre les dues famílies és habitual:

Backtracking / B&B (02-03) BFS / UCS / A* (04-03)
Què es busca La millor configuració completa (assignació, tour del TSP) El millor camí d'un estat inicial a un objectiu
Estructura explorada Arbre de decisions parcials (cada node, un únic camí des de l'arrel) Graf d'estats (a un estat s'hi arriba per molts camins)
Anti-repetició No sol caldre (és un arbre) Conjunt de visitats / g_millor imprescindible
Guia Fita optimista per podar branques que no poden millorar el millor actual Heurística admissible per ordenar l'expansió cap a l'objectiu
Quan acaba En esgotar l'arbre (amb podes): l'òptim exigeix veure-ho "tot" En extreure l'objectiu: la resta de l'espai ni es toca

Les dues famílies comparteixen l'ADN (generar successors, prioritzar amb una estimació optimista, usar un heap) i per això B&B millor-primer i A* són gairebé el mateix algorisme amb propòsit diferent. El criteri per triar: si el teu problema és "trobar el camí/la seqüència fins a un objectiu", pensa en A*; si és "trobar la millor solució completa entre totes les combinacions", pensa en B&B. El TSP de Rutalia era el segon; guiar la furgoneta pel barri en obres és el primer.

Errors Comuns i Consells

  • Comprovar l'objectiu en generar en lloc de fer-ho en extreure. El bug més freqüent en A*/UCS: retorna camins subòptims de manera intermitent i difícil de reproduir. La prova d'objectiu va quan l'estat surt del heap.
  • Heurística que sobreestima "només una mica". Usar la distància euclidiana ×1,5, o Manhattan quan es permet moviment diagonal (allà Manhattan sobreestima i l'admissible és la distància de Txebixov o l'octil). Adéu optimalitat, i de manera silenciosa: el camí retornat sembla raonable. Justifica sempre per què h no pot superar el cost real.
  • Oblidar el conjunt de visitats/g_millor en espais amb cicles. El símptoma és un programa que "es penja" o consumeix memòria sense parar: està re-expandint els mateixos estats per camins diferents.
  • Posar a l'estat menys (o més) del que cal. Si la furgoneta no pot passar dues vegades per la mateixa casella amb obres temporals que depenen de l'hora, l'estat ha d'incloure el temps; si no l'inclou, A* donarà rutes il·legals. A l'inrevés, posar-hi informació irrellevant (el nivell de combustible quan no restringeix res) multiplica l'espai inútilment.
  • Heurística cara de calcular. Si avaluar h costa més del que estalvia en expansions, A* perd contra Dijkstra en temps total encara que expandeixi menys nodes. Mesura totes dues coses: expansions i segons.
  • Consell: les bones heurístiques solen sortir de relaxar el problema (treure restriccions i resoldre exacte el relaxat): Manhattan és "el problema sense obstacles". És la mateixa idea que les fites optimistes de 02-03 — relaxació = estimació optimista, i optimista = admissible.

Exercicis

Exercici 1 — Disseny d'estat. La furgoneta de Rutalia reparteix a la quadrícula, però ara ha de passar primer pel punt de recollida R abans d'anar al client C. Defineix formalment l'espai d'estats (estat, operadors, inicial, objectiu, cost) sense escriure codi, i raona: per què no n'hi ha prou amb l'estat (fila, col)? Quants estats té el nou espai respecte a l'original?

Exercici 2 — Heurístiques admissibles. Per al problema de l'exercici 1 (passar per R i després arribar a C), es proposen tres heurístiques des d'un estat en posició p que encara no ha recollit: (a) manhattan(p, C); (b) manhattan(p, R); (c) manhattan(p, R) + manhattan(R, C). Indica quines són admissibles i quina domina les altres (més informació sense sobreestimar). Justifica cadascuna.

Exercici 3 — Experiment amb A* ponderat. Modifica a_estrella perquè accepti un pes w (f = g + w·h) i executa-la sobre MAPA amb w ∈ {0, 1, 1.5, 3}. Per a cada w apunta la longitud del camí i els nodes expandits, i explica el patró que hi observis. Quin w correspon a Dijkstra? I a un voraç pur tipus "millor primer per heurística"?

Solucions

Solució 1: l'estat ha de ser (fila, col, recollit) amb recollit ∈ {False, True}: la posició sola no determina els moviments legals cap a l'objectiu, perquè ser a C només és objectiu si ja s'ha passat per R. Operadors: els quatre moviments de sempre; en entrar a la casella R, el successor té recollit=True. Inicial: (D_f, D_c, False). Objectiu: (C_f, C_c, True). Cost: 1 per moviment. L'espai es duplica (cada casella existeix en dues "capes", abans i després de recollir): de 66 caselles lliures a 132 estats. És el patró general: cada bit de memòria rellevant per al futur multiplica l'espai — per això dissenyar l'estat mínim suficient és tan important.

Solució 2:

  • (a) manhattan(p, C): admissible — el camí real ha d'acabar a C, i cap camí a C no és més curt que la Manhattan directa; però és poc informada: ignora el desviament obligatori per R.
  • (b) manhattan(p, R): admissible — tot camí vàlid ha de passar abans per R, i arribar a R costa almenys això; també ignora part de la feina (de R a C).
  • (c) manhattan(p, R) + manhattan(R, C): admissible i dominant — el camí real es descompon en "arribar a R" (≥ manhattan(p, R)) més "de R a C" (≥ manhattan(R, C)); la suma de dues fites inferiors de trams obligatoris i disjunts és fita inferior del total. Com que (c) ≥ (a) i (c) ≥ (b) en tot estat sense sobreestimar, domina: A* amb (c) expandeix un subconjunt dels nodes que expandiria amb les altres. A la capa recollit=True, l'heurística correcta és directament manhattan(p, C).

Solució 3:

def a_estrella_w(mapa, h, w):
    # identic a a_estrella, canviant la prioritat d'insercio:
    # heapq.heappush(heap, (g_nou + w * h(suc, objectiu), g_nou, suc))
    ...

for w in [0, 1, 1.5, 3]:
    cami, exp = a_estrella_w(MAPA, manhattan, w)
    print(f"w={w}: {len(cami)-1} passos, {exp} expandits")
# Patro tipic (els valors exactes depenen dels desempats del heap):
# w=0  : 18 passos, expandeix gairebe les 66 caselles -> Dijkstra/UCS: optim, cec
# w=1  : 18 passos, al voltant de la meitat           -> A* classic: optim, dirigit
# w=1.5: 18-20 passos, encara menys                   -> suboptim fitat (<= 1.5x), mes rapid
# w=3  : pot allargar-se a 20-24 passos, minim d'expansions -> gairebe vorac, sense gairebe garantia

Interpretació: w=0 anul·la l'heurística — és exactament Dijkstra. w=1 és A* òptim. En créixer w, el terme h domina sobre g i l'algorisme tendeix al voraç per heurística (greedy best-first: f = h, el límit w→∞), que corre cap al que sembla a prop sense comptabilitzar el que ja s'ha gastat — rapidíssim i sense garanties, el mateix caràcter que els voraços amb contraexemple de 02-02. Els valors exactes depenen del mapa i dels desempats; el patró qualitatiu (expansions ↓, qualitat ↓ a partir de w > 1) és el que t'ha de sortir.

Conclusió

Amb aquesta lliçó es tanca el mòdul 4 i, amb ell, un arc complet del curs. Un espai d'estats és un graf implícit — estat, operadors, objectiu, cost — massa gran per materialitzar-lo, i sobre ell hem reutilitzat tot l'arsenal del mòdul 3 en versió "al vol": BFS quan els passos costen igual, UCS/Dijkstra quan no, i A* quan a més sabem estimar el que falta, amb l'admissibilitat com a contracte d'optimalitat i la consistència com a salvaguarda de l'invariant del heap. L'experiment dels nodes expandits deixa la moralitat del mòdul: la informació és rendiment — una heurística honesta (nascuda de relaxar el problema, com les fites de 02-03) converteix l'exploració d'un cercle cec en un passadís dirigit, i el pes w permet comprar velocitat pagant amb garanties, de manera mesurable. També ha quedat traçada la frontera mental entre cercar el millor camí (aquesta lliçó) i cercar la millor configuració (backtracking i B&B del mòdul 2): mateix ADN, pregunta diferent.

Mirem ara el conjunt: al mòdul 2 vam optimitzar amb regles que nosaltres vam dissenyar; al 3 vam modelar la ciutat com a graf amb pesos que nosaltres vam mesurar; en aquest mòdul hem cercat, ordenat i planificat amb invariants i heurístiques que nosaltres hem justificat. Tot el coneixement del problema l'hem aportat a mà. Però Rutalia té una cosa que encara no hem explotat: milions de registres històrics — lliuraments amb la seva hora, zona, pes, retard, incidències — que contenen patrons que ningú no ha escrit com a regla. Quant trigarà de debò aquest lliurament? Quins clients generaran una incidència? Quines zones es comporten igual? Al mòdul 5 canviem de paradigma: en lloc d'escriure les regles, deixarem que els algorismes les aprenguin de les dades. Comencem a 05-01 amb els fonaments de l'aprenentatge automàtic.

© Copyright 2026. Tots els drets reservats