Разминка для мозгов: вычисление экспоненты через ряд Маклорена
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Вычисление экспоненциальной функции с помощью разложения в ряд Маклорена
Для тех, кто не знает, что такое ряд Маклорена: понимания математического аппарата для решения задачи не требуется.
Значения некоторых функций можно вычислить с помощью рядов. В данном случае ряд — это бесконечная последовательность членов, в которой каждый следующий член по модулю меньше предыдущего. Члены ряда зависят от значения аргумента. Сумма всех членов ряда даёт искомое значение функции в точке, указанной аргументом.
Известно, что экспоненциальная функция раскладывается в следующий ряд (см. по ссылке первую формулу).
Необходимо написать функцию вычисления экспоненты с помощью ряда Маклорена с точностью, определяемой вторым параметром функции. На первый аргумент функции ограничений не накладывается (в разумных пределах, задаваемых точностью плавающей арифметики).
Шаблон программы прилагается. Изменять шаблон программы нежелательно: ваш код должен быть только там, где указано комментарием (в начало программы можно добавить заголовочные файлы, если это совершенно необходимо). Для удобства отладки под Visual Studio в шаблон программы вставлены операторы условной компиляции: добавляется pause перед закрытием окна программы. Постарайтесь написать наиболее эффективный код.
Публикуйте, пожалуйста, только свой вариант функции
ex
.Подсказка: достижением заданной точности считать момент, когда очередной член ряда по модулю меньше указанной точности вычислений (
epsilon
).Дополнительная информация (если кто ещё не знает или уже забыл):
Ну и чего все притихли? Сами же просили подкинуть какую-нибудь интересную задачку.
Если решать «в лоб», то задача совсем ПРОСТАЯ. Чуть сложнее, чем посчитать сумму чисел от 1 до 100 ))
А если немного подумать, то можно решить ещё проще )))
PS. Cranium — это по латыни череп.
Череп, Just to me I've guessed that cranium means Череп :) but now I haven't so much time to solve this problem, becouse protecting my diploma design. But I have one solution which I will try put at the blog tomorrow morning.
Sorry for the mistake. I was be hurry :)
I will try to put at the blog tomorrow morning. It will be right.
By the way it's MY new authorized nick :)
Череп,
Я бы сделал проверку через
#ifdef _WIN32
. В Dev C++ тожеsystem("pause")
может пригодиться.А
<cstdlib>
можно и глобально включить, т.к. там есть другие полезные функции, которые могут быть использованы в решении.Череп, с моим школьным уровнем знаний алгебры( только 8 класс )? я задание-то не совсем ещё понял :)
selevit, у меня при прогоне программы под Dev C++ (Windows) IDE сама тормозит закрытие окна. A MS VS окно программы закрывает. Поэтому поставил именно такое условие. Вообще-то это не принципиально. Тот, кто знает зачем нужно
system("pause")
, сам внесёт в код такое изменение, с которым ему будет комфортно отладить программу ))Кстати, а почему на сайте нельзя регистрироваться под русскими никами?
Юрий, точнее говоря, Alf :) а с какого бодуна ты вдруг на английский перешёл? Над дипломом перетрудился? ;-)
porshe, специально для школьного уровня знаний алгебры, я дал ссылки Дополнительная информация.
Попробую переформулировать задание:
Экспоненциальную функцию от x можно посчитать по формуле:
здесь
^
— операция возведения в степень,n!
— факториал от n (n! = 1 * 2 * 3 * ... * n
).Собственно говоря, нужно запрограммировать именно это выражение.
Поскольку ряд (см. выражение выше) бесконечный (n возрастает до бесконечности), вводится ограничение по точности вычисления: последнее вычисленное слагаемое (член ряда) должно быть по модулю меньше заданного предела точности (epsilon), который передаётся в подпрограмму вторым аргументом.
Так понятнее?
Отщеплен новый топик «Кириллические имена пользователей в Django».
Первое, перехожу на русский :)))
Второе, как и обещал — свой вариант кода.
А вот работа программы при разных значениях:
В общем диапазон вычислений, который по точности совпадает с библиотечной функцией, от -16 до 16. При дальнейшем увеличении или уменьшении значений появляется погрешность вычислений, при чем для положительных значений погрешность не велика (последний обрабатываемый аргумент 65), а при вычислении отрицательных — вычисление аргумента после -17 дает увеличение результата, а при значении -27 — результат со знаком минус.
Пока грешу на типы данных, которые я использовал в функции.
Я посмотрел на работу библиотечной функции и на данном этапе не могу даже предположить с каким типом данных она работает, так как она стабильно выдает результат, при заданной точности, в диапазоне аргумента от -22 до 709 (а это более 10^300, которым располагает long double).
Конечно может быть ошибка в коде? Но тогда как понимать точные вычисления от -16 до 16?
Череп, объясняй в чем соль... :)
Протестировал программу все работает. Погрешность дает факториал с типом данных long double, у которого сначала не хватает точности при вычислении в диапазонах от -20 до -65 и от 20 до 65, а потом и вовсе не хватает диапазона, так как значения выходят за 10^306.
С какими же типами данных работает функция exp()?
Это я и сразу понял) Я только сразу не понял что такое точность, хотя и щас толком не понял зачем она нужна) ( всё это время я пытался постичь суть всего этого, пока не понял, что это бессмысленное занятие) )
Вот мой код:
Не-е-ет, брат porshe, не пойдет. Если ты протестируешь свой код с отрицательными значениями, то увидишь, что код не рабочий:)
В проверочном условии проверку нужно делать по модулю.
Вот еще один вариант. Есть пару моментов — я думаю, что тип double здесь маловат, поэтому я заменил его на long double и добавил в функцию main() цикл для удобства сравнения значений.
Или так
Для начала мой вариант функции:
Весь цимес задачи был в том, что бы заметить, что следующий член ряда получается путём умножения предыдущего члена на
x/n
. Это даёт следующие преимущества:pow()
и, не дай Бог, от вычисления факториала на каждой итерации.f(n) =x^n
иf(n) = n!
очень быстрорастущие (здесьx
— константа!). При их вычислении возможности разрядной сетки исчерпываются очень быстро исчерпываются. Функцияf(n) = x/n
— убывающая, асимптотически приближающаяся к нулю. В данном случае это очень хорошо. Этот вариант функции считает до x = 709 включительно (так же, как и библиотечная функция).pow()
, теряются значащие цифры: в MS VC++ из 308 возможных десятичных цифр (диапазонdouble
1.7E +/- 308) работают только 15. Выражениеx/n
считается с точностью 13 знаков минимум. Отсюда абсолютное совпадение вычисленных значений с библиотечной функцией в диапазоне -5..709.Работал с Dev-C++ 5.5.3/TDM-GCC 4.7.1 64-bit. Это важно. Потому что с MS VC++ 2012 результаты другие.
В MS VC++ 2012 тип
long double
совпадает сdouble
: длина 8 байт, диапазон 1.7E +/- 308, точность 15 десятичных знаков.В TDM-GCC 4.7.1 64-bit тип
double
совпадает с майкрософтовским. Аlong double
имеет длину 16 байт, из которых используются 10 (80 бит, x86 Extended Precision Format), диапазон 1.18973E +/- 4932, точность 19 десятичных знаков.Понятно, что по точности результаты MS VC++ сильно уступают результатам TDM-GCC. (Вообще-то я был неприятно удивлён таким фокусом от MS.)
Когда я писал текст задания, у меня была мысль про аппаратную точность вычислений. Поэтому я выбрал
epsilon = 1e-10
и вывод на экран 9 знаков после запятой — достаточно грубо, что бы не влияла потеря точности на уровне железа. Кстати говоря, библиотечная функцияexp()
должна возвращать значение именноdouble
.Череп, так у тебя функция тоже не работает со значениями ниже -25 ???
А оно и не должно считать. Точность задана 1e-10, а значение функции
exp(-25) = 1.3887943864964E-11
, т.е. меньше заданной точности.Ради интереса я немножко поэкспериментировал: использовал библиотеку для вычислений с произвольной точностью. У меня получилось перекрыть весь диапазон библиотечной функции: от -708 до 709... при
epsilon = 1e-1200
и использовании 3000 значащих бит (против 64 дляdouble
и 80 дляlong double
). Это, так сказать, решение «в лоб»: путём тупого увеличения количества бит и уменьшения порога точности.Видимо библиотечная функция использует другой алгоритм ))
Но это всё выходит далеко за рамки изначальной задачи.
Череп, у меня есть два вопроса:
1. В консоли если ввести число типа double с запятой вместо точки, в качестве разделителя, то следующий ввод будет с ошибкой, так как запятая и цифры идущие за ней остаются в потоке — в чем проблема и как с ней «бороться»?
2. Можно ли на одну машину установить два компилятора? Не «подерутся» ли они?