问题
I need help finding an integral of a function using trapezoidal sums.
The program should take successive trapezoidal sums with n = 1, 2, 3, ...
subintervals until there are two neighouring values of n
that differ by less than a given tolerance. I want at least one FOR
loop within a WHILE
loop and I don't want to use the trapz function. The program takes four inputs:
f
: A function handle for a function ofx
.a
: A real number.b
: A real number larger thana
.tolerance
: A real number that is positive and very small
The problem I have is trying to implement the formula for trapezoidal sums which is
Δx/2[y0 + 2y1 + 2y2 + … + 2yn-1 + yn]
Here is my code, and the area I'm stuck in is the "sum" part within the FOR
loop. I'm trying to sum up 2y2 + 2y3....2yn-1
since I already accounted for 2y1
. I get an answer, but it isn't as accurate as it should be. For example, I get 6.071717974723753
instead of 6.101605982576467
.
Thanks for any help!
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
回答1:
The reason why this code isn't working is because there are two slight errors in your summation for the trapezoidal rule. What I am precisely referring to is this statement:
trapezoidsum = (dx/2)*(f(x) + (2*sum(f(a+(3:n-1))))+f(b));
Recall the equation for the trapezoidal integration rule:
Source: Wikipedia
For the first error, f(x)
should be f(a)
as you are including the starting point, and shouldn't be left as symbolic. In fact, you should simply get rid of the syms x
statement as it is not useful in your script. a
corresponds to x1
by consulting the above equation.
The next error is the second term. You actually need to multiply your index values (3:n-1)
by dx
. Also, this should actually go from (1:n-1)
and I'll explain later. The equation above goes from 2 to N
, but for our purposes, we are going to go from 1 to N-1
as you have your code set up like that.
Remember, in the trapezoidal rule, you are subdividing the finite interval into n
pieces. The ith piece is defined as:
x_i = a + dx*i; ,
where i
goes from 1
up to N-1
. Note that this starts at 1 and not 3. The reason why is because the first piece is already taken into account by f(a)
, and we only count up to N-1
as piece N
is accounted by f(b)
. For the equation, this goes from 2 to N
and by modifying the code this way, this is precisely what we are doing in the end.
Therefore, your statement actually needs to be:
trapezoidsum = (dx/2)*(f(a) + (2*sum(f(a+dx*(1:n-1))))+f(b));
Try this and let me know if you get the right answer. FWIW, MATLAB already implements trapezoidal integration by doing trapz as @ADonda already pointed out. However, you need to properly structure what your x
and y
values are before you set this up. In other words, you would need to set up your dx
before hand, then calculate your x
points using the x_i
equation that I specified above, then use these to generate your y
values. You then use trapz
to calculate the area. In other words:
dx = (b-a) / n;
x = a + dx*(0:n);
y = f(x);
trapezoidsum = trapz(x,y);
You can use the above code as a reference to see if you are implementing the trapezoidal rule correctly. Your implementation and using the above code should generate the same results. All you have to do is change the value of n
, then run this code to generate the approximation of the area for different subdivisions underneath your curve.
Edit - August 17th, 2014
I figured out why your code isn't working. Here are the reasons why:
The
for
loop is unnecessary. Take a look at thefor
loop iteration. You have a loop going fromi = [3:n]
yet you don't reference thei
variable at all in your loop. As such, you don't need this at all.You are not computing successive intervals properly. What you need to do is when you compute the trapezoidal sum for the
n
th subinterval, you then increment this value ofn
, then compute the trapezoidal rule again. This value is not being incremented properly in yourwhile
loop, which is why your area is never improving.You need to save the previous area inside the
while
loop, then when you compute the next area, that's when you determine whether or not the difference between the areas is less than the tolerance. We can also get rid of that code at the beginning that tries and compute the area forn = 2
. That's not needed, as we can place this inside yourwhile
loop. As such, this is what your code should look like:
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
By running your code, this is what I get:
trapezoidsum = trapintegral(@(x) (x+x.^2).^(1/3),1,4,0.00001)
trapezoidsum =
6.111776299189033
Caveat
Look at the way I defined your function. You must use element-by-element operations as the sum
command inside the loop will be vectorized. Take a look at the ^
operations specifically. You need to prepend a dot to the operations. Once you do this, I get the right answer.
Edit #2 - August 18th, 2014
You said you want at least one for
loop. This is highly inefficient, and whoever specified having one for
loop in the code really doesn't know how MATLAB works. Nevertheless, you can use the for
loop to accumulate the sum
term. As such:
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
Good luck!
来源:https://stackoverflow.com/questions/25342462/integration-via-trapezoidal-sums-in-matlab