Wartości niektórych funkcji można przybliżać wykorzystując np. ich rozwinięcie w szereg
Taylora. Napisz program, w którym zostaną początkowo zdefiniowane dwie zmienne oznaczające argument x
oraz liczbę pierwszych wyrazów z rozwinięcia, które mają zostać uwzględnione przy obliczaniu
wartości cos (x) - pierwsze wyrazy mają największy wkład w dokładność obliczeń - i na ich podstawie
obliczana będzie wartość cos (x) dla podanego argumentu i liczby wyrazów. Przetestuj działanie
programu, unikaj prób wykorzystywania zbyt dużej liczby wyrazów (wartości potęg i silni szybko
stają się bardzo wysokie). Porównaj obliczone przez siebie wartości z wartościami otrzymanymi przy
użyciu wbudowanej funkcji Math.cos(). Pamiętaj, że argumentem zarówno tej wbudowanej metody,
jak i tej wykorzystywanej przez Ciebie metody jest kąt wyrażony w radianach! Niech wartość kąta
przechowywanego w zmiennej w stopniach, Twój program musi je najpierw przeliczyć na radiany
(pamiętaj o możliwości użycia stałej Math.PI).
int wyrazy = 4;
double x = 30;
System.out.println("Wybrany argument funkcji cos (w stopniach): " + x);
System.out.println("Liczba wyrazów w rozwinięciu Taylora: " + wyrazy);
// Przeliczam stopnie na radiany
x = 2*Math.PI*x/360;
int mnoznik = 1; // zmienna ta będzie wykorzystana do liczenia silni
double wynik = 0;
double potega = 1, silnia = 1; // dla pierwszego wyrazu mamy: x^0 = 1 oraz (2*0)! = 1
for (int n = 0; n <= wyrazy-1; n++) {
// Sprawdzam czy mam wykonać dodawanie czy odejmowanie (kolejne wyrazy mają
naprzemienne znaki)
if (n%2 == 0) {
wynik += potega/silnia;
}
else {
wynik -= potega/silnia;
}
// Ustawiam wartość potęgi x oraz silni do użycia w kolejnej iteracji pętli (czyli dla kolejnego
wyrazu rozwinięcia)
potega = potega*x*x;
silnia = silnia*(mnoznik++)*(mnoznik++);
}
System.out.println("Wartość funkcji cos(x) obliczona z wykorzystaniem rozwinięcia Taylora: " +
wynik);
System.out.println("Wartość funkcji cos(x) obliczona przy użyciu metody z klasy Math: " +
Math.cos(x));
1 - Nie rozumiem dlaczego w pętli for jest "n <= wyrazy-1"
2 - "potega = potega*x*x;
silnia = silnia*(mnoznik++)*(mnoznik++);"
Nie rozumiem także powyższych dwóch linijek, nie wiem jak ta potęga ma działać; np. dla trzeciej potęgi będzie 3*x*x zamiast x^3 (jeśli to dobrze rozumiem)
Czemu przy silni nagle mnożąc zwiększamy mnożniki?
Ja próbowałem zrobić to łatwiejszym sposobem, ale coś mi nie wyszło, co mógłbym tutaj zmienić?
// double x=30; // x - kąt w stopniach
// double rad=x*Math.PI/180; // stopnie - kąt w radianach
// int n = 4; // n - liczba pierwszych wyrazów z rozwinięcia
// double cos_licznik = 0;
// int cos_mianownik=1;
// System.out.println("Wybrany argument funkcji cos (w stopniach): "+x);
// System.out.println("Liczba wyrazow w rozwinieciu Taylora: "+n);
// for(int i=0; i <= n+2; i+=2)
// {
// cos_licznik=Math.pow(-1 , n) * Math.pow(x , 2*n);
// for(int j=2*n; j >= 0; j--)
// {
// cos_mianownik *= j;
// }
// }
// System.out.println("Wartosc funkcji cos_licznik: "+cos_licznik);
// System.out.println("Wartosc funkcji cos_mianownik: "+cos_mianownik);
// System.out.println("Wartosc funkcji cos(x) obliczona z wykorzystaniem rozwiniecia Taylora: "+(double)cos_licznik/cos_mianownik);
// System.out.println("Wartosc funkcji cos(x) obliczona przy uzyciu metody z klasy Math: "+(double) Math.cos(rad));
1 - "Można z pewnością ugryźć to inaczej i np. wystartować od n = 1 i skończyć na n <= wyrazy." - w ten sposób wydaje mi się bardziej intuicyjnie, a jednak taki program daje dobrą wartość, ale ze złym znakiem.
2 - Dalej nie rozumiem za bardzo jaki mają związek te dwie linijki z tym co Pan napisał. Próbowałem zrobić wersję łatwiejszą (powyżej), ale totalnie mi nie wyszło.
3 - "Po drugie w pętli z i w każdym obiegu używasz tej samej wartości n (końcowej), a to właśnie obliczenia ze zmienną i powinny się tam pojawiać." - nie rozumiem tego zdania. Program skompilowałem i dla kąta 60 stopni cosinus wychodzi nieskończony
1 - Hmm, być może coś jeszcze trzeba by wtedy zmodyfikować, bo zmiana sposobu iteracji po pętli może sprawić, że inne miejsce też trzeba będzie odpowiednio zmienić.
2 - no np. na początku wartosc potega jest rowna 1. Po pierwszym obiegu mamy potega = potega*x*x, czyli potega = x^2. Po kolejnym obiegu znowu przemnazamy to przez x^2, wiec teraz już potega = x^4. No i z każdą kolejną iteracją pętli przemnażamy to przez kolejne x^2, więc mamy to czego chcemy ;)
3 - Hmm, szczerze mówiąc nie wiem jak inaczej zapisać to zdanie. Zauważ, że to wyrażenie: cos_licznik=Math.pow(-1 , n) * Math.pow(x , 2*n); zapisałeś w pętli, gdzie zmienną iteracyjną jest i. Więc chodzi o to, aby w zależności od tego ile akurat wynosi i, to obliczane przez Ciebie wyrażenie było coraz to inne. A Ty wykorzystałeś w nim n, które przecież się nie zmienia z każdym obiegiem pętli. Więc za każdym razem liczysz taką samą wartosc cos_licznik.
1 - Nie rozumiem tego dalej, ale pokombinowałem i wychodzi na to, że aby uzyskać wynik z poprawnym znakiem kod musi wyglądać tak:
for (int n = 1; n <= wyrazy; n++)
{
// Sprawdzam czy mam wykonać dodawanie czy odejmowanie (kolejne wyrazy mają naprzemienne znaki)
if (n%2 == 0)
{
wynik -= potega/silnia;
}
else
{
wynik += potega/silnia;
}
Czyli w ifie i w elsie muszą być zamienione znaki (plus z minusem), ale nie wiem dlaczego.
2 - No ale przecież potega = potega*x*x=potega*x^2 (a nie potega=x^2)
3 - "A Ty wykorzystałeś w nim n, które przecież się nie zmienia z każdym obiegiem pętli. Więc za każdym razem liczysz taką samą wartosc cos_licznik." - a nie powinno być tak, że skoro n się zmienia to cos_licznik także?
1 - skoro zmieniasz startową wartość n, to zmieniasz też "numerację" kolejnych wyrazów w rozwinięciu (zrobiłeś takie przesunięcie o jeden), więc w sumie to logiczne, że trzeba było to zamienić miejscami.
2 - No i dokładnie tak ma być. Czyli (napisałem to już w sumie wcześniej) jeśli na począku potega = 1, to po kolejnym obiegu potega = 1*x^2. Po kolejnym potega = x^2*x^2 = x^4 itd.
3 - No ale właśnie n się u Ciebie nie zmienia, zmienną iteracyjną tam jest i.
2 - rozumiem już potęgę a teraz jeszcze próbuję zrozumieć silnię. Dlaczego 2 razy jest napisane mnożnik++? Wtedy przecież dla n=0 jest silnia = 1 (silnia początkowa) * 2 ((mnożnik=1)+1) * 3 ((mnożnik=2)+1), czyli już na początku wychodzi 3!, chyba że tak miało być. A jeśli tak to dlaczego?
3 - Czyli do cos_licznik zamiast n powinienem dać i (takie coś też nie daje lepszego efektu)?
2 - zauważ, że wyrazenie z silnią jest obliczane przed tą linijkę, więc dla n = 0 silnia jest jeszcze równa 1. Zresztą jest tam komentarz, że ta linjka silnia = silnia itd. ustawia nam silnię na kolejną iterację pętli.
3 - tak, tej zmiany na pewno trzeba dokonać. Natomiast możliwe, że jeszcze coś tam jest nie tak, nie przyglądałem się całemu kodowi, gdy zauważyłem już te 2 błędy, które Ci wskazałem.
2 - Czyli mam to rozumieć tak, że dla n = 0 silnia = 1 ; dla n = 1 silnia = 6 = 3! ; dla n = 2 silnia = 120 =5! ?
3 - A sprawdziłby Pan co tutaj można jeszcze zmienić, aby program działał poprawnie? Czy ma on już za dużo błędów?
double x=30; // x - kąt w stopniach
double rad=x*Math.PI/180; // stopnie - kąt w radianach
int n = 4; // n - liczba pierwszych wyrazów z rozwinięcia
double cos_licznik = 0;
int cos_mianownik=1;
System.out.println("Wybrany argument funkcji cos (w stopniach): "+x);
System.out.println("Liczba wyrazow w rozwinieciu Taylora: "+n);
for(int i=0; i <= n+2; i+=2)
{
cos_licznik=Math.pow(-1 , i) * Math.pow(x , 2*i);
for(int j=2*n; j >= 0; j--)
{
cos_mianownik *= j;
}
}
System.out.println("Wartosc funkcji cos_licznik: "+cos_licznik);
System.out.println("Wartosc funkcji cos_mianownik: "+cos_mianownik);
System.out.println("Wartosc funkcji cos(x) obliczona z wykorzystaniem rozwiniecia Taylora: "+(double)cos_licznik/cos_mianownik);
System.out.println("Wartosc funkcji cos(x) obliczona przy uzyciu metody z klasy Math: "+(double) Math.cos(rad));
1 - tak.
2 - wygląda na to, że Ty z jakiegoś powodu osobno sumujesz liczniki każdego wyrazu, a osobno sumujesz mianowniki i potem dzielisz jedno przez drugie. Ale tak przecież nie można robić z sumą ułamków ;) trzeba każdy licznik/mianownik policzyć osobno i potem je dodać do siebie (zgodnie ze wzorem Taylora).
1 - Ale dlaczego liczymy silnie tylko z liczb nieparzystych?
2 - Tak, nie miałem innego pomysłu jak to zrobić, chciałem zsumować każde (cos_licznik/cos_mianownik), ale coś mi nie wychodzi :/
1 - bo tak jest we wzorze.
2 - no ale musisz takie zsumowanie zrobić, bo to co było do tej pory to w ogóle co innego liczy ;)
1 - Bo w tym przypadku startujemy od n = 0 i chcemy mieć tyle wartości n co wartość zmiennej wyrazy. Można z pewnością ugryźć to inaczej i np. wystartować od n = 1 i skończyć na n <= wyrazy. Warto zdawać sobie sprawę z tego, że w ogólności sposobów rozwiązania danego problemu jest bardzo wiele :)
2 - Tak to nie działa - spójrz jakie potegi x pojawiają się w treści zadania, są to tylko parzyste potegi. Więc w kolejnych obiegach pętli zmienna potega to 1, x^2, x^4 itd. Co do silni, to znów zauważmy, że we wzorze pojawiają się tylko silnie parzystych liczb. Więc startując od mnoznik = 1, pierwsza z nich to bedzie 1*1*2 = 2!, potem 2!*3*4 = 4! itd. Można to oczywiście zrobić inaczej, np. wykorzystując pomocnicze zmienne. Warto kombinować po swojemu, nie trzeba (a wręcz nie warto) robić wszystkiego tak jak w odpowiedziach ;)
3 - tu jest kilka rzeczy źle. Dwie zauważyłam od razu, tzn. np. x ma być w radianach, więc w obliczeniach powinieneś używać zmiennej rad, a nie x. Po drugie w pętli z i w każdym obiegu używasz tej samej wartości n (końcowej), a to właśnie obliczenia ze zmienną i powinny się tam pojawiać. Możliwe, że coś jeszcze jest nie tak - warto w takiej sytuacji robić to o czym wspominałem na zajęciach - pisać kawałek kodu, kompilować go i sprawdzać na bieżąco, czy wszystko jest ok. Wtedy łatwiej naprawie się ewentualne pomyłki niż szukanie potem błędu w całości, która jest bardziej rozbudowana. No i nie oszukujmy się, szukanie błędu w przypadku programowania to bardzo częsta rzecz i zdecydowanie warto się z tym oswoić :D