Make vector of elements less than each element of another vector - matlab

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.

Related

Matlab: make a matrix by looping through an index without using a For loop?

I have a function A(k) which returns a matrix dependent on k when I type for example A(1). I want to automatically create the matrix:
[A(3)*A(2)*A(1) A(3)*A(2) A(3)]
In Mathematica I can do for example:
Table[Apply[Dot,Table[A(k),{k,3,i,-1}]],{i,1,3}]
Assume A(k) is a function which returns a 3x3 matrix. For example typing A(1) may return:
[1,2,3;4,5,6;7,8,9]
Explanation of Mathematica code: Table[A(k),{k,3,i,-1}] is a "loop" from k=3 to k=i with each iteration decrementing k by -1. Therefore the output would be a list {A(3),A(2),...,A(i)}... obviously k starting from 3 then for i=2 the output list will be {A(3),A(2)}. The function Apply[Dot,Table[A(k),{k,3,i,-1}]] multiplied the elements of the list together. For i=2 this produces A(3)*A(2). Finally, Table[Apply[Dot,Table[A(k),{k,3,i,-1}]],{i,1,3}] applies the same logic as the first statement, looping i from 1 to 3. Because the inner table depends on i, this creates a list of elements {A(3)*A(2)*A(1),A(3)*A(2),A(3)}. A list is Mathematica's version of a matrix.
How can I achieve the same effect in MATLAB, i.e. not use a for loop to achieve the result? Thanks!
For the record, here's a looping version, mostly for subsequent timing checks. This is not the answer you want, but the answer you need;)
N = 10; %size of A
old = eye(N);
M2 = [];
for i=3:-1:1
new = old*A(i);
M2 = [new M2];
old = new;
end
Or if you want to be really efficient (which is probably not the case):
N = 10; %size of A
M2 = A(3);
old = M2;
for i=2:-1:1
new = old*A(i);
M2 = [new M2];
old = new;
end
This answer basically is the solution to the problem, but as we are discussing efficient use of matlab I want to leave my thoughts here how to use Matlab efficient. Instead of the large 2D-Matrix it is much simpler to create a 3D-Matrix where the results of each multiplication are stacked. If a 3D-Matrix is acceptable use the code as it is, otherwise comment in the last line to get a 2D-matrix.
M3=[];
n=3;
M3(:,:,n) = A(n);
for ix=n-1:-1:1
M3(:,:,ix) = M3(:,:,ix+1)*A(i);
end
%M3=reshape(M3,size(M3,1),[]);
You can use arrayfun() with indexes as parameters, eg:
%// Compute the product A(3)*A(2)*...*A(idx)
F = #(idx) prod(reshape(cell2mat(arrayfun(A,3:-1:idx, 'UniformOutput', false)),10,10,4-idx),3);
%// Use the last function with idx=1:3
M = cell2mat(arrayfun(F,1:3, 'UniformOutput', false));

generate periodic function from given function

let say that from given function f(t), we want to construct new function which is given from existed function by this way
where T is some constant let say T=3; of course k can't be from -infinity to infinity in reality because we can't do infinity summation using computer,so it is first my afford
first let us define our function
function y=f(t);
y=-1/(t^2);
end
and second program
k=-1000:1:999;
F=zeros(1,length(k));
T=3;
for t=1:length(k)
F(t)=sum(f(t+k*T));
end
but when i am running second program ,i am getting
>> program
Error using ^
Inputs must be a scalar and a square matrix.
To compute elementwise POWER, use POWER (.^) instead.
Error in f (line 2)
y=-1/(t^2);
Error in program (line 5)
F(t)=sum(f(t+k*T));
so i have two question related to this program :
1.first what is error why it shows me mistake
how can i do it in excel? can i simplify it somehow? thanks in advance
EDITED :
i have changed my code by this way
k=-1000:1:999;
F=zeros(1,length(k));
T=3;
for t=1:length(k)
result=0;
for l=1:length(k)
result=result+f(t+k(l)*T);
end
F(t)=result;
end
is it ok?
To solve your problem in a vectorized way, you'll have to change the function f such that it can be called with vectors as input. This is, as #patrik suggested, achieved by using the element-wise operators .* ./ .^ (Afaik, no .+ .- exist). Unfortunately the comment of #rayryeng is not entirely correct, which may have lead to confusion. The correct way is to use the element-wise operators for both the division ./ and the square .^:
function y = f(t)
y = -1 ./ (t.^2);
end
Your existing code (first version)
k = -1000:1:999;
F = zeros(1,length(k));
T = 3;
for t=1:length(k)
F(t) = sum(f(t+k*T));
end
then works as expected (and is much faster then the version you posted in the edit).
You can even eliminate the for loop and use arrayfun instead. For simple functions f, you can also use function handles instead of creating a separate file. This gives
f = #(t) -1 ./ (t.^2);
k = -1000:1:999;
t = 1:2000;
T = 3;
F = arrayfun(#(x)sum(f(x+k*T)), t);
and is even faster and a simple one-liner. arrayfun takes any function handle as first input. We create a function handle which takes an argument x and does the sum over all k: #(x) sum(f(x+k*T). The second argument, the vector t, contains all values for which the function handle is evaluated.
As proposed by #Divakar in comments, you can also use the bsxfun function:
f = #(t) -1 ./ (t.^2);
k = -1000:1:999;
t = 1:2000;
T = 3;
F = sum(f(bsxfun(#plus,k*T,t.')),2);
where bsxfun creates a matrix containing all combinations between t and k*T, they are all evaluated using f(...) and last, the sum along the second dimension sums over all k's.
Benchmarking
Lets compare these solutions:
Combination of for loop and sum (original question):
Elapsed time is 0.043969 seconds.
Go through all combinations in 2 for loops (edited question):
Elapsed time is 1.367181 seconds.
Vectorized approach with arrayfun:
Elapsed time is 0.063748 seconds.
Vectorized approach with bsxfun as proposed by #Divakar:
Elapsed time is 0.099399 seconds.
So (sadly) the first solution including a for loop beats both vectorized approaches. For larger k vectors (-10000:1:9999), this behavior can be reproduced. The conclusion seems to be that MATLAB has indeed learned how to optimize for loops.

Subscript indices must either be real positive integers or logicals error within Matlab decay program

I am having issues with a code of mine dealing with decay. The error "Subscript indices must either be real positive integers or logicals" continues to occur no matter how many times I attempt to fix the line of code: M=M(t)+h.*F
Here is the complete code so that it may be easier to solve the issue:
M=10000;
M=#(t) M*exp(-4.5*t);
F=-4.5*M(t);
h=.1;
t(1)=0;
tmax=20;
n=(tmax-t(1))/h;
i=1;
while h<=.5
while i<=n
t=t+h;
M=M(t)+h.*F;
data_out=[t,M];
dlmwrite('single_decay_euler_h.txt',data_out,'delimiter','\t','-append');
i=i+1;
end
h=h+.1;
end
Thanks for any help.
In the start, you're setting M = 5000;. In the following line, you're creating an anonymous function also called M:
M=#(t) M*exp(-4.5*t);
Now, your initial M = 5000 variable has been overwritten, and is substituted by the function:
M(t) = 5000 * exp(-4.5*t); %// Note that the first M is used to get 5000
Thereafter you do F = -4.5*M(t). I don't know what the value t is here, but you're giving F the value -4.5 * 5000 * exp(-4.5*t), for some value of t. You are not creating a function F.
In the first iteration of the loop, M=M(t)+h.*F; is interpreted as:
M = 5000 * exp(-4.5*0) + 0.1*F %// Where F has some value determined by previous
%// the function above and the previous value of t
%// -4.5*0 is because t = 0
M is now no longer a function, but a single scalar value. The next iteration t = 0.1. When you do: M=M(t)+h.*F; now, it interprets both the first and second M as a variable, not a function. t is therefore used as an index, instead of being an input parameter to the function M (since you have overwritten it).
When you are writing M(t), you are trying to access the 0.1'th element of the 1x1 matrix (scalar) M, which obviously isn't possible.
Additional notes:
The outer while loop has no purpose as it stands now, since i isn't reset after the inner loop. When you're finished with the first iteration of the outer loop, i is already >n, so it will never enter the inner loop again.
You shouldn't mix variable and function names (as you do with M. Use different names, always. Unless you have a very good reason not to.
data_out=[t,M]; is a growing vector inside a loop. This is considered very bad practice, ans is very slow. It's better to pre-allocate memory for the vector, for instance using data_out = zeros(k,1), and insert new values using indexes, data_out(ii) = M.
It's recommended not to use i and j as variable names in MATLAB as these also represent the imaginary unit sqrt(-1). This might cause some strange bugs if you're not paying attention to it.
You can almost certainly do what you're trying to do without loops. However, the function you have written is not functioning, and it's not explained all too well what you're trying to do, so it's hard to give advice as to how you can get what you want (but I'll give it a try). I'm skipping the dlmwrite-part, because I don't really understand what you want to output.
M = 5000;
t0 = 0;
tmax = 20;
h = 0.1; %// I prefer leading zeros in decimal numbers
t = t0: h: tmax;
data_out = M .* exp(-4.5 * t);
The problem is caused by M(t) in your code, because t is not an integer or logical (t=1,1.1,1.2,...)
You need to change your code to pass an integer as a subscript. Either multiply t by 10, or don't use the matrix M if you don't need it.

nlfilter taking same values twice

I used nlfilter for a test function of mine as follows:
function funct
clear all;
clc;
I = rand(11,11);
ld = input('Enter the lag = ') % prompt for lag distance
A = nlfilter(I, [7 7], #dirvar);
% Subfunction
function [h] = dirvar(I)
c = (size(I)+1)/2
EW = I(c(1),c(2):end)
h = length(EW) - ld
end
end
The function works fine but it is expected that nlfilter progresses element by element, but in first two iterations the values of EW will be same 0.2089 0.4162 0.9398 0.1058. But then onwards for all iterations the next element is selected, for 3rd it is 0.4162 0.9398 0.1058 0.1920, for 4th it is 0.9398 0.1058 0.1920 0.5201 and so on. Why is it so?
This is nothing to worry about. It happens because nlfilter needs to evaluate your function to know what kind of output to create. So it uses feval once before starting to move across the image. The output from this feval call is what you see the first time.
From the nlfilter code:
% Find out what output type to make.
rows = 0:(nhood(1)-1);
cols = 0:(nhood(2)-1);
b = mkconstarray(class(feval(fun,aa(1+rows,1+cols),params{:})), 0, size(a));
% Apply fun to each neighborhood of a
f = waitbar(0,'Applying neighborhood operation...');
for i=1:ma,
for j=1:na,
x = aa(i+rows,j+cols);
b(i,j) = feval(fun,x,params{:});
end
waitbar(i/ma)
end
The 4th line call to eval is what you observe as the first output from EW, but it is not used to anything other than making the b matrix the right class. All the proper iterations happen in the for loop below. This means that the "duplicate" values you observe does not affect your final output matrix, and you need not worry.
I hope you know what the length function does? It does not give you the Euclidean length of a vector, but rather the largest dimension of a vector (so in your case, that should be 4). If you want the Euclidean length (or 2-norm), use the function norm instead. If your code does the right thing, you might want to use something like:
sz = size(I,2);
h = sz - (sz+1)/2 - ld;
In your example, this means that depending on the lag you provide, the output should be constant. Also note that you might want to put semicolons after each line in your subfunction and that using clear all as the first line of a function is useless since a function will always be executed in its own workspace (that will however clear persistent or global variables, but you don't use them in your code).

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.