Reputation: 1499
Let's say I have a function that can compute one output from one input, e.g.
function y = sqrt_newton(x)
y = x ./ 2;
yo = y;
y = 0.5.*(y + x ./ y);
while abs(y - yo) > eps * abs(y)
yo = y;
y = 0.5.*(y + x ./ y);
end
end
I'd like to be able to apply this function to a vector input say sqrt_newton(2:9)
like with built-in functions. What is the best way to achieve this with a condition at the beginning of the loop or some if-clause inside? I'd like to avoid writing an extra function as a wrapper just to loop through the input vector, if possible at all.
My current cumbersome solution
What I do up until now is:
I have to expand first the inputs to the same size (using finargsz from the finance toolbox, but if you know of another core function that does the same, that would be great)
record the shape using size
deal
the inputs
loop thru all the input elements
reshape
the output
It seems that the numel
function alleviates the need of all this heavy lifting but extra comments would be most welcome.
Upvotes: 3
Views: 463
Reputation: 4118
There's always arrayfun
. You can keep the code you have, putting it into an inner function.
function y = sqrt_newton(z)
y = arrayfun(@inner, z);
function y= inner(x)
y = x ./ 2;
yo = y;
y = 0.5.*(y + x ./ y);
while abs(y - yo) > eps * abs(y)
yo = y;
y = 0.5.*(y + x ./ y);
end
end
end
Edit: The above solution has the advantage of being trivial to implement after you have it working with a 1x1 input, but the loops in the other answers are way faster for large inputs. For example, on my computer, the code
tic; sqrt_newton(rand(500)); toc
runs in ~1.24 seconds
with my code, 0.06 seconds
with @Ramashalanka's code, and 0.28 seconds
with @GuntherStruyf's code.
Upvotes: 2
Reputation: 8854
Well, you could vectorize it as follows (with any
):
function y = sqrt_newton(x)
y = x / 2;
yo = y;
y = 0.5*(y + x ./ y);
while any(abs(y - yo) > eps * abs(y))
yo = y;
y = 0.5*(y + x ./ y);
end
end
Then you get:
>> sqrt_newton(2:9)
ans =
1.4142 1.7321 2.0000 2.2361 2.4495 2.6458 2.8284 3.0000
>> ans.^2-(2:9)
ans =
1.0e-14 *
-0.0444 -0.0444 0 0.0888 -0.0888 0.0888 -0.1776 0
as expected. However, I wouldn't recommend it, since you're doing unnecessary operations on elements that have already converged. I'd just use a for
loop over x
at the start of the function:
function yall = sqrt_newton(xall)
yall = zeros(size(xall));
for xn=1:numel(xall)
x = xall(xn);
y = x / 2;
yo = y;
y = 0.5*(y + x ./ y);
while abs(y - yo) > eps * abs(y)
yo = y;
y = 0.5*(y + x ./ y);
end
yall(xn)=y;
end
end
Set the size yall
at the start to avoid it increasing in size throughout the loop.
Upvotes: 2
Reputation: 11168
I think in general you'll have to use a loop, because of the unknown character of the operation of the function. In case it's a linear operation, vectorization is possible.
For your example I'd use the following:
function y = sqrt_newton(x)
y = x ./ 2;
yo = y;
y = 0.5.*(y + x ./ y);
for i=1:numel(x)
while abs(y(i) - yo(i)) > eps * abs(y(i))
yo(i) = y(i);
y(i) = 0.5*(y(i) + x(i) / y(i));
end
end
end
I use numel, instead of size, so it can handle any array I throw at it
Upvotes: 2