Effiicient ways to count a streak of consecutive integers in MATLAB - matlab

Say I have a vector containing only logical values, such as
V = [1 0 1 0 1 1 1 1 0 0]
I would like to write a function in MATLAB which returns a 'streak' vector S for V, where S(i) represents the number of consecutive 1s in V up to but not including V(i). For the example above, the streak vector would be
S = [0 1 0 1 0 1 2 3 4 0]
Given that I have to do this for a very large matrix, I would very much appreciate any solution that is vectorized / efficient.

This should do the trick:
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
The complexity is only O(n), which I guess should be good enough.
For your sample input:
V = [1 0 1 0 1 1 1 1 0 0];
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
display(V);
display(S);
The result would be:
V =
1 0 1 0 1 1 1 1 0 0
S =
0 1 0 1 0 1 2 3 4 0

You could also do it completely vectorized with a couple intermediate steps:
V = [1 0 1 0 1 1 1 1 0 0];
Sall = cumsum(V);
stopidx = find(diff(V)==-1)+1;
V2=V;
V2(stopidx) = -Sall(stopidx)+[0 Sall(stopidx(1:end-1))];
S2 = cumsum(V2);
S = [0 S2(1:end-1)];
Afaik the only thing that can take a while is the find call; you can't use logical indexing everywhere and bypass the find call, because you need the absolute indices.

It's outside the box - but have you considered using text functions? Since strings are just vectors for Matlab it should be easy to use them.
Regexp contains some nice functions for finding repeated values.

Related

"Don't care" elements in kernel used for binary morphological tree pruning, MATLAB

I am trying to prune a skeletonized image of numbers (0-9), which is sometimes highly branched because of irregularities in the original number thickness.
For this, I am trying to use the kernels shown in Fig. 4: http://homepages.inf.ed.ac.uk/rbf/HIPR2/thin.htm, which contain "don't care" cells.
Because I don't see how to code a "don't care" element in a kernel, I am creating other kernels to cover up all of the possibilities when taking into account the "don't care" elements. However, this greatly increases the complexity of the code, as it is computationally expensive. For example, take into account the first kernel in Fig 4:
kernel1 = [1 1 1; * 1 *; 0 0 0] -->
[1 1 1; 1 1 0; 0 0 0]
[1 1 1; 0 1 1; 0 0 0]
[1 1 1; 1 1 1; 0 0 0]
[1 1 1; 0 1 0; 0 0 0]
Where * denotes "don't care" elements. Directly coding a "don't care" element would be much less cumbersome, and it would largely decrease computation time.
Does anyone have any suggestions on how to deal with this?
Victor
Thank you for your answer!
With the help of your 'result' outcome, I was able to generate all possible matrices for any number of 'don't care' elements, n. This helped out a lot!
(If anyone's interested: )
n = numel(find(A == -1))
func = #(x,n) repmat( [ ones(1, 2.^(n-x)) zeros(1, 2.^(n-x)) ] , 1, 2.^(x-1))
ind = [1:n]' %'
result = cell2mat ( arrayfun(func,ind,n*ones(1,n)','UniformOutput',false) )'
[row,col] = find(A == -1)
for i = 1:size(result,1)
for b = 1:size(result,2)
output{i}(row(b),col(b)) = result(i,b);
end
output{i} = abs(new{i} + A + isnan(A));
end
Thanks again!
I'd like to show you my approach:
Really you need to create an matrix with rows consist of all known columns (1 and 0) and unknown - *. For all unknown elements we need to create rows with all possible combinations.
So, we can find number of *, create a table with all possible combinations and combine it with known columns. For example (from your data):
[* 1 *]
to
[1 1 0;
0 1 1;
1 1 1;
0 1 0]
Or in my approach: we have 2 * so we need to create matrix of all combinations of two elements:
A =[ 0 0; B = [ 1;
0 1; and combine with 1;
1 0; 1;
1 1 ] 1 ]
Combining:
result = [ A(:,1) B A(:,2)]
This are easy operations. Only one question: how to create this matrix A.
Let's go this way:
I don't know datatype of your matrix, but in numeric we can't use * symbol. So, I will use -1 instead. (If you have symbolic or cell arrays it's easy to convert it to numeric, so my example still works just need to add several converting actions).
One more detail - let's solve your problem not only for 3-element row, but for n. So we have
A = [1 -1 -1 -1]
n = numel(find(A==-1))
func = #(x,n) repmat( [ ones(1, 2.^(n-x)) zeros(1, 2.^(n-x)) ] , 1, 2.^(x-1))
ind = [1:n]' %'
result = cell2mat ( arrayfun(func,ind,n*ones(1,n)','UniformOutput',false) )'
Result:
result =
1 1 1
1 1 0
1 0 1
1 0 0
0 1 1
0 1 0
0 0 1
0 0 0
Check your example:
A = [-1 1 -1]
result =
1 1
1 0
0 1
0 0

Generate truth table in MatLab

I want to create a truth table in MatLab with i columns and i2 rows. For example, if i=2, then
T =
[0 0]
[1 0]
[0 1]
[1 1]
Code to do this has already been created here
This is part of a larger project, which requires i large. Efficiency is a concern. Is there more efficient code to create a truth table? Does MatLab have a built in function to do this?
Edit: Sorry about the formatting!
Something like this?
n=2;
d=[0:2^n-1].';
T=dec2bin(d,n)
T =
00
01
10
11
dec2bin will give you a character array, which you can convert to logical, if needed. There's also de2bi that gives you a numeric array directly, but you need a newer version of Matlab and the ordering of the bits is reversed.
Here's Luis Mendo's speedup, which replicates dec2bin (n and d are as above):
T=rem(floor(d*pow2(1-n:0)),2);
ndgrid is very much your friend here:
function t = truthTable(n)
dims = repmat({[false, true]}, 1, n);
[grids{1:n}] = ndgrid(dims{:});
grids = cellfun(#(g)g(:), grids, 'UniformOutput',false);
t = [grids{:}];
First you need to create grids for the number of dimensions in your truth table. Once you have those you can columnize them to get the column vectors you need and you can horizontally concatenate those column vectors to get your truth table.
I imagine the performance of this will be quite competitive.
>> truthTable(2)
ans =
0 0
1 0
0 1
1 1
>> truthTable(4)
ans =
0 0 0 0
1 0 0 0
0 1 0 0
1 1 0 0
0 0 1 0
1 0 1 0
0 1 1 0
1 1 1 0
0 0 0 1
1 0 0 1
0 1 0 1
1 1 0 1
0 0 1 1
1 0 1 1
0 1 1 1
1 1 1 1
>>
>> timeit(#() truthTable(20))
ans =
0.030922626777
EDIT: Use reshape instead of column dereferencing for further performance improvement
function t = truthTable(n)
dims = repmat({[false, true]}, 1, n);
[grids{1:n}] = ndgrid(dims{:});
grids = cellfun(#(g) reshape(g,[],1), grids, 'UniformOutput',false);
t = [grids{:}];
>> timeit(#() truthTable(20))
ans =
0.016237298777
I know this question has been dead a while, but I was wondering the same thing and found a solution I like a lot. Thought I'd share it here:
fullfact(ones(1, i) + 1) - 1

Create a horizontically stretched upper triangular matrix

I'd like to create a 4x12 matrix which is very similar to a upper triangle matrix, it looks like this:
1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 1
So my question is. What is the most efficient way to create it? no loops, no cellfun. Thanks.
One vectorized approach -
nrows = 4;
ncols = 12;
row_idx = repmat(1:nrows,ncols/nrows,1)
out = bsxfun(#le,[1:nrows]',row_idx(:).')
The Matlab R2015a and later approach using the newly introduced repelem:
n = 4;
m = 3;
out = repelem(triu(ones(n)),1,m);
out =
1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 1
It even seems faster than the bsxfun approach, though I can't believe this ;)
Benchmark
Unfortunately I couldn't consider andrew's solution as it is not complete and I didn't got it totally.
function [t] = bench()
n = 4;
m = 12;
t = zeros(3,15);
for ii = 1:15
fcns = {
#() thewaywewalk(ii*n,ii*m);
#() Divakar(ii*n,ii*m);
#() LuisMendo(ii*n,ii*m);
};
% timeit
for jj = 1:100;
t(:,ii) = t(:,ii) + cellfun(#timeit, fcns);
end
end
plot(1:15,t(1,:)); hold on;
plot(1:15,t(2,:)); hold on;
plot(1:15,t(3,:)); hold on;
xlabel('Matrix size: n = x*4, m = x*12')
ylabel('timing')
legend({'thewaywewalk','Divakar','Luis Mendo'},'location','northwest')
end
function Z = thewaywewalk(n,m)
Z = repelem(triu(ones(n)),1,m/n);
end
function Z = Divakar(n,m)
row_idx = repmat(1:n,m/n,1);
Z = bsxfun(#le,[1:n]',row_idx(:).');
end
function Z = LuisMendo(n,m)
Z = reshape(repmat(permute(triu(ones(n,n)), [1 3 2]), [1 m/n 1]), [n m]);
end
First bottomline - small matrices:
The new repelem does a very good job, but also the reshape(repmat(permute... does not disappoint. The bsxfun approach stays a little behind for some medium-sized matrices, before it becomes the leader for large matrices:
Second bottomline - big matrices:
As predicted by Divakar, bsxfun is the fastest for larger matrices, actually as expected as bsxfun is always the fastest! Interesting that the other two align perfectly, on could guess they almost work the same internally.
Create an upper triangular matrix of ones, permute second and third dimensions, repeat along second dimension, and reshape into desired shape:
m = 4;
n = 12;
result = reshape(repmat(permute(triu(ones(m,m)), [1 3 2]), [1 n/m 1]), [m n]);
depending on your matlab version
m = 4;
n = 12;
dec2bin(bitshift(num,-1*[0:n/m:n-1])) %this prints out a string
these should be logical arrays (i don't have either of these so I cant test it)
decimalToBinaryVector(bitshift(num,-1*[0:n/m:n-1]))
de2bi(bitshift(num,-1*[0:n/m:n-1]))

Matlab create vectorized sequence

I want to construct a function that accepts input n and gives the vector
[n n-1 n-2 ... n-n, n-1 n-2 ... n-n, ..., n-n]
//Example
input : n=3
output : [3 2 1 0 2 1 0 1 0 0]
I know how to do this using loops, but I'm looking for a clever way to do it in MATLAB
You can use repmat to repeat the matrix a few times, and then select only the triangular part by means of tril. Like this:
n=3;
x=repmat(n:-1:0,1,n+1);
result=x(tril(ones(n+1))>0)
Or in one line:
n=3;
getfield(repmat(n:-1:0,1,n+1),{reshape(tril(ones(n+1))>0,1,(n+1)^2)})
The result of this function is the desired output:
result =
3 2 1 0 2 1 0 1 0 0
Since you haven't gotten any answers, here's a way to do it:
N = 3;
x = repmat(N:-1:0,1,N+1)-cumsum(repmat([1 zeros(1,N)],1,N+1))+1
x = x(x>=0)
x =
3 2 1 0 2 1 0 1 0 0

Matlab indexing into 2D array using

I have a 2D matrix, say M=zeros(10,10);
I have another column matrix, V=[1;2;3;4;5;6;5;4;3;2];
I would like to be able to set M(i,j) = 1 for all j >= V(i)
I know I can do this in a loop
for i=1:10
M(i,V(i):10) = 1;
end
but it seems that it would be possible to use some form of Matlab indexing to avoid using a loop. For example something like :
M(:,V:10)=1;
or
M(:,V(:):10)=1;
but neither of these produces the expected result.
Is there some syntactic sugar I can use to achieve this or should I revert to looping?
Since you're looking for syntactic sugar, here's a sort of esoteric way of doing it. :)
Assuming the length of V is the size of both dimensions in the desired matrix M, first create an identity matrix of the same size, then index appropriately and take cumsum:
V = [1;2;3;4;5;6;5;4;3;2]; #% 10x1 vector
E = eye(length(V), length(V)); #%10x10 identity matrix
M = cumsum(E(V,:),2)
M =
1 1 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 1 1
0 0 0 0 0 1 1 1 1 1
0 0 0 0 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1 1
Ok, now: less fun, but (on my machine) faster than any of the other options that have been tested so far:
n=10000;
V = randi(n-1, 1, n); #% as in #KevinRatelle's answer (but not transposed)
tic;
Vlinear = reshape(V + (0:n-1)*n, 1, []); #% find linear indices of first "ones"
M = zeros(n);
M(Vlinear)=1;
M=cumsum(M);
toc
Its hardly subtle and not really better than a loop I don't think but:
[J,I] = meshgrid(1:10,1:10);
V = [1;2;3;4;5;6;5;4;3;2];
M = J>V(I);
Enjoy.
I tried the loop method, and the 'meshgrid' method. I was wondering about the time to compute for large matrices (since the issue with loops in matlab is usually the time).
First, I optimised the code to look like this :
V = V*ones(1,n);
N = ones(1,n)'*(1:n);
M = N>=V;
Actually, N is a meshgrid, but it seems way faster to do it this way...
I tried this :
n = 10000;
V = randi(n-1,1,n)';
tic;
M = zeros(n);
for i=1:n
M(i,V(i):n) = 1;
end
toc
tic;
[J,I] = meshgrid(1:n,1:n);
M = J>=V(I);
toc
tic;
V = V*ones(1,n);
N = ones(1,n)'*(1:n);
M = N>=V;
toc
And results were :
Elapsed time is 1.726872 seconds.
Elapsed time is 5.206657 seconds.
Elapsed time is 1.548600 seconds.
But methods using matrices instead of loops are memory consuming for large n. I would personnaly stick to the loop.
try this:
v = [1;2;3;4;5;6;5;4;3;2];
n = 10;
M = repmat((1:n)', 1, numel(v)) > repmat(v', n, 1);