Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/7: Рейтинг темы: голосов - 7, средняя оценка - 4.86
0 / 0 / 0
Регистрация: 13.11.2009
Сообщений: 13
1

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

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

Author24 — интернет-сервис помощи студентам
Здравствуйте,
наткнулся на проблему, которая не дает покоя мозгу.
Ситуация следующая:
Работаю в среде 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]
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
13.11.2009, 15:31
Ответы с готовыми решениями:

Как правильно организовать проверку на отсутствие или существование записи в mysql
Вопрос в следующем как организовать проверку на отсутствие или существование записи в mysql? ...

Как правильно задать http запрос, если нужно задать reqest header?
В программе нужно перезагрузить роутер, исспользуя httpanalazer получилось определить какой запрос...

Подскажите как задать проверку
Можете помочь, я ещё начинающий кодер, мне нужно задать проверку: если у объекта угол вращения по Z...

Как задать проверку на дробную часть?
Что исправить когда ввожу число без дроби например 49 пишет сорок девять сантиметра ноль милиметров...

4
║XLR8║
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,361
Записей в блоге: 5
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 до тех пор пока.. короче он должен зациклиться..
Это касательно мелочи..

Если что конкретно не ясно, спрашивайте..
0
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?
0
133 / 132 / 29
Регистрация: 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;
}
0
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)
Но сути это не меняет, она такая же знакопеременная(не нужен только минус за скобкой как в первом примере)
0
30.11.2009, 10:56
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.11.2009, 10:56
Помогаю со студенческими работами здесь

Ряды. Как правильно записать выражение?
Подскажите, как в маткаде правильно записать выражение? 3*5*.....*(2n+1).

Как задать условие на проверку наличия файла
При запуске приложения надо проверить имеется ли на компьютере требуемый адрес и файл, как можно...

Как правильно делать проверку
Сори за название, не смог его правильно сформулировать. Не пойму как правильно сделать такую...

Как задать условие выхода из цикла, проверку на использование каждого элемента в массиве?
Есть функция: void naznachenie (string FIO, string dol) { int a; while (FIO...


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

Или воспользуйтесь поиском по форуму:
5
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru