analistica/lectures/sections/modulo-1.md

5.1 KiB

Calcolo numerico

Compilare un programma

Per eseguire un programma, un computer fa questa cosa: legge le informazioni dall'input e le mette nella memoria; poi da qui legge le istruzioni sequenzialmente e se c'è qualche calcolo da fare, lo fa fare alla ALU (aritmetic logic unit) e memorizza il risultato nella memoria; alla fine passa tutto in output.
Per essere eseguibile, un programma deve essere scritto in codice macchina e per questo si usano i linguaggi di programmazione che, attraverso il compilatore, vengono tradotti in codice macchina:

  • codice sorgente,
  • compilatore,
  • codice oggetto,
  • linker (aggiunge le librerie),
  • codice eseguibile.

Errori di implementazione

Quando si passa dal modello astratto a quello implementato, si distinguono tre tipi di errori:

  • errori analitici: causati dalla necessità di una aritmetica discreta, cioè il fatto che non si possa rappresentare una cosa continua e quindi la si fa a punti;
  • errori inerenti o algoritmici (o di round-off): causati dal numero finito di cifre significative. Dipendono dall'algoritmo utilizzato, perché ci sono metodi migliori di altri per evitare questo tipo di problemi.

Rappresentazione di un numero

L'obiettivo è quello di tenere sotto controllo gli errori di round-off. Per rappresentare un numero, esistono diverse rappresentazioni, tra cui quella di Von Neumann: si dedicano n cifre significative alla mantissa e poi un tot anche alla caratteristica, cioè all'ordine di grandezza (che generalmente è in base 2).
Con n cifre significative in base 2, il numero più alto rappresentabile è 2^n -1 perché con due cifre (0 e 1), ci sono 2^n possibili numeri rappresentabili e, se posti in ordine crescente, ognuno dista dal precedente un'unità, con il minimo che vale 0 (tutti 0), per cui il numero più alto che si può rappresentare è 2^n -1:

  • 0 è l'1,
  • 1 e il 2,
  • ...$2^n -1$ è il $2^n$-esimo.

questo numero corrisponde a un numero in base dieci di m cifre significative, per cui:


  m = \log(2^n -1)

quindi per un numero con 24 bit per la mantissa, si hanno circa 7 cifre significative in base 10.
In precisione semplice (floating point):

  • un bit per il segno,
  • 23 bit per la mantissa (quindi 7 cifre significative),
  • 8 bits per la caratteristica.

In precisione doppia (duble) si raddoppiano i bite alla mantissa e le cifre significative diventano 17, però il tempo per le moltiplicazioni è triplicato e si occupa più memoria, quindi non è che convenga moltissimo.

Esiste anche la rappresentazione fixed-point, in cui c'è un bit per il segno e i restanti per il numero in sé, in cui da qualche parte c'è la virgola. Nel caso del fixed pointd, l'errore relativo può variare moltissimo a seconda del numero rappresentato, mentre per i float è lo stesso per ogni numero: la densità relativa non è costante.
Quando un insieme di rappresentazione non è chiuso rispetto ad un'operazione, il risultato presenta sicuramente errore di round-off e deve essere approssimato.

Errori algoritmici

Per numeri troppo grandi o troppo piccoli che non si riesce a rappresentare, si parla rispettivamente di overflow o underflow. Per questo motivo, somme e soprattutto differenze di numeri grandi portano grandi errori dovuti all'arrotondamento: bisogna cercare di evitarlo se possibile. Per esempio, per calcolare un polinomio, conviene usare il metodi di Ruffini-Horner perché riduce il numero di operazioni da effettuare:

  • metodo semplice:

    
      p(x) = a_0 + a_1x + a_2x^2 + ... +a_nx^n
    

    che sono n addizioni e n(n+1)/2 moltiplicazioni, quindi \sim n^2.

  • metodo di R-H:

    
    p (x) = (((((a_n)x +a_{n-1})x +a_{n-2}x +...
    

    che sono n addizioni e n moltiplicazioni, quindi \sim n.

Il problema si verifica anche se si sottraggono due numeri molto simili tra loro, perché il risultato è un numero molto piccolo che potrebbe finire in underflow (si parla di "cancellazione catastrofica").

\textcolor{orange}{oss:} Il costo computazionale della risoluzione di un sistema lineare in n equazioni è asintoticamente uguale al costo del prodotto di due matrici nxn. Esistono algoritmi che non richiedono più di k \cdot n^{\alpha} operazioni, col il più piccolo valore noto di $\alpha$ pari a 0.2375.

Alcuni algoritmi sono definiti "instabili": accade quando gli errori di round-off si accumulano fino a portare a risultati completamente errati.
Esistono problemi detti "mal condizionati" che con qualsiasi algoritmo danno errori talmente elevati da rendere il risultato privo di significato. In questi casi, piccole variazioni dei dati iniziali portano a grandi variazioni nei risultati. Si chiama "numero di condizionamento del problema" il seguente rapporto:


  \frac{\Delta r}{\Delta \alpha} =
  \frac{\text{\% errore risultato}}{\text{\% errore dato iniziale}}

che può quindi essere espresso in questo modo:


  \Delta \alpha = \frac{x + h - x}{x} = \frac{h}{x}
  \hspace{100pt}
  \Delta r = \frac{f(x + h)-f(x)}{f(x)}

  \frac{\Delta r}{\Delta \alpha} =
  \frac{f(x + h)-f(x)}{h} \cdot \frac{x}{f(x)} =
  f'(x) \cdot \frac{x}{f(x)}

Se questo numero è << 1, allora il problema è poco sensibile ai dati iniziali.