Интегрирование через трапециевидные суммы в MATLAB

Мне нужна помощь в нахождении интеграла функции с использованием трапециевидных сумм.

Программа должна брать последовательные трапециевидные суммы с n = 1, 2, 3, ... подынтервалами до тех пор, пока не будет двух соседних значений n, отличающихся меньше заданного допуска. Мне нужен хотя бы один цикл FOR внутри цикла WHILE, и я не хочу использовать функцию trapz. Программа принимает четыре входа:

  • f: дескриптор функции для функции x.
  • a: Действительное число.
  • b: действительное число больше a.
  • tolerance: действительное число, положительное и очень маленькое.

Проблема, с которой я столкнулся, заключается в попытке реализовать формулу трапециевидных сумм, которая равна Δx/2[y0 + 2y1 + 2y2 + … + 2yn-1 + yn]

Вот мой код, и область, в которой я застрял, — это часть «суммы» в цикле FOR. Я пытаюсь суммировать 2y2 + 2y3....2yn-1, так как я уже насчитал 2y1. Я получаю ответ, но он не так точен, как должен быть. Например, я получаю 6.071717974723753 вместо 6.101605982576467.

Спасибо за любую помощь!

function t=trapintegral(f,a,b,tol)
format compact; format long;
syms x;
oldtrap = ((b-a)/2)*(f(a)+f(b));
n = 2;
h = (b-a)/n;
newtrap = (h/2)*(f(a)+(2*f(a+h))+f(b));
while (abs(newtrap-oldtrap)>=tol)
    oldtrap = newtrap;
    for i=[3:n]
        dx = (b-a)/n;
        trapezoidsum = (dx/2)*(f(x) + (2*sum(f(a+(3:n-1))))+f(b));
        newtrap = trapezoidsum;
    end
end
t = newtrap;
end

person Bruce Wayne    schedule 16.08.2014    source источник
comment
после каждых n подынтервалов создается больше трапеций - непонятно, что вы имеете в виду.   -  person Oliver Charlesworth    schedule 16.08.2014
comment
после каждых n подынтервалов количество трапеций под кривой увеличивается   -  person Bruce Wayne    schedule 16.08.2014
comment
Интеграция трапеций включена в ядро ​​Matlab, см. trapz, и если вам интересно, как это реализовано, посмотрите на источник: edit trapz. Кстати, при программировании Matlab следует избегать циклов, насколько это возможно.   -  person A. Donda    schedule 17.08.2014


Ответы (1)


Причина, по которой этот код не работает, заключается в том, что в вашем суммировании для правила трапеций есть две небольшие ошибки. Я имею в виду именно это утверждение:

trapezoidsum = (dx/2)*(f(x) + (2*sum(f(a+(3:n-1))))+f(b));

Вспомним уравнение трапецеидального правила интегрирования:

Источник: Википедия

Для первой ошибки f(x) должно быть f(a), поскольку вы включаете начальную точку, и его не следует оставлять символическим. На самом деле вам следует просто избавиться от оператора syms x, так как он бесполезен в вашем скрипте. a соответствует x1 в соответствии с приведенным выше уравнением.

Следующая ошибка — второй член. На самом деле вам нужно умножить значения индекса (3:n-1) на dx. Кроме того, на самом деле это должно идти от (1:n-1), и я объясню позже. Приведенное выше уравнение переходит от 2 к N, но для наших целей мы собираемся перейти от 1 к N-1, поскольку ваш код настроен таким образом.

Помните, что в правиле трапеций вы делите конечный интервал на n частей. iая часть определяется как:

x_i = a + dx*i;  ,

где i идет от 1 до N-1. Обратите внимание, что это начинается с 1, а не с 3. Причина в том, что первая часть уже учитывается f(a), и мы считаем только до N-1, так как часть N учитывается f(b). Для уравнения это изменяется от 2 до N, и, изменив код таким образом, именно это мы и делаем в конце.

Таким образом, ваше заявление на самом деле должно быть:

trapezoidsum = (dx/2)*(f(a) + (2*sum(f(a+dx*(1:n-1))))+f(b));

Попробуйте это и дайте мне знать, если вы получите правильный ответ. FWIW, MATLAB уже реализует трапециевидную интеграцию, выполняя trapz как @ADonda уже указал. Однако вам необходимо правильно структурировать значения x и y, прежде чем настраивать это. Другими словами, вам нужно будет заранее настроить dx, затем рассчитать x очков, используя уравнение x_i, которое я указал выше, а затем использовать их для получения значений y. Затем вы используете trapz для вычисления площади. Другими словами:

dx = (b-a) / n;
x = a + dx*(0:n);
y = f(x);
trapezoidsum = trapz(x,y);

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


Редактировать - 17 августа 2014 г.

Я понял, почему ваш код не работает. Вот почему:

  1. Цикл for не нужен. Взгляните на итерацию цикла for. У вас есть цикл, идущий от i = [3:n], но вы не вообще ссылаетесь на переменную i в своем цикле. Соответственно, вам это совершенно не нужно.

  2. Вы неправильно вычисляете последовательные интервалы. Что вам нужно сделать, так это, когда вы вычисляете трапецеидальную сумму для nth подинтервала, вы затем увеличиваете это значение n, а затем снова вычисляете трапецеидальное правило. Это значение не увеличивается должным образом в вашем цикле while, поэтому ваша область никогда не улучшается.

  3. Вам нужно сохранить предыдущую область внутри цикла while, а затем, когда вы вычисляете следующую площадь, именно тогда вы определяете, меньше ли разница между областями, чем допуск. Мы также можем избавиться от этого кода в начале, который пытается вычислить площадь для n = 2. Это не нужно, так как мы можем поместить это в ваш цикл while. Таким образом, вот как должен выглядеть ваш код:


function t=trapintegral(f,a,b,tol)
format long; %// Got rid of format compact. Useless
%// n starts at 2 - Also removed syms x - Useless statement
n = 2;
newtrap = ((b-a)/2)*(f(a) + f(b)); %// Initialize
oldtrap = 0; %// Initialize to 0
while (abs(newtrap-oldtrap)>=tol)
    oldtrap = newtrap; %//Save the old area from the previous iteration
    dx = (b-a)/n; %//Compute width
    %//Determine sum
    trapezoidsum = (dx/2)*(f(a) + (2*sum(f(a+dx*(1:n-1))))+f(b));
    newtrap = trapezoidsum; % //This is the new sum
    n = n + 1; % //Go to the next value of n
end
t = newtrap;
end

Запустив ваш код, я получаю следующее:

trapezoidsum = trapintegral(@(x) (x+x.^2).^(1/3),1,4,0.00001)

trapezoidsum = 

6.111776299189033

Предостережение

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

Редактировать № 2 — 18 августа 2014 г.

Вы сказали, что хотите хотя бы один цикл for. Это очень неэффективно, и тот, кто указал наличие в коде одного цикла for, на самом деле не знает, как работает MATLAB. Тем не менее, вы можете использовать цикл for для накопления термина sum. В качестве таких:

function t=trapintegral(f,a,b,tol)
format long; %// Got rid of format compact. Useless
%// n starts at 3 - Also removed syms x - Useless statement
n = 3;
%// Compute for n = 2 first, then proceed if we don't get a better
%// difference tolerance
newtrap = ((b-a)/2)*(f(a) + f(b)); %// Initialize
oldtrap = 0; %// Initialize to 0
while (abs(newtrap-oldtrap)>=tol)
    oldtrap = newtrap; %//Save the old area from the previous iteration
    dx = (b-a)/n; %//Compute width
    %//Determine sum
    %// Initialize
    trapezoidsum = (dx/2)*(f(a) + f(b));

    %// Accumulate sum terms
    %// Note that we multiply each term by (dx/2), but because of the
    %// factor of 2 for each of these terms, these cancel and we thus have dx
    for n2 = 1 : n-1
        trapezoidsum = trapezoidsum + dx*f(a + dx*n2);
    end
    newtrap = trapezoidsum; % //This is the new sum
    n = n + 1; % //Go to the next value of n
end
t = newtrap;
end

Удачи!

person rayryeng    schedule 17.08.2014
comment
Привет, спасибо за ответ. Я пытаюсь написать код без использования trapz, поэтому воспользовался вашим первым предложением, но ответ не так близок, как должен быть. Например, trapintegral(@(x) (x+x^2)^(1/3),1,4,0.00001) должен дать 6,111776299189035, но вместо этого я получаю 6,071717974723753. Я еще немного повозился с этим, но я не могу изменить свой ответ - person Bruce Wayne; 18.08.2014
comment
@BruceWayne Я понял это. Вы написали loop неправильно. Вы неправильно сравниваете последовательные n подинтервала. Скоро сделаю ответ. - person rayryeng; 18.08.2014
comment
Хорошо, спасибо, что указали на проблему с циклом. Но мне нужен хотя бы 1 цикл FOR в цикле while. Я понимаю код, который вы написали, но у меня возникли проблемы с реализацией где-то цикла for. Спасибо, хотя за помощь до сих пор! :) - person Bruce Wayne; 18.08.2014
comment
@BruceWayne - Зачем вам нужен хотя бы один цикл for? Это крайне неэффективно. Я изменю код, но настоятельно не рекомендую использовать цикл for. - person rayryeng; 18.08.2014
comment
Я знаю, и я сожалею об этом, но это просто то, что я хочу включить в свой код. Еще раз, тем не менее, большое спасибо за ваш вклад до сих пор. - person Bruce Wayne; 18.08.2014
comment
@BruceWayne не проблема. Я отредактировал свой ответ, добавив это для цикла. Дай мне знать, если это работает. Удачи! - person rayryeng; 18.08.2014
comment
Большое спасибо за Вашу помощь! Ваш код был не таким точным только для одного сценария, который я решил, заменив realmax на ((b-a)/2)*(f(a)+f(b)). Установка начального newtrap на это заставляла программу работать каждый раз. Так что еще раз, спасибо за все ваше время, усилия и вклад! Это было высоко оценено! - person Bruce Wayne; 18.08.2014
comment
@BruceWayne - Не беспокойся. Я изменю это в своем коде. Рад, что это помогло! - person rayryeng; 18.08.2014