Calcolatore Algoritmi di Calcolo Numerico in C++
Guida Completa agli Algoritmi di Calcolo Numerico in C++
Gli algoritmi di calcolo numerico rappresentano il fondamento dell’analisi computazionale moderna. In C++, linguaggio noto per le sue prestazioni e flessibilità, questi algoritmi trovano applicazione in campi che vanno dall’ingegneria finanziaria alla simulazione fisica. Questa guida esplora i principali metodi numerici implementabili in C++, con esempi pratici e considerazioni sulle prestazioni.
1. Metodi per la Ricerca delle Radici
1.1 Metodo di Bisezione
Il metodo di bisezione è uno degli algoritmi più semplici per trovare le radici di una funzione continua. Si basa sul teorema degli zeri di Bolzano:
- Selezionare un intervallo [a, b] dove f(a) e f(b) hanno segni opposti
- Calcolare il punto medio c = (a + b)/2
- Valutare f(c)
- Determinare il nuovo intervallo:
- Se f(c) = 0, c è la radice
- Se f(c) ha lo stesso segno di f(a), il nuovo intervallo è [c, b]
- Altrimenti, il nuovo intervallo è [a, c]
- Ripetere fino al raggiungimento della tolleranza desiderata
Complessità: O(log((b-a)/ε)) dove ε è la tolleranza
Vantaggi: Semplicità e convergenza garantita per funzioni continue
Limitazioni: Convergenza lineare, richiede intervallo iniziale valido
1.2 Metodo di Newton-Raphson
Questo metodo utilizza la derivata della funzione per ottenere convergenza quadratica:
xn+1 = xn – f(xn)/f'(xn)
Implementazione in C++:
#include <iostream>
#include <cmath>
#include <iomanip>
double f(double x) { return pow(x, 3) - 2*x - 5; }
double df(double x) { return 3*pow(x, 2) - 2; }
double newtonRaphson(double x0, double tol, int maxIter) {
double x = x0;
for (int i = 0; i < maxIter; i++) {
double fx = f(x);
if (abs(fx) < tol) return x;
double dfx = df(x);
if (dfx == 0) break; // Evita divisione per zero
x = x - fx/dfx;
}
return x;
}
int main() {
double root = newtonRaphson(2.0, 1e-6, 100);
std::cout << std::setprecision(8) << "Radice trovata: " << root << std::endl;
return 0;
}
2. Soluzione di Sistemi Lineari
2.1 Metodo di Gauss-Seidel
Algoritmo iterativo per sistemi lineari che sfrutta la decomposizione della matrice:
x(k+1)i = (bi – Σj≠i aijx(k)j) / aii
Criterio di convergenza: La matrice deve essere a diagonale dominante
| Metodo | Convergenza | Complessità per Iterazione | Memoria Ausiliaria |
|---|---|---|---|
| Gauss-Seidel | Lineare | O(n²) | O(n) |
| Jacobi | Lineare | O(n²) | O(n²) |
| SOR (Successive Over-Relaxation) | Lineare (ω ottimale) | O(n²) | O(n) |
2.2 Implementazione Efficiente in C++
Per sistemi di grandi dimensioni, è cruciale ottimizzare l’accesso alla memoria:
void gaussSeidel(const vector<vector<double>>& A,
const vector<double>& b,
vector<double>& x,
double tol, int maxIter) {
int n = A.size();
for (int k = 0; k < maxIter; k++) {
double error = 0;
for (int i = 0; i < n; i++) {
double sum = b[i];
for (int j = 0; j < n; j++) {
if (j != i) sum -= A[i][j] * x[j];
}
double new_x = sum / A[i][i];
error += abs(new_x - x[i]);
x[i] = new_x;
}
if (error < tol) break;
}
}
3. Integrazione Numerica
3.1 Regola del Trapezoide
Approssimazione dell’integrale definito usando trapezi:
∫ab f(x)dx ≈ (b-a)/2 [f(a) + f(b)]
Errore: O((b-a)³)
3.2 Regola di Simpson
Metodo più accurato che usa parabole:
∫ab f(x)dx ≈ (b-a)/6 [f(a) + 4f((a+b)/2) + f(b)]
Errore: O((b-a)⁵)
| Metodo | Ordine di Errore | Punti di Valutazione | Adatto per Funzioni |
|---|---|---|---|
| Trapezoide | O(h³) | 2 | Continue |
| Simpson | O(h⁵) | 3 | Derivabili 4 volte |
| Gauss-Legendre (n=2) | O(h⁵) | 2 | Polinomi fino grado 3 |
4. Ottimizzazione delle Prestazioni in C++
Per algoritmi numerici intensivi, considerare:
- Vettorizzazione: Utilizzare le istruzioni SIMD (SSE, AVX) attraverso:
- Compilatori con flag -O3 -march=native
- Librerie come Eigen o Armadillo
- Intrinseci del compilatore (#include <immintrin.h>)
- Parallelizzazione:
- OpenMP per loop parallel (#pragma omp parallel for)
- Thread pool con std::thread
- GPU computing con CUDA o OpenCL
- Precisione:
- double vs float (15 vs 7 cifre decimali)
- long double (estensioni x87: 80-bit)
- Librerie ad alta precisione (GMP, MPFR)
5. Librerie C++ per il Calcolo Numerico
Le principali librerie open-source per implementazioni ottimizzate:
- Eigen: Algebra lineare header-only con supporto SIMD
- Matrici e vettori di dimensione dinamica/fissa
- Decomposizioni (LU, QR, SVD)
- Solvers per sistemi lineari e problemi agli autovalori
- Armadillo: Sintassi simile a MATLAB
- Interfaccia semplice per operazioni matriciali
- Integrazione con LAPACK/BLAS
- Funzioni statistiche integrate
- GNU Scientific Library (GSL): Collezione completa di routine
- Integrazione numerica (QAG, QAGS)
- Minimizzazione (Nelder-Mead, BFGS)
- Generatori di numeri casuali avanzati
- Boost.Math:
- Funzioni speciali (Bessel, Gamma)
- Distribuzioni statistiche
- Intervalli e arrotondamenti
6. Validazione e Testing
Strategie per garantire l’affidabilità degli algoritmi:
- Test con casi noti:
- Funzioni polinomiali con radici analitiche
- Sistemi lineari con soluzioni esatte
- Integrali di funzioni elementari
- Analisi dell’errore:
- Errore assoluto vs relativo
- Condizionamento del problema (numero di condizione)
- Stabilità numerica (evitare cancellazione catastrofica)
- Benchmarking:
- Confrontare con implementazioni di riferimento (MATLAB, SciPy)
- Misurare tempi di esecuzione con <chrono>
- Profiling con Valgrind o perf
7. Applicazioni Pratiche
7.1 Finanza Computazionale
Modelli per la valutazione di opzioni:
- Metodo di Black-Scholes (equazione differenziale parziale)
- Simulazioni Monte Carlo per opzioni esotiche
- Calibrazione di modelli di volatilità
7.2 Dinamica Molecolare
Simulazioni di sistemi fisici:
- Integrazione delle equazioni del moto (Verlet, Runge-Kutta)
- Calcolo delle forze (potenziali di Lennard-Jones)
- Condizioni al contorno periodiche
7.3 Elaborazione di Immagini
Algoritmi numerici per:
- Filtri spaziali (convoluzione 2D)
- Trasformate (Fourier, Wavelet)
- Ricostruzione tomografica