[C++] programma sommatoria con ciclio

zerbo1000
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:
#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 $| | $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 $| |
#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
Super Squirrel
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.

vict85
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.

zerbo1000
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++


#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;

}


Super Squirrel
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;
}

vict85
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:
#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.

Planets
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

apatriarca
@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).

Planets
Il programma riguarda il primo punto dell'esercizio ed è:

//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)).

apatriarca
Hai scritto
2j
invece che
2 * j
. Senza il prodotto diventa un numero complesso (il numero immaginario \(2\,i\)).

Planets
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 è :

#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

vict85
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].

vict85
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;

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.