Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[ANSI C] Funkcja zwraca wartości cosinus x wykraczające poza przedział (-1,1) przy rozwinięciu w szereg.

Ostatnio zmodyfikowano 2016-06-15 09:04
Autor Wiadomość
probs
Temat założony przez niniejszego użytkownika
[ANSI C] Funkcja zwraca wartości cosinus x wykraczające poza przedział (-1,1) przy rozwinięciu w szereg.
» 2016-06-07 23:24:17
Jestem w trakcie pisania programu, który rozwija funkcję y=cos(x+a), gdzie "a" i "x" są rzeczywiste, a pogram ma zliczać rzecz jasna sumę funkcji rozwiniętej w szereg. Zanim wkleję kod, podam tok mojego rozumowania przy podejściu do tematu:

1. Funkcja cos przy rozwinięciu w szereg przechodzi przy obliczeniu pochodnej kolejno w -sin, -cos i sin, po czym wraca do cos. Implikuje to cztery przypadki i zastosowanie do każdego z nich pętli "for", a inkrementacja dla każdego n będzie zwiększana o 4.

2. No dobra, zobaczymy czy pójdzie na elementarnych przypadkach. Najpierw w podanym przykładzie przetestowałem proste przykłady z silnią w fukncji szereg. Grało wszystko, na różne wariacje, więc do funkcji "silnia" nie ma się (chyba) czego przyczepić.

3. Kłopot zaczął się przy uzupełnianiu funkcji szereg. Po uzupełnieniu funkcji do postaci poniżej zwracała ona nawet kosmiczne wartości przy niskich parametrach. Zacząłem kombinować jak się da, nawet dla potęgi zapisałem osobną funkcję.

Pytanie więc do doświadczonych użytkowników: gdzie popełniłem błąd w pisaniu kodu? Czuję, że pofanzoliłem coś z zasięgiem zmiennej, ale przy double problemów i rzutowaniu nań z inta problemów chyba być nie powinno. Poniżej prezentuję moje zapisy. Funkcję main skróciłem, bo zawiera ona instrukcje ograniczające liczbę punktów, w których ma zliczać, epsilon (nie podany niżej w funkcji szereg, gdyż wiem jak to zrobić (aż się prosi o while lub do while), dlatego też nie chciałem rozdmuchiwać całego ekranu kodem. Bardzo bym prosił o ewentualne wskazówki. Sam sobie nie odpuszczę, jeżeli tego nie rozgryzę, w końcu to tylko maszyna, goddammit! ;]

C/C++
#include <math.h>
#include <stdio.h>
#include <stdlib.h>


int main()
{
    double //zmienne do inkrementacji
    double //zmienne do przedziału
    double //argumenty i wartości funkcji
    double silnia( int );
    double potega( double, int );
    double szereg( double, int, double );
    {
        //tu instrukcje podaj x przedział warunki by był naturalny itd itp
    }
    y = szereg( x, n, aa ); //skróciłem bo nawet dla jednego y=f(x) zwraca ponad 1 lub poniżej -1
    printf( "\n" );
    system( "pause" );
    return 0;
}
double silnia( int n )
{
    int sln = 1;
    int i;
    if( n == 0 || n == 1 )
         return 1;
    else
   
    for( i = 2; i <= n; i++ )
    {
        sln = sln * i;
    }
    return sln;
}
double potega( double x, int n )
{
    return pow( x, double( n ) );
}
double szereg( double x, int nliczba, double a )
{
    double sum1 = 0;
    int n = 0;
    for( n = 0; n <= nliczba; n = n + 4 )
    {
        sum1 = sum1 + cos( a ) *(( potega( x, n ) ) /( silnia( n ) ) );
    }
    n = 1;
    double sum2 = 0;
    for( n = 1; n <= nliczba; n += 4 )
    {
        sum2 = sum2 +( - 1 ) * sin( a ) *(( potega( x, n ) ) /( silnia( n ) ) );
    }
    double sum3 = 0;
    n = 3;
    for( n = 3; n <= nliczba; n += 4 )
    {
        sum3 = sum3 +( - 1 ) * cos( a ) *(( potega( x, n ) ) /( silnia( n ) ) );
    }
    double sum4 = 0;
    n = 4;
    for( n = 4; n <= nliczba; n += 4 )
    {
        sum4 = sum4 + sin( a )(( potega( x, n ) ) /( silnia( n ) ) );
    }
    return sum1 + sum2 + sum3 + sum4;
}
P-148959
michal11
» 2016-06-08 00:38:00
Podaj jeszcze dla jakich x, n i aa w main zwraca ci złe wyniki i jakich oczekujesz.

na razie zmieniłem twój kod tak:
C/C++
int silnia( int n )
{
    int val = 1;
    for( int i = 1; i <= n; val *= i++ );
   
    return val;
}

//....


for( n = 0; n <= nliczba; ++n )
{
    double val = 0;
   
    switch( n % 4 )
    {
    case 0:
        val = cos( a );
        break;
    case 1:
        val = - 1 * sin( a );
        break;
    case 2:
        val = - 1 * cos( a );
        break;
    case 3:
        val = sin( a );
        break;
    }
   
    sum += val *(( potega( x, n ) ) /( silnia( n ) ) );
}

u mnie dla wartości
int n=2, nliczba=4;
double a=3,x=2, sum=0;

zwraca sumę = 0.848872. Ale nie mam pojęcia czy to ma jakiś sens.
P-148961
darko202
» 2016-06-08 08:14:45
z funkcją silnia jest jeden problem :-)
np. 15! = 1 307 674 368 000
itp.
https://pl.wikisource.org/wiki​/Silnia

a zwracasz wynik funkcji na int
a te maja zakres
−2 147 483 648 — +2 147 483 647 (ze znakiem)
 0 — +4 294 967 295 (bez znaku)

czyli powyżej jakiegoś n (może 12) - zwracane wartości funkcji wykraczają poza zakres

15! = 1 307 674 368 000
int      +4 294 967 295 (bez znaku)

jak nad tym się zastanowisz to zobaczysz nieprawidłowość wykonywanych działań
wynikająca z ww. ograniczeń
P-148962
Gibas11
» 2016-06-08 10:49:06
Użyj jakiejś implementacji uint128_t, wystarczy do dużych liczb chyba że przekroczysz 2^128-1. No i dobrze napisana biblioteka do tego jest szybsza od gmp itp.
P-148964
darko202
» 2016-06-08 11:37:56
sprawdź dla jakiego N  mamy 2^127 < N ! 
myślę że na pewno dla n=40, ale może nawet dla n=30, czyli niewiele lepiej  

dlatego rozwiązanie inny typ danych (np. biblioteka dużych liczb) jest to tylko przesuwanie problemu, a nie rozwiązanie problemu. 
Oczywiście czasami może wystarczyć, ale raczej powinno się zmienić algorytm na lepszy.

nie jest on taki znowu bardzo trudny.
   
 
 
P-148965
pekfos
» 2016-06-12 15:12:20
Takich rzeczy nie liczy się prosto ze wzoru. Tu nie trzeba większych typów tylko poprawnych obliczeń.

C/C++
double silnia( int n )
{
    int sln = 1;
    int i;
    if( n == 0 || n == 1 )
         return 1;
    else
   
    for( i = 2; i <= n; i++ )
    {
        sln = sln * i;
    }
    return sln;
}
Jaki sens zwracać double, skoro liczysz na int? Zdecyduj się w którąś stronę.
P-149060
probs
Temat założony przez niniejszego użytkownika
» 2016-06-14 20:14:39
Bardzo dziękuję za wszystkie wskazówki.
michal111 Twoja funkcja sumująca zwraca bardzo dobre wartości i jest krótsza za co bardzo ci dziękuję, ale też uparłem się by sprawdzić czemu moja pierwotna nie chciała zwracać normalnych wartości.

Rację mieli darko202 i pekfos. Porobiłem liczne testy dla każdej funkcji w najprostszej formie - oprintefowałem (nowomowa:P) każdy krok, drukując wartości wszystkich liczb, dla każdego powtórzenia pętli. No i faktycznie nie przewidziałem dwóch rzeczy - że dla wyższych wartości 'n' i 'x'potęga w liczniku z początku jest dużo wyższa od silni w mianowniku, a ta 'dogania ją' dopiero przy większej ilości zliczeń. Problem rozwiązałem dając wszystkie zmienne na double (nawet wprowadzaną ilość zliczeń, tylko porobiłem warunki, by niemądry użytkownik w razie błędu nie podał liczby ujemnej lub z rozwinięciem dziesiętnym.

dlatego rozwiązanie inny typ danych (np. biblioteka dużych liczb) jest to tylko przesuwanie problemu, a nie rozwiązanie problemu. 
Oczywiście czasami może wystarczyć, ale raczej powinno się zmienić algorytm na lepszy.

nie jest on taki znowu bardzo trudny.
Drogi darku, porobiłem wspomniane testy i zauważyłem, że silnie dla zmiennej double ucina i zostawia same zera po przekroczeniu wartości, która zajmuje 15 cyfr. Jedyne co wykoncypowałem, to żeby zapisać warunek, że po przekroczeniu zasięgu zapisywał wartość w pamięci, potem liczył dalej i na koniec dodawał uciętą część? Czy to nie przejdzie ze względu na wspomniany zasięg. (Zrobiłem prosty test drukując zmienną double o dużej wartości po przekroczeniu, ale po przekroczeniu 15 cyfr zwyczajnie gubi precyzję). Czy mógłbym prosić o wskazówki z jakich narzędzi skorzystać? Nie jest mi to potrzebne, ale z czystej ciekawości.

Miałbym jeszcze pytanie odnośnie przypisania i modulo w if. Dajmy na to: if(a=2) i jeżeli a wcześniej równało się 2, warunek się nie wykona. Czy należy to czytać("Jeżeli można przypisać zmiennej a wartość 2?"). Podbnie jest z modulo: if (a%3). Czy to jest zapytanie typu "Jeśli reszta z dzielenia równa się 4"
P-149136
Gibas11
» 2016-06-14 21:53:42
Warunek if(a = 2) wykona się zawsze:
C/C++
int a;
std::cout <<( a = 2 ) << std::endl;
Wersja C:
C/C++
int a;
printf( "%i\n",( a = 2 ) );
Oba wyświetlą 2, a if(2) się wykona (każda liczba niebędąca zerem to prawda).
A if(a % 3) to to samo co if(a % 3 != 0), czyli a % 3 musi być równe 1 lub 2 by spełnić warunek.
P-149139
« 1 » 2
  Strona 1 z 2 Następna strona