Calcolo Numerico Esercizi Svolti In C

Calcolatore Numerico per Esercizi in C

Inserisci i parametri per calcolare soluzioni numeriche comuni negli esercizi di programmazione in C.

Usa x come variabile, operatori: + – * / ^ (potenza), funzioni: sin(), cos(), tan(), exp(), log(), sqrt()

Guida Completa al Calcolo Numerico con Esercizi Svolti in C

Il calcolo numerico rappresenta una branca fondamentale della matematica applicata che si occupa di sviluppare algoritmi per approssimare soluzioni a problemi matematici complessi. In questo articolo esploreremo i principali metodi numerici implementabili in linguaggio C, con esempi pratici ed esercizi svolti.

1. Introduzione al Calcolo Numerico

Il calcolo numerico trova applicazione in numerosi campi scientifici e ingegneristici dove le soluzioni analitiche esatte non sono disponibili o sono troppo complesse da calcolare. I metodi numerici permettono di:

  • Trovare radici di equazioni non lineari
  • Risolvere sistemi di equazioni lineari
  • Calcolare integrali definiti
  • Risolvere equazioni differenziali ordinarie
  • Approssimare funzioni con polinomi

2. Errori nel Calcolo Numerico

Prima di addentrarci nei metodi specifici, è cruciale comprendere i concetti di errore:

  1. Errore assoluto: |x* – x| dove x* è il valore approssimato e x il valore esatto
  2. Errore relativo: |x* – x|/|x| (se x ≠ 0)
  3. Errore di troncamento: derivante dall’interruzione di processi infiniti
  4. Errore di arrotondamento: causato dalla rappresentazione finita dei numeri

3. Metodi per la Ricerca delle Radici

3.1 Metodo di Bisezione

Il metodo di bisezione è uno dei più semplici metodi iterativi per trovare le radici di una funzione continua. Il principio si basa sul teorema degli zeri:

Se f è continua in [a,b] e f(a)·f(b) < 0, allora esiste almeno una radice in (a,b)

Algoritmo:

  1. Scegliere un intervallo [a,b] tale che f(a)·f(b) < 0
  2. Calcolare c = (a + b)/2
  3. Se f(c) = 0, c è la radice
  4. Altrimenti:
    • Se f(a)·f(c) < 0, la radice è in [a,c]
    • Se f(b)·f(c) < 0, la radice è in [c,b]
  5. Ripetere fino al raggiungimento della tolleranza desiderata

Implementazione in C:

#include <stdio.h>
#include <math.h>

#define TOLLERANZA 1e-6
#define MAX_ITER 100

double f(double x) {
    return pow(x, 3) - 2*x - 5;
}

double bisezione(double a, double b) {
    if (f(a) * f(b) >= 0) {
        printf("Errore: f(a) e f(b) devono avere segni opposti\n");
        return NAN;
    }

    double c;
    int iter = 0;

    while ((b - a) >= TOLLERANZA && iter < MAX_ITER) {
        c = (a + b)/2;
        if (f(c) == 0.0) break;
        else if (f(c)*f(a) < 0) b = c;
        else a = c;
        iter++;
    }

    return c;
}

int main() {
    double a = 2, b = 3;
    double radice = bisezione(a, b);
    printf("La radice approssimata è: %.6f\n", radice);
    return 0;
}

3.2 Metodo di Newton-Raphson

Il metodo di Newton (o Newton-Raphson) è un metodo iterativo che converge più rapidamente della bisezione, ma richiede la conoscenza della derivata della funzione.

Formula iterativa: xₙ₊₁ = xₙ – f(xₙ)/f'(xₙ)

Implementazione in C:

#include <stdio.h>
#include <math.h>

#define TOLLERANZA 1e-6
#define MAX_ITER 100

double f(double x) {
    return pow(x, 3) - 2*x - 5;
}

double df(double x) {
    return 3*pow(x, 2) - 2;
}

double newton(double x0) {
    double x = x0;
    int iter = 0;

    while (iter < MAX_ITER) {
        double fx = f(x);
        if (fabs(fx) < TOLLERANZA) break;

        double dfx = df(x);
        if (dfx == 0) {
            printf("Errore: derivata nulla\n");
            return NAN;
        }

        double x_new = x - fx/dfx;
        if (fabs(x_new - x) < TOLLERANZA) break;
        x = x_new;
        iter++;
    }

    return x;
}

int main() {
    double x0 = 2.0;
    double radice = newton(x0);
    printf("La radice approssimata è: %.6f\n", radice);
    return 0;
}

4. Integrazione Numerica

4.1 Regola del Trapezio

La regola del trapezio è un metodo semplice per approssimare l’integrale definito di una funzione. L’idea è di approssimare l’area sotto la curva con una serie di trapezi.

Formula: ∫ₐᵇ f(x)dx ≈ (h/2)[f(a) + 2f(x₁) + 2f(x₂) + … + 2f(xₙ₋₁) + f(b)]

dove h = (b-a)/n e xᵢ = a + ih per i = 1,2,…,n-1

Implementazione in C:

#include <stdio.h>
#include <math.h>

double f(double x) {
    return exp(-x*x); // Funzione esempio: e^(-x^2)
}

double trapezio(double a, double b, int n) {
    double h = (b - a)/n;
    double integrale = (f(a) + f(b))/2.0;

    for (int i = 1; i < n; i++) {
        double x = a + i*h;
        integrale += f(x);
    }

    integrale *= h;
    return integrale;
}

int main() {
    double a = 0, b = 1;
    int n = 1000;
    double risultato = trapezio(a, b, n);
    printf("Approssimazione dell'integrale: %.6f\n", risultato);
    return 0;
}

4.2 Regola di Simpson

La regola di Simpson fornisce un’approssimazione generalmente più accurata rispetto alla regola del trapezio, utilizzando polinomi quadratici invece che lineari.

Formula: ∫ₐᵇ f(x)dx ≈ (h/3)[f(a) + 4f(x₁) + 2f(x₂) + 4f(x₃) + … + 2f(xₙ₋₂) + 4f(xₙ₋₁) + f(b)]

dove h = (b-a)/n e n deve essere pari

5. Risoluzione di Sistemi Lineari

5.1 Eliminazione di Gauss

L’eliminazione di Gauss è un metodo per risolvere sistemi di equazioni lineari trasformando la matrice dei coefficienti in una matrice triangolare superiore.

Algoritmo:

  1. Costruire la matrice aumentata [A|b]
  2. Per ogni colonna k da 1 a n-1:
    • Trovare il pivot (elemento non nullo)
    • Per ogni riga i sotto il pivot:
      • Calcolare il moltiplicatore m = a[i][k]/a[k][k]
      • Sottrarre m volte la riga del pivot dalla riga i
  3. Risolvere il sistema triangolare con sostituzione all’indietro

Implementazione in C:

#include <stdio.h>
#define N 3

void gauss(double a[N][N+1]) {
    int i, j, k;
    double m;

    // Eliminazione
    for (k = 0; k < N-1; k++) {
        for (i = k+1; i < N; i++) {
            m = a[i][k]/a[k][k];
            for (j = k; j <= N; j++) {
                a[i][j] -= m*a[k][j];
            }
        }
    }

    // Sostituzione all'indietro
    double x[N];
    x[N-1] = a[N-1][N]/a[N-1][N-1];
    for (i = N-2; i >= 0; i--) {
        double sum = 0;
        for (j = i+1; j < N; j++) {
            sum += a[i][j]*x[j];
        }
        x[i] = (a[i][N] - sum)/a[i][i];
    }

    // Stampa soluzione
    printf("Soluzione:\n");
    for (i = 0; i < N; i++) {
        printf("x[%d] = %.4f\n", i, x[i]);
    }
}

int main() {
    double a[N][N+1] = {
        {2, 1, -1, 8},
        {-3, -1, 2, -11},
        {-2, 1, 2, -3}
    };

    gauss(a);
    return 0;
}

6. Confronto tra Metodi Numerici

Metodo Velocità di Convergenza Requisiti Vantaggi Svantaggi Costo Computazionale
Bisezione Lineare (errore ~1/2ⁿ) f continua, f(a)·f(b) < 0 Sempre convergente Lento Basso
Newton-Raphson Quadratica (errore ~e²) f derivabile, f'(x) ≠ 0 Molto veloce vicino alla soluzione Può divergere, richiede derivata Moderato
Secante Superlineare (~1.618) f continua Non richiede derivata Meno veloce di Newton Moderato
Regola del Trapezio O(h²) f integrable Semplice da implementare Meno accurato di Simpson Basso
Regola di Simpson O(h⁴) f integrable, n pari Molto accurato Richiede n pari Moderato

7. Ottimizzazione delle Prestazioni in C

Quando si implementano algoritmi numerici in C, è importante considerare alcuni aspetti per ottimizzare le prestazioni:

  • Evita calcoli ridondanti: Memorizza valori che vengono riutilizzati
  • Usa tipi di dati appropriati:
    • float per precisione semplice (6-7 cifre decimali)
    • double per precisione doppia (15-16 cifre decimali)
    • long double per precisione estesa
  • Ottimizza i loop:
    • Minimizza le operazioni all’interno dei cicli
    • Usa restrict per pointer aliasing
    • Considera il loop unrolling per cicli piccoli
  • Parallelizzazione:
    • Usa OpenMP per parallelizzare loop indipendenti
    • Considera l’uso di SIMD (SSE, AVX) per operazioni vettoriali
  • Gestione degli errori:
    • Controlla sempre i domini delle funzioni (es: log(x) per x ≤ 0)
    • Usa isnan() e isinf() per rilevare valori non validi

8. Librerie Utili per il Calcolo Numerico in C

Esistono numerose librerie che possono semplificare l’implementazione di algoritmi numerici in C:

Libreria Descrizione Funzionalità Principali Licenza
GSL GNU Scientific Library Radici, integrazione, algebra lineare, numeri casuali, FFT GPL
LAPACK Linear Algebra Package Sistemi lineari, autovalori, decomposizioni matrici BSD
FFTW Fastest Fourier Transform in the West Trasformate di Fourier discrete GPL
BLAS Basic Linear Algebra Subprograms Operazioni vettoriali e matrici Pubblico dominio
GMP GNU Multiple Precision Aritmetica a precisione arbitraria LGPL

9. Errori Comuni e Come Evitarli

Durante l’implementazione di algoritmi numerici in C, è facile incorrere in errori che possono compromettere l’accuratezza o la stabilità dei risultati:

  1. Cancellazione catastrofica:

    Si verifica quando si sottraggono due numeri quasi uguali, perdendo precisione. Soluzione: riorganizzare le formule o usare precisione maggiore.

    Esempio problematico: (1 – cos(x))/x per x piccolo

  2. Overflow/Underflow:

    L’overflow si verifica quando un numero supera il massimo rappresentabile, mentre l’underflow quando è troppo piccolo. Soluzione: usare scale appropriate o logaritmi.

  3. Instabilità numerica:

    Algoritmi apparentemente corretti possono essere numericamente instabili. Soluzione: analizzare la propagazione degli errori e usare algoritmi stabili.

  4. Convergenza a soluzioni errate:

    Alcuni metodi iterativi possono convergere a soluzioni non desiderate. Soluzione: usare buoni valori iniziali e criteri di arresto robusti.

  5. Errori di arrotondamento accumulati:

    In algoritmi iterativi, gli errori di arrotondamento possono accumularsi. Soluzione: usare precisione maggiore o algoritmi che minimizzano gli errori.

10. Applicazioni Pratiche del Calcolo Numerico

Il calcolo numerico trova applicazione in numerosi campi:

  • Fisica computazionale:
    • Simulazione di sistemi dinamici
    • Meccanica quantistica
    • Teoria dei campi
  • Ingegneria:
    • Analisi strutturale (metodo degli elementi finiti)
    • Dinamica dei fluidi (CFD)
    • Progettazione di circuiti elettronici
  • Finanza computazionale:
    • Valutazione di opzioni (modello Black-Scholes)
    • Analisi del rischio
    • Ottimizzazione di portafoglio
  • Grafica computerizzata:
    • Ray tracing
    • Animazione fisicamente accurata
    • Modellazione 3D
  • Machine Learning:
    • Ottimizzazione di funzioni di costo
    • Reti neurali
    • Elaborazione del segnale

11. Esercizi Pratici con Soluzioni

Esercizio 1: Metodo di Bisezione

Testo: Trovare la radice dell’equazione f(x) = x³ – x – 1 nell’intervallo [1, 2] con tolleranza 10⁻⁴.

Soluzione in C:

#include <stdio.h>
#include <math.h>

double f(double x) {
    return pow(x, 3) - x - 1;
}

double bisezione(double a, double b, double tol) {
    if (f(a) * f(b) >= 0) {
        printf("Errore: f(a) e f(b) devono avere segni opposti\n");
        return NAN;
    }

    double c;
    while ((b - a) >= tol) {
        c = (a + b)/2;
        if (f(c) == 0.0) break;
        else if (f(c)*f(a) < 0) b = c;
        else a = c;
    }
    return c;
}

int main() {
    double a = 1, b = 2, tol = 1e-4;
    double radice = bisezione(a, b, tol);
    printf("Radice approssimata: %.6f\n", radice);
    printf("Valore funzione: %.6f\n", f(radice));
    return 0;
}

Output atteso: Radice approssimata: 1.324718

Esercizio 2: Regola di Simpson

Testo: Calcolare l’integrale di f(x) = sin(x) da 0 a π/2 usando la regola di Simpson con n=10.

Soluzione in C:

#include <stdio.h>
#include <math.h>

double f(double x) {
    return sin(x);
}

double simpson(double a, double b, int n) {
    double h = (b - a)/n;
    double integrale = f(a) + f(b);

    for (int i = 1; i < n; i++) {
        double x = a + i*h;
        integrale += f(x) * ((i % 2 == 0) ? 2 : 4);
    }

    integrale *= h/3;
    return integrale;
}

int main() {
    double a = 0, b = M_PI/2;
    int n = 10;
    double risultato = simpson(a, b, n);
    printf("Integrale approssimato: %.6f\n", risultato);
    printf("Valore esatto: 1.000000\n");
    return 0;
}

Esercizio 3: Eliminazione di Gauss

Testo: Risolvere il seguente sistema lineare usando l’eliminazione di Gauss:

2x + y – z = 8
-3x – y + 2z = -11
-2x + y + 2z = -3

Soluzione: Vedi implementazione nella sezione 5.1

Leave a Reply

Your email address will not be published. Required fields are marked *