Модераторы: bsa
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Программа неточно вычисляет произведение 
V
    Опции темы
konstantusha
Дата 10.10.2012, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 10.10.2012

Репутация: нет
Всего: нет



Здравствуйте! Возникла проблема с такой задачей: нужно подсчитывать произведение а[k]-тых пока а[k]-а[k-1]>eps. а[k]=а[k-1]+а[k-1]/2^(k-1), k=2,3.... a[0]=a[1]=1. Написанная мною программа работает, но начиная с произведения a[1]*..*a[4] подсчитывает с неточностью, которая в последующем увеличивается. Подскажите пожалуйста, в чем причина и как это исправить?

Код

# include <math.h>
# include <iostream>
using namespace std;
float a,a1,b,eps,p;
int k;
void main()
{
    setlocale(LC_ALL,"Russian");
    cout<<"Введите eps ";
    cin>>eps;
    p=1;
    a=1;
    k=2;
    do
    {
        a1=a+a/pow((float)2,k-1);
        b=a;
        a=a1;
        if(fabs(a1-b)>eps)
        {
            p=p*a1;
        }
        k=k+1;
    }
    while(fabs(a1-b)>eps);
   
    cout<<p<<endl;
    system("pause");
}



PM MAIL   Вверх
kosmonaFFFt
Дата 10.10.2012, 08:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 538
Регистрация: 14.4.2008
Где: Иннополис

Репутация: нет
Всего: 5



Самый простой способ увеличить точность - использовать double вместо float, но все равно точных вычислений не получится - будет погрешность, которая будет накапливаться с количеством вычислений...


--------------------
user posted image
PM MAIL ICQ   Вверх
konstantusha
Дата 10.10.2012, 09:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 10.10.2012

Репутация: нет
Всего: нет



Я пробовала это делать, особо не помогло.
PM MAIL   Вверх
NoviceF
Дата 10.10.2012, 09:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 313
Регистрация: 13.3.2012
Где: Ростов-на-Дону

Репутация: 2
Всего: 2



Вот здесь эпичный топик на тему http://forum.vingrad.ru/forum/topic-355758.html
PM MAIL   Вверх
math64
Дата 10.10.2012, 09:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 12
Всего: 72



По возможности, не пользуйся pow. Он вычисляется через логарифмы.
Замени его на умножение или сдвиг целого (если k <= 31)
PM   Вверх
konstantusha
Дата 10.10.2012, 11:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 10.10.2012

Репутация: нет
Всего: нет



math64, вы имеете ввиду вычисление степени с помощью цикла?
PM MAIL   Вверх
bsa
Дата 10.10.2012, 11:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



konstantusha, он имеет в виду, использование операции сдвига вместо вызова функции pow(). Сдвиг единицы на n бит влево (1 << n) эквивалентен возведению 2 в степень n.
PM   Вверх
konstantusha
Дата 10.10.2012, 12:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 10.10.2012

Репутация: нет
Всего: нет



Попробовала заменить pow сдвигом влево - ничего не изменилось..
PM MAIL   Вверх
math64
Дата 10.10.2012, 12:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 12
Всего: 72



Сдвигать влево можно только до 31 степени. А можно считать в цикле.
a[k]=а[k-1]+а[k-1]/2^(k-1) можно заменить на a[k]=а[k-1]*(1+1/2^(k-1))
b[2] = (2+1)/2 = 1.5;
a[2] = a[1]*b[2];
b[3] = (b[2] + 1)/2 = 1.25;
a[3] = a[2]*b[3];
Но так b[k] очень быстро станет 1. или 1+EPS (в зависимости от правил округления)
Можно прологарифмировать:
lnA[2] = lnA[1]+ln(1+0.5);
lnA[3] = lnA[2]+ln(1+0.25);
lnA[4] = lnA[3]+ln(1+0.125);
...
lnP = lnA[1] + lnA[2] + lnA[3] + ...;
lnP = ln(1+0.5)*N + ln(1+0.25)*(N-1) + ln(1+0.125)*(N-2) + ...; 
P = exp(lnP);
Для вычисления логарифма нужно вычислять ln(1+x) для малых x без потери точности.

Это сообщение отредактировал(а) math64 - 10.10.2012, 12:57
PM   Вверх
Silent
Дата 10.10.2012, 12:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 252
Регистрация: 3.10.2006

Репутация: 1
Всего: 9



konstantusha, попробую вас удивить, но применительно к float и double a+b не всегда равняется b+a  smile
Посмотрите результаты этой программы, и поймете:
Код

#include <stdio.h>
#include <math.h>

double A[30];
double eps = 0.0001;

void v0()
{
    double a,a1,b,p;
    int k;
    p=1;
    a=1;
    k=2;
    do
    {
        a1=a+a/pow((double)2,k-1);
        b=a;
        a=a1;
        if(fabs(a1-b)>eps)
        {
            p=p*a1;
        }
        k=k+1;
    }
    while(fabs(a1-b)>eps);
    printf("%0.20f\n",p);
}

int main()
{
    A[0] = 1; A[1] = 1;
    for (int i = 2; i < 30; i++)
    {
        A[i] = A[i-1] + A[i-1] / (1 << (i-1));
    }
    double p1 = 1.0;
    for (int i = 29; i > 0; i--)
        if (A[i]-A[i-1] > eps) p1 *= A[i];
    printf("%0.20f\n",p1);

    double p2 = 1.0;
    for (int i = 1; i < 30; i++)
        if (A[i]-A[i-1] > eps) p2 *= A[i];
    printf("%0.20f\n",p2);

    v0();
    return 0;
}


Результаты:
Код

74163.30098149599500000000
74163.30098149601000000000
74163.30098149601000000000

Первый результат получается, если я начинаю перемножать с малых чисел, второй результат - если делаю как автор, ну а третий результат - авторский (почти - поставлен тип double).
На float'ах же все везде одинаково - 74163.30468750000000000000
PM MAIL   Вверх
Silent
Дата 10.10.2012, 13:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 252
Регистрация: 3.10.2006

Репутация: 1
Всего: 9



math64, а нельзя ли предлагаемую формулу представить уравнением производящей функции? (например, как здесь) И тогда проблемы точности вычисления произведения ряда станут проблемой вычисления формулы, что неизмеримо проще. не?
PM MAIL   Вверх
konstantusha
Дата 10.10.2012, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 10.10.2012

Репутация: нет
Всего: нет



Silent, math64 спасибо большое) Наконец-то разобралась, с вашей помощью)
PM MAIL   Вверх
math64
Дата 10.10.2012, 22:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 12
Всего: 72



Цитата(Silent @  10.10.2012,  12:57 Найти цитируемый пост)
попробую вас удивить, но применительно к float и double a+b не всегда равняется b+a

a+b всегда равно b+a. Так же как и a*b == b*a.
Но (a+b)+c не всегда равно a+(b+c), так же как (a*b)*c не всегда равно a*(b*c)
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0521 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.