Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
brute
0 / 0 / 0
Регистрация: 13.11.2009
Сообщений: 13
#1

Расходящиеся ряды или как правильно задать проверку - C++

13.11.2009, 15:31. Просмотров 645. Ответов 4
Метки нет (Все метки)

Здравствуйте,
наткнулся на проблему, которая не дает покоя мозгу.
Ситуация следующая:
Работаю в среде MS Visual Studio 2005(C++), написал программу вычисления функции методом рядов Тейлора. Функция разложения : f=ln(1-x), Раскладывается она следующим образом:
http://i36.tinypic.com/20fysfp.gif
При входе в цикл первое значение вычисляется после 10,000,000 операций (если E задать 1e-7),
если же задать E = 1e-8, то первое значение вычисляется уже после 100,000,000 операций (это видно в выводе)
Причем если задать дополнительную проверку на максимальное кол-во итераций (допустим 1000), то точность теряется где-то уже после 3-4 знака..
Вообще говоря, происходит какое-то непонятное переполнение, после которого точность сильно повышается, а кол-во итераций снижается на несколько порядков!
Как и с чем это может быть связано и возможен ли другой способ проверки значения во втором цикле for?(может быть, y2-y1 < E? Как это можно реализовать?)
По-идее, функция(ряд Тейлора) расходится при больших n... Вот здесь то и вопрос: Как быть в таком случае?


Код программы (C++):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "iostream"
#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <windows.h>
#include <math.h> 
 
//using CharToOemA func to convert ANSI to OEM
char bufRus[256];
char* Rus(const char* text)
    {
        CharToOemA(LPCSTR(text), bufRus);
        return bufRus;
    }
 
char* Rus(const char* text);
 
int _tmain(int argc, _TCHAR* argv[])
{
    double dXbegin, dXend, dXstep, N, E;//, N1=1, i=1.0;
    printf(Rus("Программа вычисления функции ln(1-x), заданной рядами Тейлора\n"));
    printf(Rus("и вывода в столбец значений ф-ии на интервале [x1;x2]\n"));
    printf(Rus("x лежит в диапазоне (-1 <= x < 1)\n"));
    printf(Rus("с шагом dx и заданной точностью E.\n"));
    printf(Rus("Введите начало интервала, конец интервала, число шагов(N), точность вычислений.\n"));
    printf("x1, x2, N, E = ?\n");
    scanf("%lf%lf%lf%lf", &dXbegin, &dXend, &N, &E);
    
    if (    dXbegin < -1e300    // Проверка на диапазон значений вводимых чисел
        ||  dXbegin > 1e300
        ||  dXend < -1e300
        ||  dXend > 1e300
        ||  N < 1
        ||  N > 1e300
        ||  E < 0
        ||  E > 1e300)
    {
        printf(Rus("Вне диапазона\n"));
        getch();
        return 0;
    }
    if ( dXbegin > dXend )
    {
        printf(Rus("Начало интервала больше чем его конец, корректирую...\n"));
        dXbegin = -dXbegin;
        dXend = -dXend;
    }
    dXstep=(dXend-dXbegin)/N;
//  N=1.0; // экономим место в памяти(вместо объявления новой переменной), используем ненужную далее переменную N
    
    for (double x=dXbegin, i, y; x <= dXend; x+=dXstep)
    {
        if ( x < -1 || x >= 1)
        {
            printf(Rus("Значение функции в точке x=(%lf) не существует\n"), x);
            continue;
        }
    
    for (i=1, y=0, N=1; E < fabs(N/i); i++)
        {
            N*=x;   //следующий член ряда
            y-=(N/i);   //сумма
        }
    printf("i=%lf\n", i);
    printf(Rus("В точке x=[%.10lf]\ty=[%.10lf]\n"), x, y);
    }
    printf("E=[%lf]\nN=[%lf]\ndXbegin=[%lf]\ndXend=[%lf]\n", E, N, dXbegin, dXend);
    getch();
    return 0;
}
Вывод:
Программа вычисления функции ln(1-x), заданной рядами Тейлора
и вывода в столбец значений ф-ии на интервале [x1;x2]
x лежит в диапазоне (-1 <= x < 1)
с шагом dx и заданной точностью E.
Введите начало интервала, конец интервала, число шагов(N), точность вычислений.
x1, x2, N, E = ?
-1 -0.9 2 1e-8
i=100000000.000000
В точке x=[-1.0000000000] y=[0.6931471856]
i=253.000000
В точке x=[-0.9500000000] y=[0.6678293679]
E=[0.000000]
N=[0.000002]
dXbegin=[-1.000000]
dXend=[-0.900000]
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
13.11.2009, 15:31     Расходящиеся ряды или как правильно задать проверку
Посмотрите здесь:

Как задать правильно условие??? C++
есть график, проверьте правильно ли программа? и подскажите как задать в программе интервал? C++
C++ Как задать кодировку при записи в файл или чтения из него
C++ Как в условии поставить проверку принадлежности к тому или иному типу?
Как правильно записывать условные операторы и как прервать проверку при достижении результата? C++
Как правильно задать условие? C++
Как правильно задать параметр "время выполнения алгоритма"? C++
Как задать проверку на дробную часть? C++
C++ Как правильно задать цикл при заполнении массива
Как задать условие выхода из цикла, проверку на использование каждого элемента в массиве? C++
C++ Как правильно задать структуру
C++ Как правильно реализовать проверку типа передаваемого аргумента в шаблонной функции?

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
outoftime
║XLR8║
506 / 428 / 33
Регистрация: 25.07.2009
Сообщений: 2,297
13.11.2009, 18:45     Расходящиеся ряды или как правильно задать проверку #2
здесь
C++
1
for (i=1, y=0, N=1; E < fabs(N/i); i++)
при отрицательных х вы производите на 1 итерацию больше, надо брать по модулю.
здесь
C++
1
2
3
4
dXstep=(dXend-dXbegin)/N;
//      N=1.0; // экономим место в памяти(вместо объявления новой переменной), используем ненужную далее переменную N
        
        for (double x=dXbegin, i, y; x <= dXend; x+=dXstep)
вы опять потеряли -, т.к. при начале -0,5 и конце -0,3 шаг -0,2, потом вы к началу -0,5 прибавляете -0,2 до тех пор пока.. короче он должен зациклиться..
Это касательно мелочи..

Если что конкретно не ясно, спрашивайте..
brute
0 / 0 / 0
Регистрация: 13.11.2009
Сообщений: 13
13.11.2009, 22:43  [ТС]     Расходящиеся ряды или как правильно задать проверку #3
Цитата Сообщение от outoftime Посмотреть сообщение
здесь
C++
1
for (i=1, y=0, N=1; E < fabs(N/i); i++)
при отрицательных х вы производите на 1 итерацию больше, надо брать по модулю.
Если что конкретно не ясно, спрашивайте..
Что конкретно по модулю брать? х? или N?
manfeese
129 / 128 / 16
Регистрация: 04.01.2009
Сообщений: 415
14.11.2009, 03:01     Расходящиеся ряды или как правильно задать проверку #4
В случае, когда x = -1, боюсь, что ничего нельзя сделать. Ряд является знакочередующимся, поэтому и накладываються ограничения на значения X.

Добавлено через 44 минуты
Хотя, можно использовать следующее условие:
Если значение x<0, то вместо него можно использовать обратное ему значение для данной функции, а именно x=x/(x-1);
Например ln(1-(-1))=-ln(1-(1/2)).
В таком случае ряд перестает быть знакочередующимся, и операция выполняется намного быстрее.
Например для значений X = -1 и eps = 1e-7 количество итераций составляет 21, а для eps = 1e-8, всего лишь 22.
На примере своей программы приведу дополнительные условия:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <math.h>
#include <stdio.h>
#include <conio.h>
 
int main()
{
float x, eps;               
float Sum=0;                 
float Prev,temp=1;          
int n=1;                    
 
Start:
        printf("x="); scanf("%f",&x);
        if ( x<1 || x>=1)
    {
       clrscr();
       printf("X must be in -1<=X<1\n");
       goto Start;
    }
        printf("eps="); scanf("%f",&eps);
 
if ( x<0 ) x=x/(x-1);        // Проверка значения x
 
do
{
  Prev = Sum;                           
  temp *= x;                            
  Sum -= temp/n++;
}while(fabs(Sum-Prev)>eps);
 
if ( (x/(x-1)) < 0 ) Sum=-Sum;  //Меняем значение на противоположное
 
printf("Sum=%f  iteracii: %i",Sum,n);
getch();
 
return 0;
}
Добавлено через 47 минут
Немного ошибся в условиях, вот правильный
код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <math.h>
#include <stdio.h>
#include <conio.h>
 
int main()
{
float x, eps;               
float Sum=0;                 
float Prev,temp=1;          
int n=1;
bool Inv = false;                    
 
Start:
        printf("x="); scanf("%f",&x);
        if ( x<1 || x>=1)
        {
           clrscr();
           printf("X must be in -1<=X<1\n");
           goto Start;
        }
        printf("eps="); scanf("%f",&eps);
 
if ( x<0 )  // Проверка значения x
{
   x=x/(x-1);         
   Inv = true;
}
 
do
{
  Prev = Sum;                           
  temp *= x;                            
  Sum -= temp/n++;
}while(fabs(Sum-Prev)>eps);
 
if (Inv) Sum=-Sum;  //Меняем значение на противоположное
 
printf("Sum=%f  iteracii: %i",Sum,n);
getch();
 
return 0;
}
brute
0 / 0 / 0
Регистрация: 13.11.2009
Сообщений: 13
30.11.2009, 10:56  [ТС]     Расходящиеся ряды или как правильно задать проверку #5
Всем спасибо за поддержку и советы!

В итоге отладив программу пошагово, я понял наконец, где я теряю минус и преобразовал программу к такому виду(работает вполне сносно!):

code
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include "iostream"
#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <windows.h>
#include <math.h> 
 
//using CharToOemA func to convert ANSI to OEM
char bufRus[256];
char* Rus(const char* text)
    {
        CharToOemA(LPCSTR(text), bufRus);
        return bufRus;
    }
 
char* Rus(const char* text);
 
int _tmain(int argc, _TCHAR* argv[])
{
    double dXbegin, dXend, dXstep, N, E;//, N1=1, i=1.0;
    printf(Rus("Программа вычисления функции ln(x+1), заданной рядами Тейлора\n"));
    printf(Rus("и вывода в столбец значений ф-ии на интервале [x1;x2]\n"));
    printf(Rus("с шагом dx и заданной точностью E.\n"));
    printf(Rus("x лежит в диапазоне (-1;1]\n"));
    printf(Rus("Введите начало интервала, конец интервала, число шагов(N), точность вычислений.\n"));
    printf("x1, x2, N, E = ?\n");
    scanf("%lf%lf%lf%lf", &dXbegin, &dXend, &N, &E);
    
    if (    dXbegin < -1e300    // Проверка на диапазон значений вводимых чисел
        ||  dXbegin > 1e300
        ||  dXend < -1e300
        ||  dXend > 1e300
        ||  N < 1
        ||  N > 1e300
        ||  E < 0
        ||  E > 1e300)
    {
        printf(Rus("Вне диапазона\n"));
        getch();
        return 0;
    }
    if ( dXbegin > dXend )
    {
        printf(Rus("Начало интервала больше чем его конец, корректирую...\n"));
        //обойдемся без дополнительного ввода новой переменной
        dXbegin = dXbegin + dXend;  // {a = a0 + b0, b = b0}
        dXend   = dXbegin - dXend;  // {a = a0 + b0, b = a0}
        dXbegin = dXbegin - dXend;  // {a = b0, b = a0}
 
    }
    dXstep=fabs(dXend-dXbegin)/N;
    for (double x=dXbegin, i, y; x <= dXend; x+=dXstep)
    {
        if ( x <= -1 || x > 1)
        {
            printf(Rus("Значение функции в точке x=(%lf) не существует(задано условием)\n"), x);
            continue;
        }
 
        for (double i=1, minus=-1, y=0, N=1; E < fabs(N/i); i++)
        {
                if (i == 1)         //т.к. знакочередующаяся последовательность, пришлось пропустить
                    {           //программу через последовательное выполнение
                        N*=x;       //чтобы понять причину некорректного результата ...
                        y+=(N/i);   //итог: проверка условия в случае i == 1
                        continue;   //далее знак меняется нормально
                    }
                else
                    {
                    //minus*=-1.0;      //после первой итерации становится +, потом -...
                    N*=minus*x;     //следующий член ряда
                    y+=(N/i);       //сумма
                    if (E > fabs(N/i))
                        {
                            printf("i=%lf\n", i);
                            printf(Rus("В точке x=[%.10lf]\ty=[%.10lf]\n"), x, y);
                        }
                    if (i > 10000000)
                        {
                            printf(Rus("Количество итераций превысило 10,000,000 - прерываем цикл\n"));
                            printf(Rus("В точке x=[%.10lf]\ty=[%.10lf]\n"), x, y);
                            break;
                        }
                    }
        }
    }
    printf("E=[%e]\nN=[%lf]\ndXbegin=[%lf]\ndXend=[%lf]\n", E, N, dXbegin, dXend);
    getch();
    return 0;
}

P.S. функция немного другая правда: ln(x+1)
Но сути это не меняет, она такая же знакопеременная(не нужен только минус за скобкой как в первом примере)
Yandex
Объявления
30.11.2009, 10:56     Расходящиеся ряды или как правильно задать проверку
Ответ Создать тему
Опции темы

Текущее время: 17:46. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru