/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* Risolprossimatore by Matteo "netarrow" Tomasulo, netarrow@gmail.com
* Admin @ http://www.pierotofy.it
*
* Programma che data un'equazione di qualsiasi grado in forma canonica a tentativi
* cerca una soluzione valida che annulla il polinomio a 0 o ad una sua approssimazione.
*/
#include <iostream>
#include <cmath>
using namespace std;
int grado; // grado dell'equazione
double l, r, x; // l ed r servono per calcolare il punto medio, x è la variabile
double* coefficienti; // array di coefficienti
double noto; // è il termine noto dell'equazione
// Dichiarazione dei prototipi di funzione
double f(double);
double risolvi(void);
void cres(void);
void decres(void);
// Questa funzione risolve il polinomio in forma canonica
double f(double x) {
double sommaX = 0;
for(int i = 0; i < grado; i++) {
// somma tutti i coefficienti moltiplicandoli per x elevata al suo grado
// essendo ordinato più in la è il coefficiente più basso è il grado, quindi (grado - i)
sommaX += coefficienti[i] * pow(x, grado - i);
}
sommaX += noto; // aggiungi il termine noto
return sommaX;
}
/*
* Questa funzione tenta di trovare due valori di f(x) a cavallo dello 0;
* può capitare che con un pò di fortuna si trovi proprio un valore che annulli
* esattamente il polinomio.
*/
double risolvi(void) {
x = 0;
if( grado == 1 )
return -noto / coefficienti[0]; // se il grado è uno, basta spostare i termini
/*
In altro caso bisogna controllare "per che verso" la funzione va a -infinito
e in quale a +infinito, calcolando quindi f(0) si guarda se positivo o negativo
e in base al verso si aumento o si decrementa x per trovare un valore di segno
opposto, essendo a cavallo dello 0 calcolando il punto medio ci avvicineremo
ad una sua approssimazione
*/
if( f(x) > 0 ) { // quando f(0) è positiva
if( f(x - 1) < f(x) ) { // se la funzione si rimpicciolisce diminuendo x di uno
for(; f(x) > 0; x--) { // continua a dimunuirla fino a raggiungere o superare 0
cout << "\nf(" << x << ") = " << f(x); // stampa i passi
}
}
else if( f(x + 1) < f(x) ) { // se la funzione rimpicciolisce aumentando x di uno
for(; f(x) > 0; x++) { // continua ad aumentarla fino a raggiungere o superare 0
cout << "\nf(" << x << ") = " << f(x); // stampa i passi
}
}
for(l = x; f(l) < 0; l++) { // partando da x, trova un valore l di segno opposto in modo
// da avere due valori a cavallo dello 0 (quindi la soluzione sarà circa a metà)
cout << "\nf(" << l << ") = " << f(l); // stampa i passi
}
}
else if( f(x) < 0 ) { // quando invece f(0) è negativa
if( f(x - 1) > f(x) ) { // controlla se diminuendo x di uno f(x) cresce
for(; f(x) < 0; x--) { // continua a diminuire fino a raggiungere o superare 0
cout << "\nf(" << x << ") = " << f(x); // stampa i passi
}
}
else if( f(x + 1) > f(x) ) { // quando invece f(x) aumenta aumentando di uno x
for(; f(x) < 0; x++) { // continua ad aumentare fino a raggiungere o superare 0
cout << "\nf(" << x << ") = " << f(x); // stampa i passi
}
}
for(l = x; f(l) > 0; l++) { // partendo da x, trova il primo valore opposto per avere lo 0 circa a metà
cout << "\nf(" << l << ") = " << f(l); // stampa i passi
}
}
// Se dopo queste operazioni x o l è arrivato proprio a 0, esci e ritorna la x o la l trovata
if(f(x) == 0) {
cout << "\nf(" << x << ") = " << f(x) << endl;
return x;
}
else if(f(l) == 0) {
cout << "\nf(" << l << ") = " << f(l) << endl;
return l;
}
// Altrimenti crea un puntatore a funzione che punterà a decres o a cres
// a seconda che f(x) sia crescente o decrescente
void (*cut)(void);
if(f(x) > 0) cut = decres;
else cut = cres;
r = x; // memorizza la x di partenza (serve per calcolare il punto medio)
while( !((int)f(x) == 0) ) { // finchè il risultato non contiene 0 nella parte intera
x = (r + l) / 2; // calcola il punto medio
cut(); // riduci il gap
cout << "\nf(" << x << ") = " << f(x); // stampa i passi
} cout << endl; // riga vuota
return x; // ritorna la x trovata
}
// Funzione che riduce il gap per funzioni crescenti
inline void cres(void) {
if(f(x) > 0) {
l = x;
}
else {
r = x;
}
}
// Funzione che riduce il gap per funzioni decrescenti
inline void decres(void) {
if(f(x) > 0) {
r = x;
}
else {
l = x;
}
}
// MAIN, partenza programma
int main(void) {
// Inserire grado maggiore di 0
do {
cout << "Inserire grado dell'equazione: ";
cin >> grado;
} while( grado <= 0 );
coefficienti = new double[grado]; // alloca la memoria per i coefficienti
// inserisci il polinomio
for(int i = 0; i < grado; i++) {
cout << endl;
cout << "Inserire coefficiente di x^" << (grado - i) << ": ";
cin >> coefficienti[i];
}
cout << endl;
cout << "Inserire termine noto: ";
cin >> noto;
cout << "\nATTENZIONE: Se l'equazione è impossibile o ha altri intoppi il\n" \
<< "programma potrebbe andare in loop infinito, in questo caso\n" \
<< "terminalo con CTRL-C o CTRL-Z o CTRL-D a seconda del tuo sistema.\n\n" \
<< "Premi un tasto e [INVIO] per iniziare a risolvere: ";
char conferma;
cin >> conferma;
cout << endl;
risolvi(); // richiama risolvi
// visualizza un riassunto dei risultati ottenuti
cout << endl << "Con x = " << x << " ottengo " << (f(x) == 0 ? "proprio ":"l'approssimazione ") << f(x) << endl;
// Termina il programma restituendo l'exit code 0: "Tutto finito ok";
return 0;
}