Skocz do zawartości

Zarchiwizowany

Ten temat jest archiwizowany i nie można dodawać nowych odpowiedzi.

yojc

[C] rozw. równania metodą siecznych

Polecane posty

Witam, w ramach projektu na studiach mam do napisania pewien względnie prosty program w C.

Napisac program rozwiązujący równanie nieliniowe a*sin(bx)+c=0 metodą siecznych:

x0 = a

x1 = b

xi+1 = xi-f(xi)*((xi-xi-1)/(f(xi)-f(xi-1))

?xi+1/xi+1?0,1%

Jako, że jestem BARDZO początkujący w sprawach C to mi idzie trochę opornie, ale co nieco już rozgryzłem.
#include <stdio.h>
#include <math.h> // wymagane dla funkcji sin() i M_PI

float a, b, c;

double f(double z)
{
return (a*sin(b*z*M_PI/180)+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 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): ");
scanf ("%f %f %f", &a, &b, &c);

if (a == b) //wtedy w trakcie funkcji x nastepuje dzielenie przez 0
{
printf ("\nBlad! Niepoprawne dane!\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 = a;
xakt = b;
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, n);
return 0;
}

Problem jest taki, że przy wartościach b od 23 wzwyż program jako wynik zaczyna podawać głupoty (tzn. że poszukiwany x = b ), przy czym przy odpowiednio wysokich a i c błąd znika. Przesiedziałem trochę w szukaniu błędu i nie znalazłem. Pewnie jakiś głupi błąd zrobiłem gdzieś po drodze (na dobrą sprawę to mój pierwszy program w C). Jak ktoś coś zauważy to prosiłbym o pomoc.

Ponadto program ma wyszukiwać miejsca zerowe w określonym przedziale (podanym przez użytkownika programu). Nie do końca mam pomysł jak do tego podejść. bo rozumiem że miejsce zerowe które oblicze tym programem się powtarza co b*180 stopni, ale jest jeszcze drugie miejsce zerowe które się też powtarza co b*180... nie mam zielonego pojęcia jak je obliczyć. Jakieś pomysły?

I jeszcze jedno ostatnie pytanie... chciałbym trochę uczynić ten program "idiotoodpornym", tzn. że jak ktoś wpisze litery zamiast liczb to żeby program nie liczył tylko zwrócił błąd. Jak się do tego zabrać?

Link do komentarza
Udostępnij na innych stronach

Idiotoodporność. Zrób pobieranie danych od użytkownika w pętli while.

bool done = false;
while(true != done)
{
  //Pobierz dane
  if (  //Test czy dane są poprawne) )
  {
     done = true;
  }
  else
  {
     //Poinstruuj użytkownika jak należy podać dane
  }
}

O ile dobrze pamiętam to metoda siecznych należy do metod zbieżnych lokalnie. To znaczy, że wynik w sporym stopniu zależy od danych wejściowych. Także nie oczekuj, że zawsze dostaniesz poprawny wynik. Przykładowo dla sinusoidy odpalona na przedziale <-2pi:10pi> znajdzie jedno z miejsc zerowych, prawdopodobnie gdzieś w okolicy 4 pi.

Niestety jestem zbyt zmęczony, żeby przeanalizować kod. Proponuje zapoznać się z debuggerem.

Znalezienie wszystkich miejsc zerowych. Ja zacząłbym od takiego algorytmu.

1) Dla danego zakresu <A:B> sprawdzamy, czy jego szerokość jest większa od jakieś ustalonej precyzji

a) jeżeli tak to wykonuje wyszukiwanie miejsca zerowego,

b) jeżeli nie to zwracam, że nie ma miejsca zerowego w tym przedziale.

2) Dzielę zakres na dwa podzakresy w miejscu zerowym <A:0> i <0:B>.

3) Zakres <A:0> dzielę na pół <A:x> i <x:0>, zakres <0:B> na <0:y> i <y:B>.

4) Dla przedziału <A:x> skaczemy do punktu pierwszego, zachowujemy zwrócone miejsca zerowe.

5) Dla przedziału <y:B> skaczemy do punktu pierwszego, zachowujemy zwrócone miejsca zerowe.

6) Dla przedziału <A:0 - precyzja> skaczemy do punktu pierwszego, zachowujemy zwrócone miejsca zerowe.

7) Dla przedziału <0 + precyzja:y> skaczemy do punktu pierwszego, zachowujemy zwrócone miejsca zerowe.

8) Wszystkie znalezione miejsca zerowe składamy w jedną kupę i zwracamy.

Przyda Ci się rekurencja.

Zwracanie wyników przez parametry.

void fun(float ** wynik, unsigned int & rozmiarWyniku)

.

Alokacja pamięci.

Proponuje taki nagłówek funkcji

void fun(std::vector<float> & result, float a, float b, float precision)

Właśnie doczytałem, że to ma być C. Jeżeli to faktycznie ma być czyste C to zamiast wektora musi być takie coś jak w tym przykładzie powyżej + memcpy. Nie zapomnij o zwalnianiu pamięci.

Jeszcze takie drobnostki:

pisz kod w języku angielskim, powodów jest kilka (wszyscy znają angielski, wszystkie komputery rozumieją angielski),

pisz kod "samo komentujący" (używaj nazw które jednoznacznie określają co się dzieje). Masz trzy zmienne i jedną funkcję o nazwie zaczynającej się od x. Ja się gubię ;) Łatwiej by mi było jakbyś dał left, x, right, albo A, x, B, albo low, x, up itp. Funkcja można by nazwać GetFunctionValue i FindNextPoint,

opis funkcji, dobrze jest dać przed nią, a nie w środku. Można nawet pokusić się, o opis parametrów, zwracanych wartość itp. Rzecz jasna ma to sens przy nieco bardziej ambitnych projektach

//Opis
void fun()

Link do komentarza
Udostępnij na innych stronach

Źle się wyraziłem co do tych miejsc zerowych... program nie ma znajdywać wszystkich miejsc zerowych w przedziale, a tylko jedno dowolne (jeśli istnieje).

Co do reszty, to postaram się wcielić w życie. Zobaczymy co z tego wyjdzie.

Link do komentarza
Udostępnij na innych stronach



  • Kto przegląda   0 użytkowników

    • Brak zalogowanych użytkowników przeglądających tę stronę.
×
×
  • Utwórz nowe...