Programma in C per un valore approssimato di $e$

pippo931
salve, ho trovato questo esercizio:

calcolare con un programma in c un'approssimazione della costante $e=1+1/(1!) +1/(2!)+1/(3!)+...$

questo è quello che ho prodotto finora e che ovviamente non funziona perchè ci sono delle cose che non ho capito:
io apro un ciclo che serve a ottenere il fattoriale di 2 che poi viene sommato ad e, ma finisce li. Dovrei aprire un altro ciclo while (infinito, perchè si tratta di una serie infinita) che incrementi di uno ogni volta il valore di num1, come faccio? Poi resta il fatto che un ciclo infinito di questo tipo, non finendo mai, non dovrebbe permettere che venga stampato alcunché, non mi è molto chiaro.
#include <stdio.h>
   main()
{
   float e=1;
   int num1=2; 
   int fattoriale;
  

 while (num1 >= 1)
    {  fattoriale=fattoriale*num1;
       num1--;}
   
e += 1/fattoriale;}


printf("%.2f\n", e);

return 0;

}




questo è un altro tentativo dello stesso programma che mi ha fatto nascere altri dubbi: intanto non so se si possa mettere un ciclo while dentro un altro ciclo while. Comunque il fatto è che num1 viene incrementato, poi viente calcolato il fattoriale e poi viene aggiunto ad e, ma quando ricomincia il ciclo num1 viene incrementato e quindi ci si aspetta che sia più grande di 1 del precedente valore, ma non credo sia così perchè durante il calcolo del fattoriale è stato ridotto a 1. Giusto?

#include <stdio.h>
   main()
{
   float e=1;
   int num1=1; 
   int fattoriale;
  
while (num1>0)
 
 {num1=num1+1;

 while (num1 >= 1)
    {  fattoriale=fattoriale*num1;
       num1--;}
   
e += 1/fattoriale;


printf("%.2f\n", e);}

return 0;
}

Risposte
_Tipper
I cicli while non possono essere infiniti, puoi decidere a priori un limite massimo $N$ di volte per cui venga eseguito.
Inoltre, per calcolare il fattoriale, ti conviene scrivere una funzione ad hoc, altrimenti c'è il rischio di fare confusione con i vari cicli.

alberto.cena
Sono d'accordo cin Tipper, scrivere una funzione ad hoc per il fattoriale è sicuramente più chiaro. Ma in questo caso è richiesto il fattoriale di numeri successivi e non c'e' bisogno di inserire dei cicli per fattoriale.
Ho così aggiustato il codice di pippo93

#include
main()
{
int n = 50;
double e=1;
int num1=1;
double fattoriale=1;

while(num1 fattoriale=fattoriale*num1;
e += 1/fattoriale;
num1++;
}

printf("%.12f\n", e);

return 0;
}

pippo931
ah , ho capito. L'approssimazione consisteva quindi nel troncare la serie di $1+1/(1!)+1/(2!)+...$, come 5InGold ah fatto arrivato a 50, giusto?
Pensavo che l'approssimazione si dovesse basare solo sulla precisione della virgola. Una curiosità, quindi non è possibile creare un programma che calcoli una serie infinita, bisogna sempre porre un limite?

alberto.cena
Sicuramente non si puo' fare un programma che calcoli una somma di infiniti addendi....
Un programma che contiene il seguente codice
 int i=1;  
 double somma=0;
 while(i>0){
     somma=somma+1/i;
     i++;}

viene compilato senza errori ma, lanciando l'eseguibile ottenuto in output, inizia un ciclo che non ha termine (a meno di cedimenti del PC).
La somma di una serie è il limite delle sue ridotte, somme con finiti addendi. Secondo la precisione desiderata, prendi la ridotta con un numero di addendi opportuno. Ad esempio, se vuoi sapere dove troncare la serie per il calcolo di una approssimazione di $e$ con precisione data, come al solito non c'e' bisogno di andare lontano e una risposta la puoi trovare su matematicamente.it:
http://www.matematicamente.it/approfondimenti/matematica/sul_calcolo_di_pi_greco_e_del_numero_di_nepero_200709051481/

La tua curiosità mi ha fatto tornare in mente questo bellissimo racconto di Cesare Zavattini da I tre libri, Parliamo tanto di me, 1931.
Come il ciclo innescato da questo codice anche la gara di matematica non ha termine...


E’ un ricordo della mia infanzia. Abitavo a Gottinga nel dicembre del milleottocentosettanta. Mio padre ed io giungemmo all’Accademia quando il presidente Maust stava cominciando l’appello dei partecipanti alla Gara Mondiale di Matematica. Subito babbo andò a mettersi fra gli iscritti dopo avermi affidato alla signora Katten, amica di famiglia.

Seppi da lei che il colpo del cannone di Pombo, il bidello, avrebbe segnato l’inizio della storica contesa. La signora Katten mi raccontò un episodio, ignoto ai più, intorno all’attività di Pombo. Costui sparava da trent’anni un colpo di cannone per annunciare il mezzogiorno preciso. Una volta se n’era dimenticato. Il dì appresso, allora, aveva sparato il colpo del giorno prima, e così di seguito fino a quel venerdì del milleottocentosettanta, Nessuno a Gottinga si era mai accorto che Pombo sparava il colpo del giorno avanti.

Esauriti i preliminari, la gara ebbe inizio alla presenza del principe Ottone e di un ragguardevole gruppo di intellettuali.

“Uno, due, tre, quattro, cinque… " Nella sala si udivano soltanto le voci dei gareggianti.

Alle diciassette circa, avevano superato il ventesimo migliaio. Il pubblico si appassionava alla nobile contesa e i commenti si intrecciavano. Alle diciannove, Alain, della Sorbona, si accasciò sfinito.

Alle venti, i superstiti erano sette.

”36767, 36768, 36769, 36770…”

Alle ventuno Pombo accese i lampioni. Gli spettatori ne approfittarono per mangiare le provviste portate da casa. “40719, 40720, 40721…”

Io guardavo mio padre, madido di sudore, ma tenace. La signora Katten accarezzandomi i capelli ripeteva come un ritornello: ’Che bravo babbo hai,’ e a me non pareva neppure di avere fame. Alle ventidue precise avvenne il primo colpo di scena: l’algebrista Pull scattò:

"’Un miliardo "

Un oh di meraviglia coronò l’inattesa sortita; si restò tutti col fiato sospeso.

Binacchi , un italiano, aggiunse issofatto:

“’Un miliardo di miliardi di miliardi.’ Nella sala scoppiò un applauso subito represso dal Presidente. Mio padre guardò intorno con superiorità, sorrise alla signora Katten e cominciò:

“’Un miliardo di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi…’

La folla delirava: "Evviva, evviva." La signora Katten e io, stretti uno all’altro, piangevamo dall’emozione.

“…di miliardi di miliardi di miliardi di miliardi di miliardi di miliardi.’

Il presidente Maust, pallidissimo, mormorava a mio padre, tirandolo per le falde della palandrana: ’Basta, basta, le farà male.’ Mio padre seguitava fieramente:

“… di miliardi di miliardi di miliardi di miliardi ...’ A poco a poco la sua voce si smorzò, l’ultimo fievole di miliardi gli uscì dalle labbra come un sospiro, indi si abbattè sfinito sulla sedia. Gli spettatori in piedi lo acclamavano freneticamente.

Il principe Ottone gli si avvicinò e stava per appuntargli una medaglia sul petto quando Gianni Binacchi urlò:

"Più uno!"

La folla precipitatasi nell’emiciclo portò in trionfo Gianni Binacchi. Quando tornammo a casa, mia madre ci aspettava ansiosa alla porta. Pioveva. Il babbo, appena sceso dalla diligenza, le si gettò tra le braccia singhiozzando: "Se avessi detto più due avrei vinto io."

pippo931
ho modificato un po' il programma di 5InGold e ho fatto questo:


#include <stdio.h>
#include <math.h>
main()
{
double e=1;
int num1=1; 
double fattoriale=1;


 while (num1>0){  
fattoriale=fattoriale*num1;
e += 1/fattoriale;
num1++;
printf("%.50f\n", e);
}
return 0;
}


in pratica è un ciclo infinito che però riscrive ogni volta il valore di e rendendolo più preciso, anche se poi è inutile per le poche postazioni decimali, giusto?

pippo931
il fatto è che come ha detto Sergio sono proprio alle prime armi, è da una settimana che leggo un manuale di C. Non ho ancora studiato le funzioni e molte parti dei programmi da voi postati mi sono incomprensibili

pippo931
"Sergio":
[quote="pippo93"]il fatto è che come ha detto Sergio sono proprio alle prime armi, è da una settimana che leggo un manuale di C. Non ho ancora studiato le funzioni e molte parti dei programmi da voi postati mi sono incomprensibili

E allora.... dammi retta: segui il manuale (e, appena puoi, il Kernighan & Ritchie) e prova a copiare - compilare - eseguire gli esempi che ti propone passo passo. Prova a cambiarli poco per vedere che succede (vecchio trucco del programmatore esperto: solo una modifica alla volta!).
Il C è un linguaggio stupendo, ma molto delicato: non ti protegge dagli errori, ti consente di fare cose "vergognose" (come cambiare i singoli bit della rappresentazione binaria di un numero in virgola mobile), ma devi prima imparare i rudimenti, devi prima acquisire piena familiarità con i suoi costrutti. Come dice Stroustrup, "col C è facile spararsi sui piedi"; Kernighan e Ritchie (che il linguaggio l'hanno creato) replicano che col C puoi fare quello che vuoi, a condizione di sapere cosa stai facendo.
Non avere fretta. Se ancora non hai studiato le funzioni, cosa succederà quando affronterai i puntatori? Strumento potentissimo, ma.... errori sia banali che perversi sono sempre dietro l'angolo!
Vai con calma.
Accontentati di piccoli passi.
Arriverai lontano.

Motto consigliato: Un cammino di mille leghe comincia con un passo.[/quote]

Ha ragione Sergio, ho chiesto cose che vanno oltre i miei livelli di comprensione, mi scuso con tutti

vict85
"Sergio":


2) Calcolo del fattoriale con la funzione Gamma

La funzione Gamma permette di spingersi oltre. Si sa, infatti, che $Gamma(n+1)=n!$ e si può approfittare del maggior numero di cifre significative del tipo long double.
Occorre fare attenzione anche qui all'overflow, ma, trattandosi di operazioni in virgola mobile, questo viene rilevato in esecuzione e si può usare la variabile errno.
Temo che l'implementazione di Gamma non sia uguale in tutti i compilatori. Nel mio caso (Linux Kubuntu a 32 bit, compilatore gcc 4.2.3, glibc 2.7) i long double hanno 18 cifre significative (valore della costante LDBL_DIG), va usata la funzione tgammal() e si deve chiamare feclearexcept(FE_ALL_EXCEPT) prima di tgammal() per rilevare errori nella variabile errno:

#include <stdio.h>
#include <math.h>
#include <errno.h>
#include <fenv.h>
#include <float.h>

int main() {
    long double e, d, term;

    e = 2.0;                   /* e = 1 + 1/1! */
    d = 3.0;
    errno = 0;
    while(1) {
        feclearexcept(FE_ALL_EXCEPT);
        term = tgammal(d);      /* tgammal(d) == (d-1)! */
        if (errno != 0)
            break;
        e += 1.0/term;
        d += 1.0;
    }
    printf("valore approssimato di e: %.*Lf\n", LDBL_DIG, e);
}


Da notare che il ciclo "while(1) {...}" è un ciclo definito come se fosse infinito (1 rimane sempre 1, cioè vero, e non diventa mai 0, cioè falso), ma in realtà termina dopo un numero finito di passi grazie al break che scatta quando l'argomento passato a tgammal() risulta troppo grande.


1) Chiamo $P_n$ e $Q_n$ il numeratore e il denominatore dell'n-simo convergente della serie. Abbiamo che $P_0 = 1$, $\ P_n = nP_(n-1) + 1$, $\ Q_n = n!\ $ cosa che è facilmente derivabile dalla serie... Personalmente credo che il modo più efficiente sia quello di calcolare il numeratore per ricorsione (o con un ciclo) e calcolare il denominatore con la funzione gamma.
Tra l'altro è inutile farlo andare avanti infinitamente perché già a $25$ l'errore rispetto al valore reale è circa -$2,5748*10^(-27)$ (calcolato con PARI/GP). Di fatto non ha senso andare oltre la 20-sima, proseguendo si sommano soltanto errori.

2) l'utilizzo delle frazioni continue (che nel caso del numero di eulero è piuttosto semplice) permette valori ancora migliori. La frazione generata con 25 passaggi dell'algoritmo scritto sopra (equivalente a quello scritto all'inizio) è $85351903640077042215979/31399210614030336000000$ che è molto al di sopra del limite di un unsigned long long. Utilizzando le frazioni continue si arriva alla frazione $5739439214861417731/2111421691000680031\ $ (che è una delle due migliori approssimazioni possibili di $e$ con denominatore inferiore a $2^64$, in questo caso anche il numeratore lo è) che approssima il valore di $e$ alla 38-esima cifra decimale e dato che LDBL_MANT_DIG in gcc è 64 entrambi i numeri sono tranquillamente memorizzabili in un long double.

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