[C] Program obl. miejsca zerowe metodą siecznych

0

Witam, mam problem z napisaniem programu w C. Ma on obliczać miejsca zerowe funkcji a+sin(bx)+c metodą siecznych. Treść zadania: http://yojo2.ids.pl/c/1.jpg

Problem jest taki że niby mam kod, ale nie działa on poprawnie. Poprawne dane wypisuje tylko wtedy, gdy miejsce zerowe jest w x = 0. W innym przypadku wynik programu nie pokrywa się z rzeczywistością.

#include <stdio.h>
#include <math.h>	// wymagane dla funkcji sin() i M_PI
#include <stdlib.h>	// wymagane dla funckji atof()

float a, b, c, p, k;

double f(double z)
{
	return (a*sin(b*z)+c); // funkcja obliczajaca wartosci rownania
}

double x(double q, double w)
{
	return (w-f(w)*(w-q)/(f(w)-f(q))); // funkcja obliczajaca xi+1
}

int isNumber( char *t ) 
{ 
    int tac = 0; 
    while( t[ tac ] != 0 ) 
    { 
        if ( !(t[ tac ] == 45 || !t[ tac ] == 46 || ( t[ tac ] >= 48 && t[ tac ] <= 57 ) ) ) 
        { 
            return 0; 
        } 
        tac++; 
    } 
    return 1; 
}

int main()
{
	int n = 1;	// numer pierwszej iteracji
	double xmin, xakt, xplus;	// xmin=x0 (xi-1); xakt=x1 (xi); xplus=xi+1;
	printf ("\nRozwiazywanie rownania a*sin(bx)+c=0. \nPodaj wspolczynniki a, b, c (oddzielone spacjami): ");
	
	char ta[128], tb[128], tc[128]; 
    scanf("%s %s %s", ta, tb, tc); 

    if (isNumber(ta) != 1 || isNumber(tb) != 1 || isNumber(tc) != 1) 
    { 
        printf("\nBlad! Parametr nie jest liczba!\n"); 
        return -1; 
    } 
	
    a = atof(ta); 
    b = atof(tb); 
    c = atof(tc);
	
	printf ("Podaj poczatek i koniec przedzialu (w stopniach, oddzielone spacjami): ");
	
	char tp[128], tk[128]; 
    scanf("%s %s", tp, tk); 

    if (isNumber(tp) != 1 || isNumber(tk) != 1) 
    { 
        printf("\nBlad! Przedzial nie jest liczba!\n"); 
        return -1; 
    } 
	
    p = atof(tp); 
    k = atof(tk); 
	
	if (p == k)	//wtedy w trakcie funkcji x nastepuje dzielenie przez 0
	{
		printf ("\nBlad! p nie moze byc rowne k!\n");
		return 1;
	} 

	if ((a==0 || b==0) && c==0)	// wtedy wykres to prosta y = 0
	{
		printf ("\nRownanie jest spelnione dla kazdego rzeczywistego x.\n");
		return 2;
	}
	
	if ((a==0 || b==0) && c!=0)	// wtedy wykres to prosta y != 0
	{
		printf ("\nRownanie nie jest spelnione dla zadnego x.\n");
		return 3;
	}

	if ((a>0 & c>0 && c>a) || (a<0 & c>0 && c>-a) || (a>0 & c<0 && -c>a) || (a<0 & c<0 && -c>-a))	// wtedy wykres nie ma przeciec z osia x
	{
		printf ("\nRownanie nie jest spelnione dla zadnego x.\n");
		return 4;
	}
	
	xmin = p*M_PI/180;
	xakt = k*M_PI/180;
	xplus = x(xmin, xakt);	// przypisanie x-om odpowiednich wartosci
	
	if (xplus > xakt)
	{
		while ((xplus-xakt)/xplus > 0.001)
		{
			n++;	// zwiekszenie numeru iteracji
			xmin = xakt;
			xakt = xplus;
			xplus = x(xmin, xakt);	// przejscie do kolejnej iteracji - zmiana wartosci x-ow
		}
	}
	else
	{
		while ((xakt-xplus)/xplus > 0.001)
		{
			n++;
			xmin = xakt;
			xakt = xplus;
			xplus = x(xmin, xakt);
		}
	}
	
	printf ("\nRownanie jest spelnione dla x = %lf stopni. Liczba iteracji: %d.\n", xakt*180/M_PI, n);
	return 0;
} 

Można liczyć na pomoc?

1

kto powiedział, że przy każdej iteracji relacja (większe/mniejsze) pomiędzy xplus i xakt jest taka sama?

Co do tego jak pisać kod, proponuje wyraźnie rozdzielać cześć odpowiedzialną za wczytywanie danych i za obliczania.
Osobne funkcje były by wskazane.

0

Faktycznie, przeoczyłem to że nie zawsze może być ten sam znak. Dzięki za zwrócenie uwagi. Co do rozdzielenia kodu to w tym projekcie chyba nie będę z tym już mieszać, ale na pewno skorzystam z tego w następnym projekcie.
Dodatkowo zmieniłem nieco warunek pętli - zamiast fabs(xplus-xakt)/xplus jest teraz fabs(xplus-xakt). Nową wersję mi podpowiedziała Wikipedia i wydaje się, że teraz program działa prawidłowo. No, prawie... bo wyniki, choć w przybliżeniu dobre, są kompletnie nie z przedziału.

Kod wygląda tak:

 #include <stdio.h>
#include <math.h>	//wymagane dla funkcji sin() i M_PI
#include <stdlib.h>	//wymagane dla funckji atof()

float a, b, c, p, k;

double f(double z)
{
	return (a*sin(b*z)+c); //funkcja obliczajaca wartosci rownania
}

double x(double q, double w)
{
	return (w-(f(w)*(w-q))/(f(w)-f(q))); //funkcja obliczajaca xi+1
}

int isNumber(char *t) //funkcja sprawdzajaca poprawnosc danych
{ 
    int tac = 0; 
    while(t[tac] != 0) 
    { 
        if (!(t[tac] == 45 || t[tac] == 46 || ( t[tac] >= 48 && t[tac] <= 57 ))) 
        { 
            return 0; 
        } 
        tac++; 
    } 
    return 1; 
}

int main()
{
	int n = 1;	//numer pierwszej iteracji
	double xmin, xakt, xplus;	//xmin=x0 (xi-1); xakt=x1 (xi); xplus=xi+1;
	printf ("\nRozwiazywanie rownania a*sin(bx)+c=0. \nPodaj wspolczynniki a, b, c (oddzielone spacjami): ");
	
	char ta[128], tb[128], tc[128]; 
    scanf("%s %s %s", ta, tb, tc); //wczytywanie danych do tablic w celu sprawdzenia poprawnosci

    if (isNumber(ta) != 1 || isNumber(tb) != 1 || isNumber(tc) != 1) //spr. czy dane sa poprawne
    { 
        printf("\nBlad! Parametr nie jest liczba!\n"); 
        return -1; 
    } 
	
    a = atof(ta); 
    b = atof(tb); 
    c = atof(tc);	//konwersja stringow na floaty
	
	printf ("Podaj poczatek i koniec przedzialu (w stopniach, oddzielone spacjami): ");
	
	char tp[128], tk[128]; 
    scanf("%s %s", tp, tk); 

    if (isNumber(tp) != 1 || isNumber(tk) != 1) 
    { 
        printf("\nBlad! Przedzial nie jest liczba!\n"); 
        return -1; 
    } 
	
    p = atof(tp); 
    k = atof(tk);	//konwersja stringow na floaty
	
	if (p == k)	//wtedy w trakcie funkcji x nastepuje dzielenie przez 0
	{
		printf ("\nBlad! p nie moze byc rowne k!\n");
		return 1;
	} 

	if ((a==0 || b==0) && c==0)	//wtedy wykres to prosta y = 0
	{
		printf ("\nRownanie jest spelnione dla kazdego rzeczywistego x.\n");
		return 2;
	}
	
	if ((a==0 || b==0) && c!=0)	//wtedy wykres to prosta y != 0
	{
		printf ("\nRownanie nie jest spelnione dla zadnego x.\n");
		return 3;
	}

	if ((a>0 & c>0 && c>a) || (a<0 & c>0 && c>-a) || (a>0 & c<0 && -c>a) || (a<0 & c<0 && -c>-a))	// wtedy wykres nie ma przeciec z osia x
	{
		printf ("\nRownanie nie jest spelnione dla zadnego x.\n");
		return 4;
	}
	
	xmin = p*M_PI/180;
	xakt = k*M_PI/180;
	xplus = x(xmin, xakt);	//przypisanie x-om odpowiednich wartosci

	while (fabs(xplus-xakt) > 0.001)
	{
		n++;	//zwiekszenie numeru iteracji
		xmin = xakt;
		xakt = xplus;
		xplus = x(xmin, xakt);	//przejscie do kolejnej iteracji - zmiana wartosci x-ow
	}

	printf ("\nRownanie jest spelnione dla x = %lf stopni. Liczba iteracji: %d.\n", xakt*180/M_PI, n);
	printf ("%lf", f(xakt));	//debug
	return 0;
}

Przykładowo dla a=1, b=2, c=1 w przedziale <0, 90> równanie ma rozwiązane dla x=45 stopni. Natomiast program jako wynik wypluwa 2048434441386581500000000000 stopni. Wydaje się być dobry (po podstawieniu do równania wychodzi f(xakt)=0.000004, więc dokładność całkiem przyzwoita), no ale nijak się to ma do danego przedziału. Jakieś pomysły co z tym zrobić? Bo rozumiem że to miejsce zerowe ma okres równy 2*M_PI/b, no ale odejmować okres od tak kosmicznego wyniku to mogę do końca świata.

1 użytkowników online, w tym zalogowanych: 0, gości: 1