Domanda Metodo di Newton per calcolare soluzione approssimata di f(x)=logx+x

MaximusXMeridius

Utente Iron
10 Febbraio 2022
2
1
0
2
Buon pomeriggio.Scusatemi,ho un problema nel seguente codice da me scritto(in linguaggio C). Si deve calcolare e stampare la soluzione approssimata e il numero di iterate svolte tramite il metodo di Newton(o delle tangenti) dell'equazione logx+x=0. Ora,la soluzione la approssima (credo,e più che altro spero,correttamente).Il problema è che il criterio di arresto del metodo consiste nell'arrestare il procedimento quando |x(i)-x(i-1)| sia minore di una certa tolleranza prefissata,o quando le iterate al massimo divengano 100. Quindi in teoria,se è la prima condizione a far terminare tutto,le iterate possono anche essere di numero minore di 100. Perché,almeno apparentemente,qualsiasi valore di a e b l'utente inserisce,il numero di iterazioni che viene stampato è sempre e comunque 100? Non mi torna questa cosa. Grazie mille a chi potrà e/o vorrà aiutarmi.Ecco a voi il codice intero:
C:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define TOLL 1.e-5
#define MAXITERAZIONI 100
void letturanumerireali_ab(double&,double&);
double estremodifourier(double&,double&);
int newton(double&,double);
int main(){
printf("Programma C++ che legge due numeri reali a e b,con a<b(estremi dell'intervallo in cui lo zero di f(x)=logx+x e'stato precedentemente localizzato e separato)e l'estremo di Fourier,cioe' l'approssimazione iniziale dello zero;stampa il numero di iterazioni compiute dal metodo di Newton per approssimare lo zero con errore minore di 10^-5\n(preventivando al massimo 100 iterazioni)e il valore approssimato dello zero fornito dal metodo\n");
double a,b;
letturanumerireali_ab(a,b);/*inserimento dei due reali a e b(a<b)estremi dell'intervallo in cui si trova lo zero di f(x):lo zero
                            semplice di f(x)appartiene ad [a,b]*/
/*Tramite gnuplot si vede,dal grafico della funzione,che lo zero semplice di f(x)=logx+x(inserita su gnuplot come x-exp(-x))
è circa x=0.567143*/
double x0=estremodifourier(a,b);//estremo di Fourier dell'intervallo [a,b]
int iterazioni=newton(x0,TOLL);//metodo di Newton(restituisce,come risultato,il numero di iterazioni eseguite)
//Dopo la chiamata della funzione newton,il valore x0 sarà diventato quello dell'approssimazione dello zero con il metodo di Newton
printf("L'approssimazione dello zero di f(x)=logx+x calcolata tramite il metodo di Newton e':%lf\n",x0);
printf("Il numero di iterazioni eseguite per approssimare lo zero di f(x)=logx+x tramite il metodo di Newton e' %d\n",iterazioni);
return 0;
}
//Inserimento di due numeri reali a e b,con a<b,in cui sarà stato precedentemente localizzato e separato lo zero:
void letturanumerireali_ab(double& a,double& b){
printf("Lo zero semplice della funzione f(x)=logx+x(calcolato con gnuplot),appartenente ad [a,b],e' uguale ad x=0.567143\n");
do{printf("Inserire il numero reale a,tale che a<b:\n");scanf("%lf",&a);
printf("Inserire il numero reale b,tale che a<b:\n");scanf("%lf",&b);}
    while(b<=a);
return;
}
//Calcolo dell'estremo di Fourier della funzione nell'intervallo [a,b] (esso rappresenterà l'approssimazione iniziale dello zero di f(x) ):
double estremodifourier(double& a,double& b)
{double estremo;
/*Per calcolare l'estremo di Fourier,devono essere verificate le ipotesi EF,secondo cui in [a,b]: f(x)sia di classe C1([a,b]),f(a)*f(b)<0,e
f(x) sia convessa,o concava,in [a,b] (cioè f'(x) sia non crescente,o non decrescente,in [a,b]).In questo caso,f(x)=logx+x è di classe
C1([a,b]).Proseguendo con le altre ipotesi,si ha:*/
if((log(a)+a)*(log(b)+b)<0){
/*Se f(a)*f(b)<0,si procede con la verifica dell'ultima ipotesi,secondo cui f(x) sia convessa o concava in [a,b].Per farlo,si utilizza la
derivata prima di f(x),cioè f'(x)=1/x + 1.Questa è monotona decrescente(quindi non crescente)nell'intorno [a,b](preso piccolo) dello zero
0.567143 di f(x),quindi è convessa in [a,b].Si prosegue quindi con il calcolo dell'estremo di Fourier x(0).
Per calcolare quale tra i due punti a e b sia l'estremo x(0) di Fourier dal quale partire con l'approssimazione dello zero di f(x),si deve
calcolare la derivata seconda f''(x) della funzione,che sarà uguale a f''(x)=-1/x^2.
Successivamente,per calcolare effettivamente l'estremo di Fourier,si calcola il prodotto tra f(x) e f''(x) nei punti a e b,e si effettua la
seguente scelta:*/
if((log(a)+a)*(-1*(1/pow(a,2)))>0) estremo=a; //se f(a)*f''(a)>0 l'estremo di Fourier x(0) sarà uguale all'estremo a di [a,b]
else estremo=b; //altrimenti,cioè se f(b)*f''(b)>0,l'estremo di Fourier x(0) sarà uguale all'estremo b di [a,b]
return estremo;}
else {printf("Non sono verificate le ipotesi dell'Estremo di Fourier per l'applicazione del metodo di Newton alla funzione f(x)=logx+x\n");
      return 1;}
 }
//Calcolo dell'approssimazione x(i) dello zero di f(x)=logx+x e del numero di iterazioni eseguite,tramite il metodo di Newton:
int newton(double& estremo,double tau){
/*Si passa una variabile(l'estremo di Fourier x(0) )per indirizzo perché la funzione deve restituire due output,cioè la soluzione
approssimata e il numero di iterazioni eseguite,e in genere una funzione può restituirne solo uno.*/
int cont=0;//contatore del numero di iterazioni che si eseguiranno nel metodo di Newton
double delta;//variabile che servirà per verificare la condizione del criterio di arresto del metodo,cioè |delta|<tau
double x_imeno1;//dichiarazione di x(i-1) del passo precedente dell'iterazione di Newton
do{x_imeno1=estremo;
   estremo=x_imeno1-((log(x_imeno1)+x_imeno1)/((1/x_imeno1)+1));//x(i)=x(i-1)- f(x(i-1))/f'(x(i-1))
   delta=fabs((estremo-x_imeno1));
   cont++;
   printf("%d\n",cont);
  }
 while(delta>=tau||cont<MAXITERAZIONI);/*condizione di arresto del metodo di Newton:si va avanti finché |x(i)-x(i-1)|è
                                                         maggiore o uguale a TOLL o finché il numero di iterazioni non è al massimo 100*/
return cont;
}
 
La condizione del do-while dev'essere while (delta >= tau && cont < MAXITERAZIONI); (con la and invece che con la or) perché il ciclo si deve fermare appena una delle due condizioni è falsa, per il resto mi sembra tutto giusto. Stai mischiando C e C++, che non è proprio una bella cosa. Se posso darti un consiglio, quando hai un errore e vuoi terminare il programma usa exit(1); invece di return 1; perché la funzione estremodifourier può restiture 1 sia come valore corretto che per segnalare un problema.
 
La condizione del do-while dev'essere while (delta >= tau && cont < MAXITERAZIONI); (con la and invece che con la or) perché il ciclo si deve fermare appena una delle due condizioni è falsa, per il resto mi sembra tutto giusto. Stai mischiando C e C++, che non è proprio una bella cosa. Se posso darti un consiglio, quando hai un errore e vuoi terminare il programma usa exit(1); invece di return 1; perché la funzione estremodifourier può restiture 1 sia come valore corretto che per segnalare un problema.
Aspetta,se deve essere falsa una delle due,perché ci va l'&& e non l'or? Mi ci sto incartando
 
Aspetta,se deve essere falsa una delle due,perché ci va l'&& e non l'or? Mi ci sto incartando
Il ciclo do-while si ripete finché la condizione all'interno del while è vera: A || B è vera se A è vera oppure se B è vera (o se sono vere entrambe), A && B è vera se A è vera e anche B è vera. Tu stavi usando A || B che è falsa (i.e., interrompe il ciclo) solo quando sia A che B sono false. Nel tuo caso delta >= tau diventava falsa dopo poche iterazioni, ma cont < MAXITERAZIONI diventava falsa solo dopo 100 iterazioni. Se 100 iterazioni non fossero bastate per ottenere un errore inferiore a 1.e-5 il tuo codice continuava a girare per più di 100 iterazioni.

Prova a riguardarti la tabella di verità di OR e AND se non sei ancora convinto.