Derivate C++ ?

Messaggioda Vincenzo98 » 26/02/2017, 15:38

Codice:
#include <stdio.h>
#include <math.h>
// Calcola la derivata di una funzione in un punto dato

float fnc(float x)
{
/*f(x)=x^2+cos(x)
Funzione di prova */
return powf(x,2)+cosf(x); //Immettere qui la funzione

}
int main()
{

float d,d1,x,x1;
int n;
d=0;
printf("Introdurre il valore del punto: ");
scanf("%f",&x1);
for(n=0;n<10;n++)
{
d1=d;
x=x1+powf(.5,n);
d=(fnc(x)-fnc(x1))/(x-x1);
}
printf("La derivata nel punto %f e pari a %f\n",x1,2*d-d1);
}


Come " generalizzo " il programma ? Ossia vorrei che il programma mi deve far inserire una funzione qualsiasi e il punto e stamparmi poi il risultato.

Grazie.
Vincenzo98
New Member
New Member
 
Messaggio: 31 di 82
Iscritto il: 16/02/2014, 21:23

Re: Derivate C++ ?

Messaggioda vict85 » 26/02/2017, 21:45

Dipende da quello che intendi con generalizzare. Ci sono comunque varie critiche che mi vengono al tuo codice iniziale:
  • Dici che è C++ ma scrivi un codice in c90 con la sola eccezione dei commenti in linea. Definire le variabili ad inizio dei blocchi non è più richiesto dal C e non lo è mai stato dal C++, e il C++ sfrutta l'overloading di funzioni per ignorare varie cose a cui invece tu fai attenzione.
  • Hai uno strano amore per la funzione pow anche se in entrambi i casi esistono scelte migliori. Scrivere x*x è sempre meglio di pow(x,2) e per le potenze di due puoi usare ldexp e varianti.
  • Fai un ciclo ma usi solo gli ultimi due valori calcolati. Insomma fai calcoli inutili.
  • Ricalcoli x-x1 anche se sai esattamente quale sia il valore ed inoltre potresti tranquillamente moltiplicare per il suo inverso invece che dividere per la differenza (per esempio usando ldexp).
vict85
Moderatore
Moderatore
 
Messaggio: 9025 di 19253
Iscritto il: 16/01/2008, 00:13
Località: Berlin

Re: Derivate C++ ?

Messaggioda apatriarca » 26/02/2017, 22:14

Che cosa intendi dire con "far inserire una funzione qualsiasi"? Vuoi che l'utente inserisca la funzione come stringa? Se è così allora è necessario implementare un parser che legga la stringa e ne interpreti l'espressione. Potrebbe essere sufficiente un "recursive descent parser" o qualcosa come gli algoritmi "shunting yard" o "precedence climbing".

Per quanto riguarda la derivazione della funzione, avendo accesso all'espressione simbolica potresti fare di meglio rispetto a quello che stai facendo ora. Il metodo più semplice fa uso di un sistema numerico chiamato "numeri duali". Sono numeri simili ai numeri complessi, in cui la base \(\epsilon\) ha la proprietà \(\epsilon^2 = 0.\) Sono cioè numeri nella forma \( a + b\,\epsilon \) con le seguente operazioni:
\[ (a + b\,\epsilon) + (c + d\,\epsilon) = (a + c) + (b + d)\,\epsilon \quad \mathrm{e} \quad (a + b\,\epsilon)\,(c + d\,\epsilon) = a\,c + (a\,d + b\,c)\,\epsilon. \]
Se in questa espressione supponiamo che il coefficiente di \(\epsilon\) sia la derivata e l'altro sia il valore calcolati nello stesso punto, vediamo che le formule sopra corrispondono alla somma e prodotto di derivate. Estendendo la definizione delle varie funzioni che ci interessano a questo sistema numerico possiamo quindi calcolare derivata e valore di una espressione generica usando semplicemente questi numeri invece dei numeri reali. Per esempio, supponiamo di voler calcolare \(x^2 + \cos(x)\) usando questo metodo. Definiamo \(\cos(a + b\,\epsilon) = \cos(a) - \sin(a)\,b\,\epsilon\). Facendo i calcoli si vede quindi che
\[\begin{align*}
(x + \epsilon)^2 + \cos(x + \epsilon) &= x^2 + 2\,x\,\epsilon + \cos(x) - \sin(x)\,\epsilon \\ &= \bigl(x^2 + \cos(x)\bigr) + \bigl( 2\,x - \sin(x) \bigr)\,\epsilon.
\end{align*}\]
Nota che ho scelto la derivata del valore \(x\) uguale a \(1\). Nota inoltre che anche se ho svolto i calcoli per mostrarti il risultato, il calcolo delle derivate con questo metodo è automatico e numerico. L'unico aspetto importante è quello di aver definito le varie funzioni per lavorare con questo sistema numerico.

Esiste anche un altro metodo di derivazione che funziona al contrario e che fa uso della formula di derivazione delle funzioni composte. Consiste nel costruirsi una specie di grafo da cui ricavare tutte le formule. Il metodo con i numeri duali è molto più semplice per cui non aggiungo altro su questo metodo a meno che tu non sia interessato.
apatriarca
Moderatore
Moderatore
 
Messaggio: 4555 di 10436
Iscritto il: 08/12/2008, 20:37
Località: Madrid

Re: Derivate C++ ?

Messaggioda Vincenzo98 » 27/02/2017, 15:35

apatriarca ha scritto:Che cosa intendi dire con "far inserire una funzione qualsiasi"? Vuoi che l'utente inserisca la funzione come stringa? Se è così allora è necessario implementare un parser che legga la stringa e ne interpreti l'espressione. Potrebbe essere sufficiente un "recursive descent parser" o qualcosa come gli algoritmi "shunting yard" o "precedence climbing".

Per quanto riguarda la derivazione della funzione, avendo accesso all'espressione simbolica potresti fare di meglio rispetto a quello che stai facendo ora. Il metodo più semplice fa uso di un sistema numerico chiamato "numeri duali". Sono numeri simili ai numeri complessi, in cui la base \(\epsilon\) ha la proprietà \(\epsilon^2 = 0.\) Sono cioè numeri nella forma \( a + b\,\epsilon \) con le seguente operazioni:
\[ (a + b\,\epsilon) + (c + d\,\epsilon) = (a + c) + (b + d)\,\epsilon \quad \mathrm{e} \quad (a + b\,\epsilon)\,(c + d\,\epsilon) = a\,c + (a\,d + b\,c)\,\epsilon. \]
Se in questa espressione supponiamo che il coefficiente di \(\epsilon\) sia la derivata e l'altro sia il valore calcolati nello stesso punto, vediamo che le formule sopra corrispondono alla somma e prodotto di derivate. Estendendo la definizione delle varie funzioni che ci interessano a questo sistema numerico possiamo quindi calcolare derivata e valore di una espressione generica usando semplicemente questi numeri invece dei numeri reali. Per esempio, supponiamo di voler calcolare \(x^2 + \cos(x)\) usando questo metodo. Definiamo \(\cos(a + b\,\epsilon) = \cos(a) - \sin(a)\,b\,\epsilon\). Facendo i calcoli si vede quindi che
\[\begin{align*}
(x + \epsilon)^2 + \cos(x + \epsilon) &= x^2 + 2\,x\,\epsilon + \cos(x) - \sin(x)\,\epsilon \\ &= \bigl(x^2 + \cos(x)\bigr) + \bigl( 2\,x - \sin(x) \bigr)\,\epsilon.
\end{align*}\]
Nota che ho scelto la derivata del valore \(x\) uguale a \(1\). Nota inoltre che anche se ho svolto i calcoli per mostrarti il risultato, il calcolo delle derivate con questo metodo è automatico e numerico. L'unico aspetto importante è quello di aver definito le varie funzioni per lavorare con questo sistema numerico.

Esiste anche un altro metodo di derivazione che funziona al contrario e che fa uso della formula di derivazione delle funzioni composte. Consiste nel costruirsi una specie di grafo da cui ricavare tutte le formule. Il metodo con i numeri duali è molto più semplice per cui non aggiungo altro su questo metodo a meno che tu non sia interessato.



#include <iostream>
#include <math.h>
using namespace std;


float funzione(float x){
return 6*sin(x)+2*powf(x,2)+3*x+1;
}

int main(){
float d=0,d1,x,x1;int n;
cout << "Calcolo della derivata nel punto x="; cin >> x1;
for(n=0;n<10;n++){
d1=d;x=x1+powf(.5,n);d=(funzione(x) - funzione(x1))/(x-x1);}
cout << "La derivata nel punto x="<<x1<<" e' uguale a "<<2*d-d1<< endl;
system("pause");}

Il codice precedente l'ho scritto seguendo questo codice che abbiamo scritto in classe. Ma non ho ben capito in questo codice cosa vogliono dire queste tre stringhe nell'ambito delle derivate.
for(n=0;n<10;n++){
d1=d;x=x1+powf(.5,n);d=(funzione(x) - funzione(x1))/(x-x1);}
cout << "La derivata nel punto x="<<x1<<" e' uguale a "<<2*d-d1<< endl;

Grazie.
Vincenzo98
New Member
New Member
 
Messaggio: 32 di 82
Iscritto il: 16/02/2014, 21:23

Re: Derivate C++ ?

Messaggioda apatriarca » 27/02/2017, 15:42

La definizione di derivata di una funzione in un punto è
\[ \frac{df(x_0)}{dx} = \lim_{x \to x_0} \frac{f(x) - f(x_0)}{(x - x_0)} \]
Stai quindi semplicemente usando la definizione di derivata. Per ottenerne una approssimazione. Ma il metodo che ti ho scritto è superiore in quanto preciso e automatico.
apatriarca
Moderatore
Moderatore
 
Messaggio: 4557 di 10436
Iscritto il: 08/12/2008, 20:37
Località: Madrid

Re: Derivate C++ ?

Messaggioda Vincenzo98 » 27/02/2017, 16:05

apatriarca ha scritto:La definizione di derivata di una funzione in un punto è
\[ \frac{df(x_0)}{dx} = \lim_{x \to x_0} \frac{f(x) - f(x_0)}{(x - x_0)} \]
Stai quindi semplicemente usando la definizione di derivata. Per ottenerne una approssimazione. Ma il metodo che ti ho scritto è superiore in quanto preciso e automatico.


Ah ok ok.

Un'ultima cosa volevo chiederti: il significato di queste due stringhe.
for(n=0;n<10;n++){
d1=d;x=x1+powf(.5,n);

Grazie in anticipo per la tua disponibilità.
Vincenzo98
New Member
New Member
 
Messaggio: 33 di 82
Iscritto il: 16/02/2014, 21:23

Re: Derivate C++ ?

Messaggioda apatriarca » 27/02/2017, 18:15

Ma te l'ho scritto.. Stai scegliendo valori di \(x\) sempre più vicini a \(x_0\). Tuttavia il codice non ha senso per numerosi motivi come ti ha scritto @vict85 più in alto e come credo avessi già commentato in una precedente discussione.
apatriarca
Moderatore
Moderatore
 
Messaggio: 4559 di 10436
Iscritto il: 08/12/2008, 20:37
Località: Madrid

Re: Derivate C++ ?

Messaggioda vict85 » 27/02/2017, 19:59

Sostituendo d1 con \(\displaystyle d' \), x1 con \(\displaystyle x \) e x con \(\displaystyle x + h \) e usando i pedici per indicare il valore alla fine del ciclo \(\displaystyle n \), le variabili soddisfano le seguenti proprietà: \[\begin{align*}
d_n &= \frac{f(x + h_n) - f(x)}{h_n} \\
d'_n &= d_{n-1} \\
h_n &= 2^{-n}
\end{align*}\] Risulta quindi evidente che \(\displaystyle d_n \) non dipende da \(\displaystyle d_{n-1} \) e che quindi il ciclo risulta totalmente inutile: potevi calcolarti direttamente \(\displaystyle d_{9} \) e \(\displaystyle d_{10} \). Se gli altri difetti del codice potrebbero essere catalogati come "codice antiquato" , questo aspetto manda da solo l'intero codice nella categoria dei codici scritti e pensati male.

Tra l'altro \[\begin{align*} 2d_{10} - 2d_{9} &= 2^{11} \bigl(f(x+2^{-10}) - f(x) \bigr) - 2^{9} \bigl(f(x+2^{-9}) - f(x) \bigr) \\
&= 2^{9} \bigl( 4f(x+2^{-10}) - 3f(x) -f(x+2^{-9}) \bigr)\end{align*}\] che poteva essere calcolato direttamente senza passare dai valori \(\displaystyle d_n \).
vict85
Moderatore
Moderatore
 
Messaggio: 9026 di 19253
Iscritto il: 16/01/2008, 00:13
Località: Berlin

Re: Derivate C++ ?

Messaggioda Vincenzo98 » 27/02/2017, 21:05

vict85 ha scritto:Sostituendo d1 con \(\displaystyle d' \), x1 con \(\displaystyle x \) e x con \(\displaystyle x + h \) e usando i pedici per indicare il valore alla fine del ciclo \(\displaystyle n \), le variabili soddisfano le seguenti proprietà: \[\begin{align*}
d_n &= \frac{f(x + h_n) - f(x)}{h_n} \\
d'_n &= d_{n-1} \\
h_n &= 2^{-n}
\end{align*}\] Risulta quindi evidente che \(\displaystyle d_n \) non dipende da \(\displaystyle d_{n-1} \) e che quindi il ciclo risulta totalmente inutile: potevi calcolarti direttamente \(\displaystyle d_{9} \) e \(\displaystyle d_{10} \). Se gli altri difetti del codice potrebbero essere catalogati come "codice antiquato" , questo aspetto manda da solo l'intero codice nella categoria dei codici scritti e pensati male.

Tra l'altro \[\begin{align*} 2d_{10} - 2d_{9} &= 2^{11} \bigl(f(x+2^{-10}) - f(x) \bigr) - 2^{9} \bigl(f(x+2^{-9}) - f(x) \bigr) \\
&= 2^{9} \bigl( 4f(x+2^{-10}) - 3f(x) -f(x+2^{-9}) \bigr)\end{align*}\] che poteva essere calcolato direttamente senza passare dai valori \(\displaystyle d_n \).


Ok grazie.
Vincenzo98
New Member
New Member
 
Messaggio: 34 di 82
Iscritto il: 16/02/2014, 21:23

Re: Derivate C++ ?

Messaggioda vict85 » 27/02/2017, 23:52

Mi sono reso conto di aver sbagliato prima: il ciclo si ferma a 9 e non a 10. Comunque, ho implementato una versione del tuo codice, scrivendolo in un C++ più attuale e mettendo a confronto i vari metodi. La parte più complessa da capire è il metodo suggerito da apatriarca (che ho implementato usando i template ma non è obbligatorio).

Codice:
// math.h
#include <cmath>

#include <iostream>

using namespace std;

template <class T>
T func(const T x)
{
   return x*x + cos(x);
}

float derivata(const float x)
{
   return 2.f*x - sin(x);
}

struct Jet
{
   float x, eps;
};

Jet operator+(const Jet& a, const Jet& b)
{
   return Jet{ a.x + b.x, a.eps + b.eps };
}

Jet operator*(const Jet& a, const Jet& b)
{
   return Jet{ a.x * b.x, a.x * b.eps + a.eps * b.x };
}

Jet cos(const Jet& a)
{
   return Jet{ cos(a.x), -sin(a.x) * a.eps };
}

int main()
{
   Jet x = Jet{ 0, 1 };
   cout << "Inserisci il punto:\t";
   cin >> x.x;

   cout << "Derivata analitica" << endl;
   cout << "La derivata della funzione nel punto " << x.x
      << " e' uguale a " << derivata(x.x) << endl;

   cout << endl << endl;
   cout << "Metodo Professore" << endl;

   float d = 0.f, d_ = 0.f;
   float x_ = 0.f;
   for (int i = 0; i < 10; i++)
   {
      d_ = d;
      x_ = x.x + ldexp(1.f, -i);
      d = (func(x_) - func(x.x)) / (x_ - x.x);
   }

   cout << "La derivata della funzione nel punto " << x.x
      << " e' uguale a " << 2 * d - d_ << endl;

   cout << endl << endl;
   cout << "Eliminazioni cicli ignorati" << endl;

   const float h_1 = ldexp(1.f, -9);
   const float h_2 = ldexp(1.f, -8);
   const float f = func(x.x);
   const float d_1 = ldexp(func(x.x + h_1) - f, 9);
   const float d_2 = ldexp(func(x.x + h_2) - f, 8);
   
   cout << "La derivata della funzione nel punto " << x.x
             << " e' uguale a " << 2*d_1 - d_2 << endl;

   cout << endl << endl;
   cout << "Eliminazione ulteriori passaggi" << endl;

   const float f_1 = func(x.x + h_1);
   const float f_2 = func(x.x + h_2);

   cout << "La derivata della funzione nel punto " << x.x
      << " e' uguale a " << ldexp( 4*f_1 - 3*f -f_2, 8 ) << endl;

   cout << endl << endl;
   cout << "Metodo apatriarca" << endl;
   cout << "La derivata della funzione nel punto " << x.x
      << " e' uguale a " << func(x).eps << endl;

   cout << endl << endl;
   cout << "Altra formula numerica (central difference)" << endl;

   const float f_ = func(x.x - h_1);

   cout << "La derivata della funzione nel punto " << x.x
      << " e' uguale a " << ldexp(f_1 - f_, 8) << endl;
}
vict85
Moderatore
Moderatore
 
Messaggio: 9027 di 19253
Iscritto il: 16/01/2008, 00:13
Località: Berlin


Torna a Informatica

Chi c’è in linea

Visitano il forum: Nessuno e 1 ospite