How to provide a positive and negative value for the tolerance value?
2 views (last 30 days)
Show older comments
I am using a while loop to determine the taylor expansion of cos(x), I am trying to work out how many iterations (with the result of each iteration) it takes to reach the tolerance value (tol) for a given value of x (in this case pi). However I want the tolerance value to be both positive and negative, as otherwise the line "while result(counter)>tol" reaches a negative value, the code stops.
clear; clc;
tol = 1E-5; %I want the tolerance within the code to be +/- this value
x = pi;
counter = 1;
n(counter) = 0;
result(counter) = (((-1)^n(counter)) * (x^(2*n(counter)))/(factorial(2*n(counter))));
while result(counter)>tol %When I run this while loop, it stops at the 2nd value (-4.9348) still outside the tolerance.
counter = counter + 1;
n(counter) = n(counter-1) + 1;
result(counter) = (((-1)^n(counter)) * (x^(2*n(counter)))/(factorial(2*n(counter))))
end
0 Comments
Accepted Answer
Voss
on 17 Sep 2024
"I want the tolerance value to be both positive and negative"
However, you also need to compare the current result with the target (cos(pi) = -1).
And taylor expansion of cos(x) is a sum, so you need to add each new result term to what's previously been calculated.
tol = 1E-5;
x = pi;
target = cos(x);
counter = 1;
n(counter) = 0;
result(counter) = (((-1)^n(counter)) * (x^(2*n(counter)))/(factorial(2*n(counter))));
while abs(result(counter)-target) > tol
counter = counter + 1;
n(counter) = n(counter-1) + 1;
result(counter) = result(counter-1) + (((-1)^n(counter)) * (x^(2*n(counter)))/(factorial(2*n(counter))))
end
More Answers (1)
John D'Errico
on 17 Sep 2024
Edited: John D'Errico
on 18 Sep 2024
You could just test to see if the current term being added in is less than your tolerance (in absolute value of course.) Since the terms are generally decreasing in magnitude, that should work. Not as good as seeing if the sum of the terms has stabilized, of course, since I can easily show a series where the terms are growing smaller all the time, yet the series is itself divergent. That is, consider the sum of 1/n, as n goes to infinity. It is of course divergent, so merely stopping when any one term is less than the tolerance will never work.
An idea perhaps is to recognize this is an alternating series. So you should expect the cumulative sums to alternate above, then below the final value. And that suggests you might try using the average of each consecutive pair of estimates, to possilby make the series converge more rapidly. For example, try these ideas on the cosine series. I'll use a few different values for x, some sufficiently large that the terms will start to grow initially.
x = (0.5:0.5:5)';
n = 0:25;
terms = (-1).^n.*x.^(2*n)./factorial(2*n);
First, when should we stop here? The simple rule that when an individual term is less than eps will suffice on an alternating series. This is especially reasonable when the terms are decreasing in magnitude as fast as they will in this series.
semilogy(n,abs(terms),'-o')
ylim([1e-20,50])
yline(eps,'k')
grid on
In the above plot, with x no larger than 1, even 10 terms will probably be adequate, but when x==5, 18 terms will be a better choice.
We can see how well things do on the cumulative sum.
truth = cos(x);
estimate = cumsum(terms,2);
deviation = abs(truth - estimate);
semilogy(n,deviation,'-o')
grid on
And here we see that approach did indeed work. 9 or 10 terms will be sufficient when x=1, and 5 terms should be adequate when x=1/2, but around 17-18 terms would be necessary for the highest curve where x=5. (The missing dot on the blue curve indicates a case where the difference was EXACTLY zero by what is virtually random luck, and a log plot does not like exactly zero values.)
Finally, when x grows larger, the first few terms of that alternating series actually grow in size. This mean you will see massive subtractive cancellation happen. (With x only as large as 5, this is not really, truly "massive". Had I done the some computation at x=100, that would be a significant problem and indeed massive.) In turn, that tells us to expect to see a final error on the sum that will actually grow with x. You can see that happening here. There are actually some very nice tricks you can use to reduce that final error to a much lower value. These would often be described as range reduction methods.
There are many such schemes one can find, eploying a variety of trigonometric identities to reduce the range the series will need to cover, and that will make your series more rapidly convergent.
Finally, I mentioned the idea of taking the average of consecutive estimates on an alternating series. When the terms are decreasing so rapidly in magnitude, that will gain you very little. (On some other series, that ends up being a good idea indeed, but not here.)
estimate2 = conv2(estimate,[0.5 0.5],'valid');
deviation2 = abs(truth - estimate2);
semilogy(conv(n,[.5 .5],'valid'),deviation2,'-o')
grid on
Of course, all of this goes far beyond your original question. Look carefully at how I computed and summed the cosine sequences, as something possibly of interest. But you might find some of this to be of interest, at some point in time. Or, maybe the next person to read this question.
See Also
Categories
Find more on Waveform Generation in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!