I got an index-array ind of length len which is either 0 or 1. I need results coming from a complex and time-intensive function func to be stored in a result-vector res (also length len). Input-vector is called inp.
I could do this via the two following ways:
% 1st way:
res = zeros(len,1);
res(ind) = func(inp);
% 2nd way:
res = ind .* func(inp);
My question for the 2nd one: for those entries where ind is 0, does MATLAB evaluate func()? I hope not, because zero times anything else is zero, so it is a waste to evaluate func.
For those entries where ind is 0, your first option won't work because res(ind) will throw an error:
Subscript indices must either be real positive integers or logicals.
Anyway, I think this is what you are looking for:
allowed_indices = ind > 0; % Logical indexing of valid indices
res = zeros(len,1);
res(allowed_indices) = func(inp(allowed_indices));
It probably does, but this is very easy for you to test by making a test func that prints to the console and then make all of ind 0 and see if it prints anything (I'm sure it will, but I don't have MATLAB to check myself).
I hoped no because zero times sth. else is zero
Not necessarily, what about 0*inf? Or 0*NaN?
If the point of your question is which is more efficient, test both using timeit
Related
I am trying to code something in Matlab and it involves a lot of accessing elements in vectors. Below is a snippet of code that I am working on:
x(1)=1;
for i=2:18
x(i)=0;
end
for i=1:18
y(i)=1;
end
for i = 0:262124
x(i+18+1) = x(i+7+1) + mod(x(i+1),2);
y(i+18+1) = y(i+10+1) + y(i+7+1) + y(i+5+1) + mod(y(i+1), 2);
end
% n can be = 0, 1, 2,..., 262142
n = 2;
for i = 0: 262142
z(i+1) = x(mod(i+n+1, 262143)); %error: Subscript indices must either be real positive integers or logicals.
end
In the last "for" loop where I am initialising vector z(), I get an error saying: "Subscript indices must either be real positive integers or logicals." However, when I do not suppres z(i+1) by ommiting the semi colon, the program is able to run, and I can see the values of z in the workspace. Why is this?
The code I am writing in Matlab is based upon the series of instructions shown in the image below. However, I can't seem to track down my error which leads to me not being able to access the elements of x() (without not suppressing the output of z()).
I appreciate any ideas :-) Thank you!
The code breaks at that loop last iteration because , for i=262140 you get
(mod(i+n+1, 262143)) = 0
so you cant access x(0) in matlab. the first elements of any variable is x(1).
In addition, and not related to your question, this code doesn't use the advantages matlab has, instead of
for i=2:18
x(i)=0;
end
you can just write:
x(2:18)=0;
etc
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.
I've been trying to implement the following integral in MATLAB
Given a number n, I wrote the code that returns an array with n elements, containing approximations of each integral.
First, I tried this using a 'for' loop and the recurrence relationship on the first line. But from the 20th integral and above the values are completely wrong (correct to 0 significant figures and wrong sign).
The same goes if I use the explicit formula on the second line and two 'for' loops.
As n grows larger, so does the error on the approximations.
So the main issue here is that I haven't found a way to minimize the error as much as possible.
Any ideas? Thanks in advance.
Here is an example of the code and the resulting values, using the second formula:
This integral, for positive values of n, cannot have values >1 or <0
First attempt:
I tried the iterative method and found interesting thing. The approximation may not be true for all n. In fact if I keep track of (n-1)*I(n-1) in each loop I can see
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
There is some problem around n=18. In fact, I18 = 0.05719 and 18*I18 = 1.029 which is larger than 1. I don't think there is any numerical error or number overflow in this procedure.
Second attempt:
To make sure the maths is correct (I verified twice on paper) I used trapz to numerically evaluate the integral, and n=18 didn't cause any problem.
>> x = linspace(0,1,1+1e4);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
>> trapz(f(5))
ans =
1.708934160520510e-01
>> trapz(f(17))
ans =
5.571936009790170e-02
>> trapz(f(18))
ans =
5.277113416899408e-02
>>
A closer look is as follows. I18 is slightly different (to the 4th significant digit) between the (stable) numerical method and (unstable) iterative method. 18*I18 is therefore possible to exceed 1.
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
J = zeros(20,3);
x = linspace(0,1,1+1e4);
f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
J(1,1) = trapz(f(1));
for jj = 2:20
J(jj,1) = trapz(f(jj));
J(jj,2) = jj-1;
J(jj,3) = (jj-1)*J(jj-1,1);
end
I suspect there is an error in each iterative step due to the nature of numerical computations. If the iteration is long, the error propagates and, unfortunately in this case, amplifies rapidly. In order to verify this, I combined the above two methods into a hybrid algo. For most of the time the iterative way is used, and once in a while a numerical integral is evaluated from scratch without relying on previous iterations.
K = zeros(40,4);
K(1,1) = 1-1/exp(1);
for kk = 2:40
K(kk,2) = trapz(f(kk));
K(kk,3) = (kk-1)*K(kk-1,1);
K(kk,4) = 1-K(kk,3);
if mod(kk,5) == 0
K(kk,1) = K(kk,2);
else
K(kk,1) = K(kk,4);
end
end
If the iteration lasts more than 4 steps, error amplification will be large enough to invert the sign, and starts nonrecoverable oscillation.
The code should be able to explain all the data structures. Anyway, let me put some focus here. The second column is the result of trapz, which is the numerical integral done on the non-iterative integration definition of I(n). The third column is (n-1)*I(n-1) and should be always positive and less than 1. The forth column is 1-(n-1)*I(n-1) and should always be positive. The first column is the choice I have made between the trapz result and iterative result, to be the "true" value of I(n).
As can be seen here, in each iteration there is a small error compared to the independent numerical way. The error grows in the 3rd and 4th iteration and finally breaks the thing in its 5th. This is observed around n=25, under the case that I pick the numerical result in every 5 loops since the beginning.
Conclusion: There is nothing wrong with any definition of this integral. However the numerical error when evaluating the expressions is unfortunately aggregating, hence limiting the way you can perform the computation.
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.
I have a matrix with integers and I need to replace all appearances of 2 with -5. What is the most efficient way to do it? I made it the way below, but I am sure there is more elegant way.
a=[1,2,3;1,3,5;2,2,2]
ind_plain = find(a == 2)
[row_indx col_indx] = ind2sub(size(a), ind_plain)
for el_id=1:length(row_indx)
a(row_indx(el_id),col_indx(el_id)) = -5;
end
Instead of loop I I seek for something like: a(row_indx,col_indx) = -5, which does not work.
find is not needed in this case.
Use logical indexing instead:
a(a == 2) = -5
In case of searching whether a matrix is equal to inf you should use
a(isinf(a)) = -5
The general case is:
Mat(boolMask) = val
where Mat is your matrix, boolMask is another matrix of logical values, and val is the assignment value
Try this:
a(a==2) = -5;
The somewhat longer version would be
ind_plain = find(a == 2);
a(ind_plain) = -5;
In other words, you can index a matrix directly using linear indexes, no need to convert them using ind2sub -- very useful! But as demonstrated above, you can get even shorter if you index the matrix using a boolean matrix.
By the way, you should put semicolons after your statements if (as is usually the case) you're not interested in getting the result of the statement dumped out to the console.
The Martin B's method is good if you are changing values in vector. However, to use it in matrix you need to get linear indices.
The easiest solution I found is to use changem function. Very easy to use:
mapout = changem(Z,newcode,oldcode)
In your case: newA = changem(a, 5, -2)
More info: http://www.mathworks.com/help/map/ref/changem.html
Here's a trivial, unoptimised, probably slow implementation of changem from the Mapping Toolbox.
function mapout = changem(Z, newcode, oldcode)
% Idential to the Mapping Toolbox's changem
% Note the weird order: newcode, oldcode. I left it unchanged from Matlab.
if numel(newcode) ~= numel(oldcode)
error('newcode and oldcode must be equal length');
end
mapout = Z;
for ii = 1:numel(oldcode)
mapout(Z == oldcode(ii)) = newcode(ii);
end
end