[C++] programma sommatoria con ciclio
Un approssimazione di $pi$ di “grado $n$” può essere calcolata tramite la somma
$ pi_i=sum_(j=0)^i (-1)^j*4/(2j+1) $
1. Sviluppare un programma che prenda un numero intero, $n$, e calcoli l’approssimazione di “grado $n$”.
2. Sviluppare una seconda versione che prenda un double, $epsilon$, e calcoli un approssimazione di $pi$ di “grado
$n$” tale che $|pi_i - pi_(i-1)|
il primo punto è cosi è funziona:
ho dei problemi con il secondo punto:
ho provato con cicli annidati per calcolare le somme e metterle in relazione con la richiesta di $| |
$diff=4-3/4$ è il primo termine della differenza di sommatorie, ho dei problemi con l'indice i in quando non saprei come dargli una fine, visto che la fine è il relazione con la soddisfazione della $| |
$ pi_i=sum_(j=0)^i (-1)^j*4/(2j+1) $
1. Sviluppare un programma che prenda un numero intero, $n$, e calcoli l’approssimazione di “grado $n$”.
2. Sviluppare una seconda versione che prenda un double, $epsilon$, e calcoli un approssimazione di $pi$ di “grado
$n$” tale che $|pi_i - pi_(i-1)|
il primo punto è cosi è funziona:
#include <iostream> #include <cmath> using namespace std; int main() { double pi=0; int j=0,x; cout<<"dammi il grado dell approssimazione"; cin>>x; while (j<=x){ pi=pi+((pow(-1,j)*4)/((2*j)+1)); j++; } cout<<pi; return 0; }
ho dei problemi con il secondo punto:
ho provato con cicli annidati per calcolare le somme e metterle in relazione con la richiesta di $| |
#include <iostream> #include <cmath> using namespace std; int main() { double pi=0,pi1=0,e,diff=4-3/4; int i,j=0; cin>>e; while (diff<e) { while (j<=i){ pi=pi+((pow(-1,j)*4)/((2*j)+1)); j++; } while (j<=i){ pi1=pi1+((pow(-1,j-1)*4)/((2*(j-1))+1)); j++; } diff=pi-pi1; } cout<<diff; return 0; }
Risposte
Sono un po' arrugginito in matematica, ma non dovrebbe essere
$ |pi_i-pi_(i-1)|=4/(2i+1)i>2/epsi-0.5 $ ??
Quindi $ n $ può essere ottenuto arrotondando per eccesso il suddetto valore.
$ |pi_i-pi_(i-1)|=4/(2i+1)
Quindi $ n $ può essere ottenuto arrotondando per eccesso il suddetto valore.
Quello è il valore matematicamente corretto. All'atto pratico dovresti tenere conto che i valori sono in floating point, quindi la somma potrebbe convergere prima di quell'\(i\). E suppongo che il \(-0.5\) possa essere ignorato o sostituito con un diverso metodo di arrotondamento.
cosa intendi che bisogna tener conto che sono floating point all atto pratico?
se scrivo il programma con quella condizione non funziona?
ho scritto questo ma qualsiasi double che scrivo mi esce sempre 0,
i double li scrivo con il . esempio 0.06 ; come ide uso DEV c++
se scrivo il programma con quella condizione non funziona?
ho scritto questo ma qualsiasi double che scrivo mi esce sempre 0,
i double li scrivo con il . esempio 0.06 ; come ide uso DEV c++
#include <iostream> #include <cmath> #include <stdlib.h> #include <stdio.h> using namespace std; int main() { double pi=0,e; int j; cout<<"dammi epsilon"; cin>>e; for(j=0;j<=2/e-1/2;j++) { pi=pi+(pow(-1,j)*4)/((2*j)+1) } cout<<pi; }
ho scritto questo ma qualsiasi double che scrivo mi esce sempre 0,
i double li scrivo con il . esempio 0.06 ; come ide uso DEV c++
Strano che quel codice compila, manca un ";" nel corpo del ciclo for. Fatta questa correzione cmq a me il programma sembra funzionare, quindi non saprei dirti qual è il problema.
In ogni caso la condizione
j<=2/e-1/2
andrebbe sostituita con
j<=ceil(2/e-1/2)
affinché la disequazione $ |pi_i - pi_(i-1)|
Questo è il programma che avevo scritto io:
#include <iostream> #include <math.h> using namespace std; double pi(const unsigned int n) { double p = 0; for(unsigned int i = 0; i <= n; ++i) { p += pow(-1, i) * 4 /(2 * i + 1); } return p; } int main() { double e; cout << "epsilon -->"; cin >> e; unsigned int n = ceil(2 / e - 0.5); cout << "n = " << n << endl; e = pi(n); cout << "pi(n) = " << e << endl; e -= pi(n - 1); if(e < 0) { e = -e; } cout << "|pi(n)-pi(n-1)| = " << e; }
Vorrei far notare che in C++ [inline]1/2[/inline] è di tipo [inline]int[/inline] ed è uguale a \(0\).
Per valori positivi, che è quello che ci aspettiamo, [inline]j <= std::ceil(a - 0.5 )[/inline] è equivalente a [inline]j <= std::round( a )[/inline]. Inoltre usare [inline]pow[/inline] per la potenza di [inline]-1[/inline] è 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:
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):
Puoi fare ancora meglio della funzione da me proposta. Per esempio questa sembra essere un po' più performante:
Per valori positivi, che è quello che ci aspettiamo, [inline]j <= std::ceil(a - 0.5 )[/inline] è equivalente a [inline]j <= std::round( a )[/inline]. Inoltre usare [inline]pow[/inline] per la potenza di [inline]-1[/inline] è 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:
#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):
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:
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.
Ciao a tutti,
Non capisco perché nel primo punto mi da errore nella formula del pi e mi dice "cannot convert complex double to double assignment ".
Grazie
Non capisco perché nel primo punto mi da errore nella formula del pi e mi dice "cannot convert complex double to double assignment ".
Grazie
@Planets: A quale codice stai facendo riferimento? Potresti mostrare le righe di codice e il relativo errore? Di solito problemi del genere possono essere dovuti alla mancanza di un punto e virgola, all'overloading di un operatore/funzione ad un tipo diverso da quello in cui ci si aspetta o ad un errore di disattenzione (scrivere per esempio qualcosa come 1.i al posto di un 1.f).
Il programma riguarda il primo punto dell'esercizio ed è:
e mi da questo errore in corrispondenza della formula del pi: pi=pi+((pow(-1,j))*4/(2j+1)).
//Program that calculates the approximation of pi greek of degree n. #include <iostream> #include <cmath> using namespace std; int main () { double pi; int j, n; cout << "Scrivere il grado dell'approssimazione:\n"; cin >> n; while (j<=n) { j=0; pi=pi+((pow(-1,j))*4/(2j+1)); j++; } return 0; // indicate that program ended successfully } // end function main
e mi da questo errore in corrispondenza della formula del pi: pi=pi+((pow(-1,j))*4/(2j+1)).
Hai scritto
2jinvece che
2 * j. Senza il prodotto diventa un numero complesso (il numero immaginario \(2\,i\)).
Ok capito grazie
in questo modo il primo punto dell'esercizio viene.
Il secondo punto invece chiede:
2. Sviluppare una seconda versione che prenda un double, ε, e calcoli un approssimazione di π di “grado
n” tale che |πi−πi−1|<ε
Il codice che ho scritto io è :
ma quando lo eseguo mi restituisce sempre lo stesso numero, come mai?
Grazie

Il secondo punto invece chiede:
2. Sviluppare una seconda versione che prenda un double, ε, e calcoli un approssimazione di π di “grado
n” tale che |πi−πi−1|<ε
Il codice che ho scritto io è :
#include <iostream> #include <cmath> using namespace std; int main () { double pi, e; int n,j; cout << "Scrivere il valore di epsilon:\n"; cin >> e; for ( j=0; j > ((4/e)-(1/2)); j++ ) pi=pi+((pow(-1,j))*4/(2*j+1)); cout << pi; return 0; }
ma quando lo eseguo mi restituisce sempre lo stesso numero, come mai?
Grazie
La variable [inline]pi[/inline] non è inizializzata. Quindi teoricamente assume valori casuali all'inizio.
Inoltre, la condizione [inline]j > ((4 / e) - (1 / 2))[/inline] è falsa per [inline]j = 0[/inline], quindi non entra nel ciclo. Nota inoltre che è meglio usare una variabile e cambiargli il segno invece di usare [inline]pow[/inline]. Inoltre, non usi la variabile [inline]n[/inline], e puoi definire [inline]j[/inline] direttamente nel [inline]for[/inline].
Inoltre, la condizione [inline]j > ((4 / e) - (1 / 2))[/inline] è falsa per [inline]j = 0[/inline], quindi non entra nel ciclo. Nota inoltre che è meglio usare una variabile e cambiargli il segno invece di usare [inline]pow[/inline]. Inoltre, non usi la variabile [inline]n[/inline], e puoi definire [inline]j[/inline] direttamente nel [inline]for[/inline].
Io comunque avrei seguito un diverso approccio per le condizioni e il ciclo:
double pi = 0.; // initial value double delta = 1.; double sign = 1.; for (double delta = 1.; e * delta < 1.; delta += 2., sign *= -1.) { pi += sign / delta; } pi *= 4;