Matly

Algorismes: correcció, invariants de bucle i complexitat asimptòtica

Vols practicar? Aquest tema té 102 exercicis interactius de 5 tipus, de deu en deu, amb correcció immediata i explicació pas a pas.

Hi ha dues preguntes independents sobre qualsevol algorisme: fa el que ha de fer i quant costa. Es responen amb eines diferents i confondre-les és l'error conceptual d'aquest tema. Un algorisme ràpid i incorrecte no val res, i un de correcte però inviable tampoc.

Què vol dir que un algorisme és correcte

La correcció parcial diu que, si l'algorisme acaba i les precondicions es compleixen, la sortida satisfà la postcondició. La correcció total hi afegeix la terminació. Són dues coses separades i es demostren per separat.

L'eina per a la correcció parcial és l'invariant de bucle: una propietat que es compleix just abans d'entrar al bucle, es preserva a cada iteració, i que combinada amb la condició de sortida implica el que vols demostrar. Els tres passos són obligatoris; provar-ne només dos no demostra res.

L'eina per a la terminació és una variant: una quantitat entera no negativa que disminueix estrictament a cada iteració. Com que no pot baixar indefinidament, el bucle acaba.

Cerca binària: invariant i variant

Vols trobar un valor en una taula ordenada mantenint un interval actiu.

Invariant: si el valor és a la taula, és dins de l'interval actiu. Es compleix al principi (l'interval és tota la taula) i es preserva: en comparar amb l'element del mig, l'ordre garanteix que la meitat que descartes no el pot contenir.

Condició de sortida: l'interval és buit (no hi és) o l'element del mig coincideix (l'has trobat). Combinada amb l'invariant, això dona exactament el resultat correcte.

Variant: la longitud de l'interval, que com a mínim es divideix per dos a cada pas i és sempre no negativa. Per tant l'algorisme acaba, i ho fa en O(log⁡n)O(\log n) iteracions.

Fixa't que la correcció no depèn de comparar amb el punt mig: qualsevol punt de l'interval preservaria l'invariant. El que dona el cost logarítmic és triar el mig.

Notació asimptòtica

Dir que f(n)=O(g(n))f(n) = O(g(n)) significa que existeixen una constant C>0C > 0 i un llindar a partir del qual ∣f(n)∣≤C g(n)|f(n)|\leq C\,g(n). La notació Ω\Omega és la cota inferior anàloga i Θ\Theta vol dir les dues alhora.

Què no diu una O gran

No és un valor exacte. Dir que un algorisme és O(n2)O(n^{2}) no vol dir que el seu cost sigui quadràtic: és una cota superior, i podria ser lineal. Si vols afirmar que realment creix com un quadrat, has de dir Θ(n2)\Theta(n^{2}).

No mesura segons. Sense fixar un model de màquina i un cost per operació, l'asimptòtica no diu res sobre el temps real. Dos algorismes Θ(n)\Theta(n) poden diferir per un factor cent.

Les constants importen a la pràctica. Un algorisme Θ(nlog⁡n)\Theta(n\log n) amb constant enorme pot perdre contra un Θ(n2)\Theta(n^{2}) per a totes les mides que faràs servir de veritat.

Un bucle de nn passos amb cost constant és Θ(n)\Theta(n). Però dos bucles niats no sempre donen n2n^{2}: cal mirar els límits reals. Si el bucle interior va fins a l'índex de l'exterior, el cost total és

∑i=1ni=n(n+1)2=Θ(n2),\sum_{i=1}^{n} i = \frac{n(n+1)}{2} = \Theta(n^{2}),

que sí que és quadràtic, però amb la meitat de la constant. I si el bucle interior fes un nombre constant de passos, el total seria lineal.

Per a les recursions, l'ordenació per fusió satisfà T(n)=2T(n/2)+Θ(n)T(n) = 2T(n/2) + \Theta(n), que dona Θ(nlog⁡n)\Theta(n\log n). La lectura és directa: hi ha log⁡2n\log_2 n nivells de recursió i a cada nivell es fa un treball total lineal.

Un avís pràctic sovint oblidat: l'aritmètica amb enters grans no té cost constant. Quan la longitud dels operands creix amb l'entrada, cal distingir el nombre d'operacions de la complexitat en bits.

Dos bucles niats que no són quadràtics

Compara aquests dos patrons, tots dos amb dos bucles niats.

Patró A: el bucle exterior va de 11 a nn i l'interior també. Cost: nn iteracions per nn, és a dir Θ(n2)\Theta(n^{2}).

Patró B: el bucle exterior va de 11 a nn i l'interior fa córrer un índex que es duplica fins a passar de nn. Cada execució de l'interior fa Θ(log⁡n)\Theta(\log n) passos, de manera que el total és

Θ(nlog⁡n),\Theta(n\log n),

que per a n=106n = 10^{6} és unes cinquanta mil vegades més barat que el patró A.

La lliçó. «Dos bucles niats» no és una unitat d'anàlisi. El que compta és quantes iteracions fa cadascun realment, i això surt dels límits i del pas, no de l'aparença del codi.

Algorismes de grafs

Amb llistes d'adjacència, les cerques en amplada i en profunditat costen O(∣V∣+∣E∣)O(|V|+|E|): cada vèrtex i cada aresta es visiten un nombre constant de vegades. La cerca per amplada dona a més les distàncies mínimes en nombre d'arestes en grafs no ponderats, perquè explora per capes.

Dijkstra calcula distàncies mínimes amb pesos no negatius. A cada pas fixa definitivament el vèrtex provisional de distància mínima. La justificació és exactament la no-negativitat: qualsevol camí alternatiu hauria de passar per un vèrtex encara no fixat, que ja té distància provisional més gran, i afegir-hi arestes no negatives no pot millorar-la. Amb un munt binari, el cost habitual és O((∣V∣+∣E∣)log⁡∣V∣)O((|V|+|E|)\log|V|).

Dijkstra amb pesos negatius no és «menys precís»: és incorrecte

Amb una sola aresta de pes negatiu, l'argument que justifica fixar un vèrtex s'ensorra, i l'algorisme pot donar una distància més gran que la real. La solució correcta és Bellman i Ford, amb cost O(∣V∣ ∣E∣)O(|V|\,|E|), que a més detecta cicles de pes negatiu accessibles des de l'origen: quan n'hi ha, les distàncies mínimes senzillament no existeixen com a nombres finits.

Per a l'arbre d'expansió mínim d'un graf connex no dirigit, Kruskal ordena les arestes per pes i va afegint les que no tanquen cap cicle. La justificació correcta és la propietat del tall: donada qualsevol partició dels vèrtexs en dos grups, una aresta de pes mínim que els travessi es pot incloure en algun arbre òptim. Dir només «sempre agafem la més barata» no és una demostració: és una descripció, i hi ha problemes on aquesta estratègia voraç falla.

Com atacar una anàlisi de cost

  1. Declara la representació de les dades i el model de cost per operació.
  2. Compta iteracions reals, no el nombre de bucles niats.
  3. Si hi ha recursió, escriu la recurrència i resol-la (el tema de recurrències té els mètodes).
  4. Digues si el que has trobat és una cota superior o un ordre exacte.
  5. Comprova si la mida dels números creix: si sí, l'aritmètica no és de cost constant.
Exercici resolt: cost d'un bucle de duplicació

Un bucle comença amb x=1x = 1 i repeteix x←2xx \leftarrow 2x fins que x≥nx \geq n. Quantes iteracions fa?

Després de kk passos, x=2kx = 2^{k}. La condició de sortida és 2k≥n2^{k}\geq n, és a dir

k≥log⁡2n,k=⌈log⁡2n⌉.k \geq \log_2 n, \qquad k = \lceil \log_2 n\rceil.

El cost és O(log⁡n)O(\log n) en el model d'operacions unitàries.

El matís que gairebé sempre s'oblida. Si nn és astronòmic, el número xx té fins a log⁡2n\log_2 n bits i cada duplicació ja no és una operació de cost constant. En complexitat de bits el cost puja. Per a mides de màquina habituals el model unitari és raonable, però cal dir-ho.

Exercici resolt: invariant d'una suma acumulada

Un bucle recorre una llista i va acumulant en una variable ss, començant amb s=0s = 0. Quin és l'invariant i què demostra?

Invariant: després de processar els ii primers elements, ss val exactament la suma d'aquests ii elements.

Inicialització: amb i=0i = 0, la suma de cap element és 00, i s=0s = 0. Es compleix.

Preservació: si es compleix per a ii i el pas següent fa s←s+ai+1s \leftarrow s + a_{i+1}, aleshores la nova ss és la suma dels i+1i+1 primers. Es preserva.

Terminació: el bucle acaba amb i=ni = n, i combinat amb l'invariant això diu que ss és la suma de tots els elements, que és exactament la postcondició.

Variant: la quantitat n−in - i, entera, no negativa i estrictament decreixent. Demostra terminació, i el cost és Θ(n)\Theta(n).

Aquest esquema de quatre passos (inicialització, preservació, terminació, variant) és el que s'espera en qualsevol demostració de correcció, i es pot escriure sempre igual.

Ara prova-ho tu. 102 exercicis d'aquest tema, de 5 tipus diferents, per resoldre en línia, de deu en deu: escrius la resposta, te la corregeix a l'instant i, si falles, t'explica com es fa pas a pas. Quan acabis una tanda, en tens deu més, amb dades noves.

Fer els exercicisSimulacre d'examen