Ряд Тейлора по методу Горнера с использованием рекурсии

Наш профессор научил нас находить ряд Тейлора с помощью рекурсии, используя статическую переменную суммирования, которая накапливает сумму каждого члена, а затем умножает сама себя на каждой итерации.

#include <stdio.h>

double t(int n,int m){
    static double a = 1.0;
    if (m==0)
        return a;
    a = 1+(double)n/m*a;
    return t(n,m-1);
}

int main(){
    printf("%lf\n",t(4,1500));
}

Это дает желаемую точность и результат.


Я попытался реализовать код, используя один оператор возврата:

#include <stdio.h>

double e(int x, int m){
    if (m==0)
        return 1.0;
    return 1+((double)x/m)*e(x,m-1);
}

int main(){
    printf("%lf\n",e(4,1500));
}

Это дает результат 1.002674.

Какая разница между двумя? Что заставляет этот код привести к такой неточности?

Исходный код обновляется a на каждом этапе рекурсии. Ваш новый код вычисляет выражение на выходе из рекурсии.

Eric Postpischil 21.05.2024 10:33

Не отмечайте это как dsa. Тег dsa предназначен для Digital Signature Algorithm.

PaulMcKenzie 21.05.2024 10:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
75
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  • Пункт списка

Дело не в неточности. Выражение, которое вычисляется, отличается.

Давайте заменим второй аргумент (1500) всего на 3. Итак, t(4, 3) и e(4, 3). Логика t(4, 3) вычисляет это выражение:

a = 1
a = 1 + (4 / 3) * a
a = 1 + (4 / 2) * a
a = 1 + (4 / 1) * a

Это распространяется на:

1 + (4 / 1) * (1 + (4 / 2) * (1 + (4 / 3) * 1))
= 23.66666...

а e(4, 3) рассчитывается следующим образом:

e(4, 0) = 1
e(4, 1) = 1 + (4 / 1) * e(4, 0)
e(4, 2) = 1 + (4 / 2) * e(4, 1)
e(4, 3) = 1 + (4 / 3) * e(4, 2)

который расширяется до:

1 + (4 / 3) * (1 + (4 / 2) * (1 + (4 / 1) * 1))
=15.66666...

На этом небольшом примере вы можете увидеть, как дроби вычисляются в обратном порядке. Из-за операции 1+ для каждого промежуточного результата другой порядок дает другой результат.

Возможно, важно понимать, что в первой версии переменная a достигла своего конечного значения к моменту достижения базового случая.

Коррекция

Я понимаю, что вы захотите провести рефакторинг первой функции, поскольку использование переменной static может оказаться невозможным, когда функция будет вызвана второй раз на верхнем уровне. В этом случае вы бы хотели, чтобы значение a было сброшено до 1, но это не так.

Вы можете заменить его дополнительным параметром, которому вы передаете начальное значение 1,0. Это не проблема, но вы можете опустить явное приведение к double, поставив a в качестве первого операнда выражения, которое требует деления с плавающей запятой. Я назвал функцию f:

double f_recur(int n, int m, double a) {
    if (m == 0)
        return a;
    return f_recur(n, m - 1, 1 + a * n / m);
}

double f(int n, int m) {
    return f_recur(n, m, 1.0);
}

Вы также можете сделать это итеративным способом, что предпочтительнее, поскольку не используется дополнительное пространство стека. На этот раз позвонил g:

double g(int n, int m) {
    double a = 1.0;
    for (int i = m; i > 0; i--) {
        a = 1 + a * n / i;
    }
    return a;
}

C не имеет необязательных аргументов

Shawn 21.05.2024 16:01

@Шон, спасибо, я по ошибке использовал C++. Исправлено сейчас.

trincot 21.05.2024 17:03

Другие вопросы по теме