For loop with multiplication step in MATLAB - matlab

Is there any way to use a for-loop in MATLAB with a custom step? What I want to do is iterate over all powers of 2 lesser than a given number. The equivalent loop in C++ (for example) would be:
for (int i = 1; i < 65; i *= 2)
Note 1: This is the kind of iteration that best fits for-loops, so I'd like to not use while-loops.
Note 2: I'm actually using Octave, not MATLAB.

Perhaps you want something along the lines of
for i=2.^[1:6]
disp(i)
end
Except you will need to figure out the range of exponents. This uses the fact that since
a_(i+1) = a_i*2 this can be rewritten as a_i = 2^i.
Otherwise you could do something like the following
i=1;
while i<65
i=i*2;
disp(i);
end

You can iterate over any vector, so you can use vector operations to create your vector of values before you start your loop. A loop over the first 100 square numbers, for example, could be written like so:
values_to_iterate = [1:100].^2;
for i = values_to_iterate
i
end
Or you could loop over each position in the vector values_to_iterate (this gives the same result, but has the benefit that i keeps track of how many iterations you have done - this is useful if you are writing a result from each loop sequentially to an output vector):
values_to_iterate = [1:100].^2;
for i = 1:length(values_to_iterate)
values_to_iterate(i)
results_vector(i) = some_function( values_to_iterate(i) );
end
More concisely, you can write the first example as simply:
for i = [1:100].^2
i
end
Unlike in C, there doesn't have to be a 'rule' to get from one value to the next.
The vector iterated over can be completely arbitrary:
for i = [10, -1000, 23.3, 5, inf]
i
end

Related

Make vector of elements less than each element of another vector

I have a vector, v, of N positive integers whose values I do not know ahead of time. I would like to construct another vector, a, where the values in this new vector are determined by the values in v according to the following rules:
- The elements in a are all integers up to and including the value of each element in v
- 0 entries are included only once, but positive integers appear twice in a row
For example, if v is [1,0,2] then a should be: [0,1,1,0,0,1,1,2,2].
Is there a way to do this without just doing a for-loop with lots of if statements?
I've written the code in loop format but would like a vectorized function to handle it.
The classical version of your problem is to create a vector a with the concatenation of 1:n(i) where n(i) is the ith entry in a vector b, e.g.
b = [1,4,2];
gives a vector a
a = [1,1,2,3,4,1,2];
This problem is solved using cumsum on a vector ones(1,sum(b)) but resetting the sum at the points 1+cumsum(b(1:end-1)) corresponding to where the next sequence starts.
To solve your specific problem, we can do something similar. As you need two entries per step, we use a vector 0.5 * ones(1,sum(b*2+1)) together with floor. As you in addition only want the entry 0 to occur once, we will just have to start each sequence at 0.5 instead of at 0 (which would yield floor([0,0.5,...]) = [0,0,...]).
So in total we have something like
% construct the list of 0.5s
a = 0.5*ones(1,sum(b*2+1))
% Reset the sum where a new sequence should start
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1)
% Cumulate it and find the floor
a = floor(cumsum(a))
Note that all operations here are vectorised!
Benchmark:
You can do a benchmark using the following code
function SO()
b =randi([0,100],[1,1000]);
t1 = timeit(#() Nicky(b));
t2 = timeit(#() Recursive(b));
t3 = timeit(#() oneliner(b));
if all(Nicky(b) == Recursive(b)) && all(Recursive(b) == oneliner(b))
disp("All methods give the same result")
else
disp("Something wrong!")
end
disp("Vectorised time: "+t1+"s")
disp("Recursive time: "+t2+"s")
disp("One-Liner time: "+t3+"s")
end
function [a] = Nicky(b)
a = 0.5*ones(1,sum(b*2+1));
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1);
a = floor(cumsum(a));
end
function out=Recursive(arr)
out=myfun(arr);
function local_out=myfun(arr)
if isscalar(arr)
if arr
local_out=sort([0,1:arr,1:arr]); % this is faster
else
local_out=0;
end
else
local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
end
end
end
function b = oneliner(a)
b = cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),a,'UniformOutput',false));
end
Which gives me
All methods give the same result
Vectorised time: 0.00083574s
Recursive time: 0.0074404s
One-Liner time: 0.0099933s
So the vectorised one is indeed the fastest, by a factor approximately 10.
This can be done with a one-liner using eval:
a = eval(['[' sprintf('sort([0 1:%i 1:%i]) ',[v(:) v(:)]') ']']);
Here is another solution that does not use eval. Not sure what is intended by "vectorized function" but the following code is compact and can be easily made into a function:
a = [];
for i = 1:numel(v)
a = [a sort([0 1:v(i) 1:v(i)])];
end
Is there a way to do this without just doing a for loop with lots of if statements?
Sure. How about recursion? Of course, there is no guarantee that Matlab has tail call optimization.
For example, in a file named filename.m
function out=filename(arr)
out=myfun(in);
function local_out=myfun(arr)
if isscalar(arr)
if arr
local_out=sort([0,1:arr,1:arr]); % this is faster
else
local_out=0;
end
else
local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
end
end
end
in cmd, type
input=[1,0,2];
filename(input);
You can take off the parent function. I added it just hoping Matlab can spot the recursion within filename.m and optimize for it.
would like a vectorized function to handle it.
Sure. Although I don't see the point of vectorizing in such a unique puzzle that is not generalizable to other applications. I also don't foresee a performance boost.
For example, assuming input is 1-by-N. In cmd, type
input=[1,0,2];
cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),input,'UniformOutput',false)
Benchmark
In R2018a
>> clear all
>> in=randi([0,100],[1,100]); N=10000;
>> T=zeros(N,1);tic; for i=1:N; filename(in) ;T(i)=toc;end; mean(T),
ans =
1.5647
>> T=zeros(N,1);tic; for i=1:N; cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),in,'UniformOutput',false)); T(i)=toc;end; mean(T),
ans =
3.8699
Ofc, I tested with a few more different inputs. The 'vectorized' method is always about twice as long.
Conclusion: Recursion is faster.

Looping over fifty elements at a time (matlab)

I am currently new to matlab, and I am trying to do a loop over fifty elements at a time instead of one element at a time. For example, I have a list of 1000 elements, and I would like to compute the sum for every fifty elements. Instead of doing a sum function through indexing, it would be much faster with a loop. How would I go about doing this?
I.e. [1,...50th element, 51th element... 100...]
Output would be the the sum values of 1:50, 51:100, 101:150... and so on.
Thanks in advance
I'm not really sure what you mean by "a sum function through indexing", but there are various ways to do this. In general I try to avoid explicit loops in Matlab and let MathWorks functions do their magic.
results = zeros(20,1);
for i = 1:20
results(i) = sum(1 + (50 * (i - 1)):50 + 50 * (i - 1));
end
Another option is to do something like arrayfun.
sIndex = 1:50:951;
eIndex = 50:50:1000;
result = arrayfun(#(x, y) sum(x:y), sIndex, eIndex);
You could also use reshape and sum to do it one shot.
numbers = 1:1000;
numbers2 = reshape(numbers, 50, []);
result = sum(numbers2);
This last method is what I personally would say is a Matlab way of doing it. arrayfun is basically a wrapper around a loop and the loop is...well a loop.
In case you need the sum, you can also use movsum:
array = 1:1000;
win = 50; % window size
msum = movsum(array,win,'Endpoints','discard');
in the same way, you can use:
movmax Moving maximum
movmean Moving mean
movmedian Moving median
movmin Moving minimum
movstd Moving standard deviation
movvar Moving variance
Using cumsum and diff you can obtain the desired result.
C = [0 cumsum(a)];
out = diff(C(1:50:end));

Basic structure of a for loop

I am trying to write a MATLAB function that accepts non-integer, n, and then returns the factorial of it, n!. I am supposed to use a for loop. I tried with
"for n >= 0"
but this did not work. Is there a way how I can fix this?
I wrote this code over here but this doesn't give me the correct answer..
function fact = fac(n);
for fact = n
if n >=0
factorial(n)
disp(n)
elseif n < 0
disp('Cannot take negative integers')
break
end
end
Any kind of help will be highly appreciated.
You need to read the docs and I would highly recommend doing a basic tutorial. The docs state
for index = values
statements
end
So your first idea of for n >= 0 is completely wrong because a for doesn't allow for the >. That would be the way you would write a while loop.
Your next idea of for fact = n does fit the pattern of for index = values, however, your values is a single number, n, and so this loop will only have one single iteration which is obviously not what you want.
If you wanted to loop from 1 to n you need to create a vector, (i.e. the values from the docs) that contains all the numbers from 1 to n. In MATLAB you can do this easily like this: values = 1:n. Now you can call for fact = values and you will iterate all the way from 1 to n. However, it is very strange practice to use this intermediary variable values, I was just using it to illustrate what the docs are talking about. The correct standard syntax is
for fact = 1:n
Now, for a factorial (although technically you'll get the same thing), it is clearer to actually loop from n down to 1. So we can do that by declaring a step size of -1:
for fact = n:-1:1
So now we can find the factorial like so:
function output = fac(n)
output = n;
for iter = n-1:-1:2 %// note there is really no need to go to 1 since multiplying by 1 doesn't change the value. Also start at n-1 since we initialized output to be n already
output = output*iter;
end
end
Calling the builtin factorial function inside your own function really defeats the purpose of this exercise. Lastly I see that you have added a little error check to make sure you don't get negative numbers, that is good however the check should not be inside the loop!
function output = fac(n)
if n < 0
error('Input n must be greater than zero'); %// I use error rather than disp here as it gives clearer feedback to the user
else if n == 0
output = 1; %// by definition
else
output = n;
for iter = n-1:-1:2
output = output*iter;
end
end
end
I don't get the point, what you are trying to do with "for". What I think, what you want to do is:
function fact = fac(n);
if n >= 0
n = floor(n);
fact = factorial(n);
disp(fact)
elseif n < 0
disp('Cannot take negative integers')
return
end
end
Depending on your preferences you can replace floor(round towards minus infinity) by round(round towards nearest integer) or ceil(round towards plus infinity). Any round operation is necessary to ensure n is an integer.

MATLAB Piecewise function

I have to construct the following function in MATLAB and am having trouble.
Consider the function s(t) defined for t in [0,4) by
{ sin(pi*t/2) , for t in [0,1)
s(t) = { -(t-2)^3 , for t in [1,3)*
{ sin(pi*t/2) , for t in [3,4)
(i) Generate a column vector s consisting of 512 uniform
samples of this function over the interval [0,4). (This
is best done by concatenating three vectors.)
I know it has to be something of the form.
N = 512;
s = sin(5*t/N).' ;
But I need s to be the piecewise function, can someone provide assistance with this?
If I understand correctly, you're trying to create 3 vectors which calculate the specific function outputs for all t, then take slices of each and concatenate them depending on the actual value of t. This is inefficient as you're initialising 3 times as many vectors as you actually want (memory), and also making 3 times as many calculations (CPU), most of which will just be thrown away. To top it off, it'll be a bit tricky to use concatenate if your t is ever not as you expect (i.e. monotonically increasing). It might be an unlikely situation, but better to be general.
Here are two alternatives, the first is imho the nice Matlab way, the second is the more conventional way (you might be more used to that if you're coming from C++ or something, I was for a long time).
function example()
t = linspace(0,4,513); % generate your time-trajectory
t = t(1:end-1); % exclude final value which is 4
tic
traj1 = myFunc(t);
toc
tic
traj2 = classicStyle(t);
toc
end
function trajectory = myFunc(t)
trajectory = zeros(size(t)); % since you know the size of your output, generate it at the beginning. More efficient than dynamically growing this.
% you could put an assert for t>0 and t<3, otherwise you could end up with 0s wherever t is outside your expected range
% find the indices for each piecewise segment you care about
idx1 = find(t<1);
idx2 = find(t>=1 & t<3);
idx3 = find(t>=3 & t<4);
% now calculate each entry apprioriately
trajectory(idx1) = sin(pi.*t(idx1)./2);
trajectory(idx2) = -(t(idx2)-2).^3;
trajectory(idx3) = sin(pi.*t(idx3)./2);
end
function trajectory = classicStyle(t)
trajectory = zeros(size(t));
% conventional way: loop over each t, and differentiate with if-else
% works, but a lot more code and ugly
for i=1:numel(t)
if t(i)<1
trajectory(i) = sin(pi*t(i)/2);
elseif t(i)>=1 & t(i)<3
trajectory(i) = -(t(i)-2)^3;
elseif t(i)>=3 & t(i)<4
trajectory(i) = sin(pi*t(i)/2);
else
error('t is beyond bounds!')
end
end
end
Note that when I tried it, the 'conventional way' is sometimes faster for the sampling size you're working on, although the first way (myFunc) is definitely faster as you scale up really a lot. In anycase I recommend the first approach, as it is much easier to read.

Storing Results of a Operation in a Matrix

Let's say I want to take the sin of 1 through 100 (in degrees).
I come from a C background so my instinct is to loop 1 through 100 in a for loop (something I can do in Matlab). In a matrix/vector/array I would store sin(x) where x is the counter of the for loop.
I cannot figure out how to do this in Matlab. Do I create a array like
x = [1 .. 100];
And then do
x[offset] = numberHere;
I know the "correct" way. For operations like addition you use .+ instead of + and with a function like sin I'm pretty sure you just do
resultArray = sin(x);
I just want to know that I could do it the C way in case that ever came up, thus my question here on SO. :)
% vectorized
x = sin((1:100)*pi/180);
or
% nonvectorized
x=[];
for i = 1:100
x(i) = sin(i*pi/180);
end
I beleive this can actually be done as a one liner in MatLab:
x = sind(1:100);
Note that you use sind() instead of sin(). Sin() takes radians as arguments.
As others have already pointed out there are for-loops in MATLAB as well.
help for
should give you everything you need about how it works. The difference from C is that the loop can go over objects and not only an integer:
objects = struct('Name', {'obj1', 'obj2'}, 'Field1', {'Value1','Value2'});
for x = objects
disp(sprintf('Object %s Field1 = %d', x.Name, x.Field1))
end
That example will output:
Object obj1 Field1 = Value1
Object obj2 field1 = Value2
This could have been done as
for i=1:length(objects)
x = objects(i);
disp(sprintf('Object %s Field1 = %d', x.Name, x.Field1))
end
And now to what I really wanted to say: If you ever write a for loop in MATLAB, stop and think!. For most tasks you can vectorize the code so that it uses matrix operations and builtin functions instead of looping over the data. This usually gives a huge speed gain. It is not uncommon that vectorized code executes 100x faster than looping code. Recent versions of MATLAB has JIT compilation which makes it less dramatic than before, but still: Always vectorize if you can.
#Daniel Fath
I think you'll need the final line to read
resultArray(i) = sin(x(i)) (rather than x(1))
I think you can also do:
for i = x
...
though that will behave differently if x is not a simple 1-100 vector
Hmm, if understand correctly you want a loop like structure
resultArray = zeros(1,length(x)) %% initialization aint necessary I just forgot how you dynamically add members :x
for i = 1:length(x) %% starts with 1 instead of zero
resultArray(i) = sin(x(i))
end
Warning I didn't test this but it should be about right.