What's the difference between these two codes to get the sum of infinite series in MATLAB? - matlab

1 + 1/(2^4) + 1/(3^4) + 1/(4^4) + ...
This is the infinite series that I'd like to get the sum value. So I wrote this code in MATLAB.
n = 1;
numToAdd = 1;
sum = 0;
while numToAdd > 0
numToAdd = n^(-4);
sum = sum + numToAdd;
n = n + 1;
end
disp(sum);
But I couldn't get the result because this code occurred an infinite loop. However, the code I write underneath -- it worked well. It took only a second.
n = 1;
oldsum = -1;
newsum = 0;
while newsum > oldsum
oldsum = newsum;
newsum = newsum + n^(-4);
n = n+1;
end
disp(newsum);
I read these codes again and googled for a while, but coudln't find out the critical point. What makes the difference between these two codes? Is it a matter of precision of double in MATLAB?

The first version would have to go down to the minimum value for a double ~10^-308, while the second will only need to go down to the machine epsilon ~10^-16. The epsilon value is the largest value x such that 1+x = 1.
This means the first version will need approximately 10^77 iterations, while the second only needs 10^4.

The problem boils down to this:
x = 1.23456789; % Some random number
xEqualsXPlusEps = (x == x + 1e-20)
ZeroEqualsEps = (0 == 1e-20)
xEqualsXPlusEps will be true, while ZeroEqualsEps is false. This is due to the way floating point arithmetic works. The value 1e-20 is smaller than the least significant bit of x, so x+1e-20 won't be larger than x. However 1e-20 is not considered equal to 0. In comparison to x, 1e-20 is relatively small, whereas in comparison to 0, 1e-20 is not small at all.
To fix this problem you would have to use:
while numToAdd > tolerance %// Instead of > 0
where tolerance is some small number greater than zero.

Related

Speed Up Finding Number of Elements Between Values Matlab

I've created what is a fairly simple MATLAB script to simulate the behaviour discussed in this question over on Maths SE.
clearvars;
samples = 1000;
x = 256;
r=exprnd(1/20e6,1,samples); % Generate exponentially distributed randoms.
endTime = sum(r);
quickMean=sum(r(1:x))/x; % Quick calc the mean and median.
quickMedian=0.693 * quickMean;
p = cumsum(r); % Convert event deltas into timestamps
bitstream = false(1,samples);
time = 0;
lastTime = 0;
for i = 1:samples
lastTime = time;
time = time + quickMedian;
if (numel(p(p < time & p > lastTime)) > 0)
bitstream(i) = true;
end
if (time > p(end))
break
end
end
ratio = sum(bitstream)/samples;
The script seems to work, however, if I use a large number of samples (say a million), which would be beneficial, it really crawls.
I'm assuming that the problematic statement is this one:
p(p < time & p > lastTime)
Is there a more efficient way to check if any elements in an array fall between two values?
As I mentioned in my comment, we can use the fact that p is monotonically increasing and ignore values less than lastTime. If we find the last value for which p < time, only the values to the right can be greater than* time on the next iteration (lastTime).
clearvars;
samples = 1000;
x = 256;
r=exprnd(1/20e6,1,samples); % Generate exponentially distributed randoms.
endTime = sum(r);
quickMean=sum(r(1:x))/x; % Quick calc the mean and median.
quickMedian=0.693 * quickMean;
p = cumsum(r); % Convert event deltas into timestamps
bitstream = false(1,samples);
time = 0;
lastTime = 0;
% code is the same up to here ---
lastTimeIdx = 1; % index of (last value < lastTime) + 1
for i = 1:samples
lastTime = time;
time = time + quickMedian;
valsInRange = p(lastTimeIdx:end) < time; % p > lastTime & p < time
timeIdx = find(valsInRange, 1, 'last'); % returns [] or index
if ~isempty(timeIdx)
bitstream(i) = true;
lastTimeIdx = lastTimeIdx + timeIdx; % update start of next search
end
if (time > p(end))
break
end
end
ratio = sum(bitstream)/samples;
*Actually, this is "greater than or equal to", but since the values of p are unique, they are the same thing.
Okay, I just tried histc in Octave. I'm embarrassed to say that it's ridiculously fast. Like 4 orders of magnitude faster. Here's the code I used, but histc is deprecated in MATLAB, and the binning for histcounts is different, so you may have to play with it a bit.
bitstream_hist = histc(p, [0:samples]*quickMedian) > 0;
bitstream_hist = bitstream_hist(1:samples);
One million samples finishes in a fraction of a second. Sorry I didn't think of this sooner.
Let’s examine that whole expression:
numel(p(p < time & p > lastTime)) > 0
We can separate that out for clarity:
I = p < time & p > lastTime;
tmp = p(I);
n = numel(tmp);
n > 0
Here, the creation of tmp is pretty expensive: it looks at where I is true, and it copies those elements over to a new array. But the only thing you do with this array is seeing how many elements it has. Logically n will be equal to the number of true elements in I. And you don’t really need this number, you just need to know if it’s larger than 0. That is, you want to know if any of the elements in I is true. You can do so with any:
any(p < time & p > lastTime)

How do I linearly interpolate past missing values using future values in a while loop?

I am using MATLAB R2020a on a MacOS. I am trying to remove outlier values in a while loop. This involves calculating an exponentially weighted moving mean and then comparing this a vector value. If the conditions are met, the vector input is then added to a separate vector of 'acceptable' values. The while loop then advances to the next input and calculates the new exponentially weighted moving average which includes the newly accepted vector input.
However, if the condition is not met, I written code so that, instead of adding the input sample, a zero is added to the vector of 'acceptable' values. Upon the next acceptable value being added, I currently have it so the zero immediately before is replaced by the mean of the 2 framing acceptable values. However, this only accounts for one past zero and not for multiple outliers. Replacing with a framing mean may also introduce aliaising errors.
Is there any way that the zeros can instead be replaced by linearly interpolating the "candidate outlier" point using the gradient based on the framing 2 accepted vector input values? That is, is there a way of counting backwards within the while loop to search for and replace zeros as soon as a new 'acceptable' value is found?
I would very much appreciate any suggestions, thanks in advance.
%Calculate exponentially weighted moving mean and tau without outliers
accepted_means = zeros(length(cycle_periods_filtered),1); % array for accepted exponentially weighted means
accepted_means(1) = cycle_periods_filtered(1);
k = zeros(length(cycle_periods_filtered),1); % array for accepted raw cycle periods
m = zeros(length(cycle_periods_filtered), 1); % array for raw periods for all cycles with outliers replaced by mean of framing values
k(1) = cycle_periods_filtered(1);
m(1) = cycle_periods_filtered(1);
tau = m/3; % pre-allocation for efficiency
i = 2; % index for counting through input signal
j = 2; % index for counting through accepted exponential mean values
n = 2; % index for counting through raw periods of all cycles
cycle_index3(1) = 1;
while i <= length(cycle_periods_filtered)
mavCurrent = (1 - 1/w(j))*accepted_means(j - 1) + (1/w(j))*cycle_periods_filtered(i);
if cycle_periods_filtered(i) < 1.5*(accepted_means(j - 1)) && cycle_periods_filtered(i) > 0.5*(accepted_means(j - 1)) % Identify high and low outliers
accepted_means(j) = mavCurrent;
k(j) = cycle_periods_filtered(i);
m(n) = cycle_periods_filtered(i);
cycle_index3(n) = i;
tau(n) = m(n)/3;
if m(n - 1) == 0
m(n - 1) = (k(j) + k(j - 1))/2;
tau(n - 1) = m(n)/3;
end
j = j + 1;
n = n + 1;
else
m(n) = 0;
n = n + 1;
end
i = i + 1;
end
% Scrap the tail
accepted_means(j - 1:end)=[];
k(j - 1:end) = [];

Approximate pi using finitie series

Given the equation to approximate pi
I need to the number of terms (n) that are needed to obtain an approximation that is within 10^(-12) of the actual value of pi. The code I have to find the n looks like this:
The while loop statement I have seems to never end, so I feel like my code must be wrong.
Try something along these lines (transcribed from your image), incrementing the number of approximation terms n inside your infinite while loop:
s = 1
n = 1
while true
s = abs(pi - approximate_pi(n))
if s <= 0.001
break
end
n = n + 1
end
On a related note, this calculation is a little bit pointless if you know the value of pi beforehand. Termination condition should be on the absolute magnitude of the n-th term.
The way you're doing it makes sense only if you're trying to find out minimum n for which your approximation series produces the result within some margin of error.
Edit. So, normally you would do it like this:
n = 1;
sum_running = 0
sum_target = (pi^2 - 8) / 16;
while true
sum_running += 1 / ((2*n-1)^2 * (2*n+1)^2);
if abs(sum_target - sum_running) <= 10e-12
break
end
n += 1;
end
pi_approx = sqrt(16*sum_running + 8)
There's no need to keep recalculating pi approximation up to n terms, for each new n. This is has O(n) complexity, while your initial solution had O(n^2), so it's much faster for large n.

MatLab using Fixed Point method to find a root

I wanna find a root for the following function with an error less than 0.05%
f= 3*x*tan(x)=1
In the MatLab i've wrote that code to do so:
clc,close all
syms x;
x0 = 3.5
f= 3*x*tan(x)-1;
df = diff(f,x);
while (1)
x1 = 1 / 3*tan(x0)
%DIRV.. z= tan(x0)^2/3 + 1/3
er = (abs((x1 - x0)/x1))*100
if ( er <= 0.05)
break;
end
x0 = x1;
pause(1)
end
But It keeps running an infinite loop with error 200.00 I dunno why.
Don't use while true, as that's usually uncalled for and prone to getting stuck in infinite loops, like here. Simply set a limit on the while instead:
while er > 0.05
%//your code
end
Additionally, to prevent getting stuck in an infinite loop you can use an iteration counter and set a maximum number of iterations:
ItCount = 0;
MaxIt = 1e5; %// maximum 10,000 iterations
while er > 0.05 & ItCount<MaxIt
%//your code
ItCount=ItCount+1;
end
I see four points of discussion that I'll address separately:
Why does the error seemingly saturate at 200.0 and the loop continue infinitely?
The fixed-point iterator, as written in your code, is finding the root of f(x) = x - tan(x)/3; in other words, find a value of x at which the graphs of x and tan(x)/3 cross. The only point where this is true is 0. And, if you look at the value of the iterants, the value of x1 is approaching 0. Good.
The bad news is that you are also dividing by that value converging toward 0. While the value of x1 remains finite, in a floating point arithmetic sense, the division works but may become inaccurate, and er actually goes NaN after enough iterations because x1 underflowed below the smallest denormalized number in the IEEE-754 standard.
Why is er 200 before then? It is approximately 200 because the value of x1 is approximately 1/3 of the value of x0 since tan(x)/3 locally behaves as x/3 a la its Taylor Expansion about 0. And abs(1 - 3)*100 == 200.
Divisions-by-zero and relative orders-of-magnitude are why it is sometimes best to look at the absolute and relative error measures for both the values of the independent variable and function value. If need be, even putting an extremely (relatively) small finite, constant value in the denominator of the relative calculation isn't entirely a bad thing in my mind (I remember seeing it in some numerical recipe books), but that's just a band-aid for robustness's sake that typically hides a more serious error.
This convergence is far different compared to the Newton-Raphson iterations because it has absolutely no knowledge of slope and the fixed-point iteration will converge to wherever the fixed-point is (forgive the minor tautology), assuming it does converge. Unfortunately, if I remember correctly, fixed-point convergence is only guaranteed if the function is continuous in some measure, and tan(x) is not; therefore, convergence is not guaranteed since those pesky poles get in the way.
The function it appears you want to find the root of is f(x) = 3*x*tan(x)-1. A fixed-point iterator of that function would be x = 1/(3*tan(x)) or x = 1/3*cot(x), which is looking for the intersection of 3*tan(x) and 1/x. However, due to point number (2), those iterators still behave badly since they are discontinuous.
A slightly different iterator x = atan(1/(3*x)) should behave a lot better since small values of x will produce a finite value because atan(x) is continuous along the whole real line. The only drawback is that the domain of x is limited to the interval (-pi/2,pi/2), but if it converges, I think the restriction is worth it.
Lastly, for any similar future coding endeavors, I do highly recommend #Adriaan's advice. If would like a sort of compromise between the styles, most of my iterative functions are written with a semantic variable notDone like this:
iter = 0;
iterMax = 1E4;
tol = 0.05;
notDone = 0.05 < er & iter < iterMax;
while notDone
%//your code
iter = iter + 1;
notDone = 0.05 < er & iter < iterMax;
end
You can add flags and all that jazz, but that format is what I frequently use.
I believe that the code below achieves what you are after using Newton's method for the convergence. Please leave a comment if I have missed something.
% find x: 3*x*tan(x) = 1
f = #(x) 3*x*tan(x)-1;
dfdx = #(x) 3*tan(x)+3*x*sec(x)^2;
tolerance = 0.05; % your value?
perturbation = 1e-2;
converged = 1;
x = 3.5;
f_x = f(x);
% Use Newton s method to find the root
count = 0;
err = 10*tolerance; % something bigger than tolerance to start
while (err >= tolerance)
count = count + 1;
if (count > 1e3)
converged = 0;
disp('Did not converge.');
break;
end
x0 = x;
dfdx_x = dfdx(x);
if (dfdx_x ~= 0)
% Avoid division by zero
f_x = f(x);
x = x - f_x/dfdx_x;
else
% Perturb x and go back to top of while loop
x = x + perturbation;
continue;
end
err = (abs((x - x0)/x))*100;
end
if (converged)
disp(['Converged to ' num2str(x,'%10.8e') ' in ' num2str(count) ...
' iterations.']);
end

How can I make this MATLAB code fast (m-file script)?

I want to make the code below fast. It takes so long time to run, and I got this error:
Warning: FOR loop index is too large. Truncating to 2147483647.
I need to calculate over 3^100 so... is it impossible?
function sodiv = divisorSum(n)
sodiv = 0;
for i=1:n
if (mod(n,i) == 0)
sodiv = sodiv + i;
end
end
end
function finalSum1 = formular1(N,n)
finalSum1 = 0;
for k = 1:N
finalSum1 = finalSum1 + (divisorSum(k) * divisorSum(3^n*(N-k)));
end
end
Nv=100;
nv=[1:20];
for i=1:length(nv)
tic;
nfunc1(i)=formular1(Nv,nv(i));
nt1(i)=toc;
sprintf('nt1 : %d finished, %f', i,nt1(i))
end
The purpose of this code is to check the algorithm's calculation time.
The algorithm is too general and inefficient for this particular problem.
I understand you want to sum the divisors of 3^100. But these divisors are easily determined.
S = 1 + 3 + 3^2 + 3^3 + ... + 3^100, a geometric series.
3*S = 3 + 3^2 + ... + 3^101
subtract
2*S = 3^101 - 1
S = (3^101 - 1)/2
This code will never finish, because it is so inefficient.
For instance, there is a function that counts number of all divisors and is going through all numbers from 1 to N and count. But using an efficient formula would make it run much master.
Let's say that one need to sum divisors of number a^b where a is prime number.
Instead of calculating a^b and going form 1 to a^b, one can see that it is better going
a^1, a^2, a^3, ..., a^n, because only these numbers are divisors. But you can go even further and observe that the sum of these numbers are the sum of geometric progression so the number of divisors become:
sum divisors, a^b = (a^(b+1)-1) / (a-1)