How to provide a positive and negative value for the tolerance value?

2 ビュー (過去 30 日間)
Asher
Asher 2024 年 9 月 17 日
編集済み: John D'Errico 2024 年 9 月 18 日
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

採用された回答

Voss
Voss 2024 年 9 月 17 日
"I want the tolerance value to be both positive and negative"
Use abs.
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
result = 1×2
1.0000 -3.9348
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
result = 1×3
1.0000 -3.9348 0.1239
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
result = 1×4
1.0000 -3.9348 0.1239 -1.2114
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
result = 1×5
1.0000 -3.9348 0.1239 -1.2114 -0.9760
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
result = 1×6
1.0000 -3.9348 0.1239 -1.2114 -0.9760 -1.0018
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
result = 1×7
1.0000 -3.9348 0.1239 -1.2114 -0.9760 -1.0018 -0.9999
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
result = 1×8
1.0000 -3.9348 0.1239 -1.2114 -0.9760 -1.0018 -0.9999 -1.0000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
  2 件のコメント
Asher
Asher 2024 年 9 月 17 日
Fantastic response, thank you very much. Makes a lot of sense.
Voss
Voss 2024 年 9 月 17 日
You're welcome!

サインインしてコメントする。

その他の回答 (1 件)

John D'Errico
John D'Errico 2024 年 9 月 17 日
編集済み: John D'Errico 2024 年 9 月 18 日
Good answer by @Voss of course. I'll merely add a few points of interest.
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.
  1 件のコメント
Asher
Asher 2024 年 9 月 18 日
Wow that certainly will help expand my knowledge of Matlab! I will certainly come back to have a look at this in the future, and hope it helps potential future matlab learners to understand their code!

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeWaveform Generation についてさらに検索

製品


リリース

R2023a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by