Vorrei far notare che in C++
1/2
è di tipo
int
ed è uguale a \(0\).
Per valori positivi, che è quello che ci aspettiamo,
j <= std::ceil(a - 0.5 )
è equivalente a
j <= std::round( a )
. Inoltre usare
pow
per la potenza di
-1
è ridicolo. Personalmente trovo che sia usato
troppo dagli studenti di informatica. Da notare inoltre che
ceil ritorna un valore di tipo double.
Comunque la mia preoccupazione sull'epsilon del double era inutile: la convergenza è molto molto lenta.
Il risultato di questi commenti è il seguente:
- Codice:
#include <iostream>
#include <cmath>
#include <chrono>
#include <limits>
double pi(const long n, double& last_error)
{
double p = 0;
double eps = 1.;
double den = 0.25;
double elem = 0.;
for (long i = 0; i <= n; ++i)
{
elem = 1 / den;
p += (eps * elem);
eps *= -1.;
den += 0.5;
}
last_error = elem;
return p;
}
double pi2(const long n, double& last_error)
{
double p = 0.;
double el = 0.;
for (long i = 0; i <= n; ++i)
{
el = pow(-1, i) * 4. / (2. * i + 1.);
p += el;
}
return p;
}
int main()
{
const double min_epsilon = 4. / std::numeric_limits<long>::max();
double e;
std::cout << "epsilon = ";
std::cin >> e;
if (e < min_epsilon)
{
e = min_epsilon;
std::cerr << "ERRORE: valore di epsilon troppo piccolo, l'epsilon sara' " << e << std::endl;
}
long n = std::lround(2. / e);
std::cout << "n = " << n << std::endl;
auto now = std::chrono::high_resolution_clock::now();
double res = pi(n, e);
auto end = std::chrono::high_resolution_clock::now();
std::cout << "pi(n) = " << res << std::endl;
std::cout << "|pi(n)-pi(n-1)| = " << e << std::endl;
std::cout << "time = " << std::chrono::duration<double, std::milli>(end - now).count() << "ms" << std::endl;
now = std::chrono::high_resolution_clock::now();
res = pi2(n, e);
end = std::chrono::high_resolution_clock::now();
std::cout << "pi(n) = " << res << std::endl;
std::cout << "|pi(n)-pi(n-1)| = " << e << std::endl;
std::cout << "time = " << std::chrono::duration<double, std::milli>(end-now).count() << "ms" << std::endl;
}
Tutti quei chrono e le due funzioni sono state messe per mostrare come usare pow abbia una forte influenza nelle performance. Per esempio, in debug e sul mio portatile lentissimo, ho avuto il seguente risultato (compilato con Visual Studio 2017 Community ed. con sottosistema Console e per x86):
- Codice:
epsilon = 1E-7
n = 20000000
pi(n) = 3.14159
|pi(n)-pi(n-1)| = 1e-07
time = 824.768ms
pi(n) = 3.14159
|pi(n)-pi(n-1)| = 1e-07
time = 4862.78ms
Premere un tasto per continuare . . .
Con il minimo epsilon che ho messo i tempi sono molto lenti.
Puoi fare ancora meglio della funzione da me proposta. Per esempio questa sembra essere un po' più performante:
- Codice:
double pi(const long n, double& last_error)
{
double p = 0;
double eps = 1.;
double den = 0.25;
double elem = 0.;
for (long i = 0; i <= n; ++i)
{
elem = 1. / den;
p += elem;
den += 0.5;
if (i++ == n) break;
elem = 1. / den;
p -= elem;
den += 0.5;
}
last_error = elem;
return p;
}
e suppongo si possa fare ancora meglio.