Vectorization of 2 for loops in MATLAB - matlab

I have just started exploring world of vectorization. I got the 1-D vectorization down but i am having trouble vectorizing the following code. I want to do away with at least one of the for loops if possible b/c I plan to use this on a much larger data set over many iterations so saving computation time is of the essence.
CityPairs = [7 3
3 1
3 1
1 7
7 1
3 4
5 1
4 6];
Offices = [1;3;7];
nOffices = size(Offices,1);
connection = zeros(nOffices);
for i = 1:nOffices
for j = 1:nOffices
connection(i,j) = sum(Offices(i) == CityPairs(:,1)...
& CityPairs(:,2) == Offices(j));
end
end
disp(connection)
In this example there are 7 cities, three of which have offices. I want a pairwise matrix for the cities with offices to capture the sum of all the one way connections between each. The answer for above problem should be:
0 0 1
2 0 0
1 1 0
Any suggestions are welcome. Thanks in advance.
Keith

Here's an alternative solution with sparse:
dims = max(max(CityPairs), max(Offices));
A = sparse(CityPairs(:, 1), CityPairs(:, 2), 1, dims(1), dims(2));
result = full(A(Offices, Offices));
This should speed up your computation a bit1 when compared to the suggested bsxfun solution.
1 Runs 5 times faster on MATLAB 2012a (Windows Server 2008 R2 running on a 2.27GHz 16-core Intel Xeon processor)

Your task is some selective cross-tabulation. You can accomplish this easily by accumulating counts to the positions of interest, indexed by your Offices:
% Row and col subs
[~,rsubs] = ismember(CityPairs(:,1),Offices);
[~,csubs] = ismember(CityPairs(:,2),Offices);
% Select where both belong to Offices, i.e. non 0
subs = [rsubs,csubs];
subs = subs(all(subs,2),:);
% Accumulate
accumarray(subs,1)
The result
ans =
0 0 1
2 0 0
1 1 0
If you have the Statistics Toolbox, you could use crosstab directly, but then you would need to select the rows and columns of interest:
crosstab(CityPairs(:,1),CityPairs(:,2))
ans =
0 0 0 0 1
2 0 1 0 0
0 0 0 1 0
1 0 0 0 0
1 1 0 0 0

Answer provided by Cedric Wannaz on MathWorks Forum
It is not trivial to "vectorize" this setup, as there are operations that require some look up table, and there is accumulation (unless you find a trick). This is likely to make the approach without FOR loops more complicated (code-wise) than the basic, loop-based approach. So let's start with the trick first ;-) ..
A = double( bsxfun(#eq, CityPairs(:,1), Offices.') ) ;
B = double( bsxfun(#eq, CityPairs(:,2), Offices.') ) ;
A.' * B
Alternate approach: http://www.mathworks.com/matlabcentral/answers/91294-vectorization-of-2-for-loops-in-matlab

Related

Combinations of zeros and ones of vector size n [duplicate]

I have an algorith that the number of possibles combinations of 0 and 1, can reach the number 2^39. Let's say i have n=2 situations, or n1=2^2=4 combinations of 0 and 1: 00,01,10,11.From that i can create an array a=zeros(n,n1) and fill the columns with the possible combinations? That means first column has 00,second 01,third 10,last 11.I want this to be dynamic that means that n can be 1,2,3...,39, show the array will be a=zeros(n,2^n).Thanks for any response!
Just for general understanding: why do you think you need an array of all combinations of all integers from 0 to 2³⁹? That array would consume 39×2³⁹/1000⁴ ≈ 21TB of RAM...last time I checked, only the world's most advanced supercomputers have such resources, and most people working with those machines consider generating arrays like this quite wasteful...
Anyway, for completeness, for any N, this is the simplest solution:
P = dec2bin(0:(2^N)-1)-'0'
But, a little piece of advice: dec2bin outputs character arrays. If you want numerical arrays, you can subtract the character '0', however, that gives you an array of doubles according to the rules of MATLAB:
>> P = dec2bin(0:(2^3)-1)-'0';
>> whos P
Name Size Bytes Class Attributes
P 8x3 192 double
If you want to minimize your memory consumption, generate a logical array instead:
>> P = dec2bin(0:(2^3)-1)=='1';
>> whos P
Name Size Bytes Class Attributes
P 8x3 24 logical
If you want to also speed up the execution, use the standard algorithm directly:
%// if you like cryptic one-liners
B1 = rem(floor((0:pow2(N)-1).' * pow2(1-N:0)), 2) == 1;
%// If you like readability
B = false(N,pow2(N));
V = 0:pow2(N)-1;
for ii = 1:N
B(ii,:) = rem(V,2)==1;
V = (V-B(ii,:))/2;
end
That last one (the loop) is fastest of all solutions for any N (at least on R2010b and R2013a), and it has the smallest peak memory (only 1/Nth of the cryptic one-liner).
So I'd go for that one :)
But, that's just me.
Using ndgrid with a comma-separated list as output (see also here):
[c{1:N}] = ndgrid(logical([0 1]));
c = cat(N+1,c{N:-1:1});
c = reshape(c,[],N);
Example: N=4 gives
c =
0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1

Count length and frequency of island of consecutive numbers

I have a sequence of ones and zeros and I would like to count how often islands of consecutive ones appear.
Given:
S = [1 1 0 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 1 1 0 1]
By counting the islands of consecutive ones I mean this:
R = [4 3 1]
…because there are four single ones, three double ones and a single triplet of ones.
So that when multiplied by the length of the islands [1 2 3].
[4 3 1] * [1 2 3]’ = 13
Which corresponds to sum(S), because there are thirteen ones.
I hope to vectorize the solution rather than loop something.
I came up with something like:
R = histcounts(diff( [0 (find( ~ (S > 0) ) ) numel(S)+1] ))
But the result does not make much sense. It counts too many triplets.
All pieces of code I find on the internet revolve around diff([0 something numel(S)]) but the questions are always slightly different and don’t really help me
Thankful for any advice!
The following should do it. Hopefully the comments are clear.
S = [1 1 0 0 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 1 1 0 1];
% use diff to find the rising and falling edges, padding the start and end with 0
edges = diff([0,S,0]);
% get a list of the rising edges
rising = find(edges==1);
% and falling edges
falling = find(edges==-1);
% and thereby get the lengths of all the runs
SRuns = falling - rising;
% The longest run
maxRun = max(SRuns);
% Finally make a histogram, putting the bin centres
R = hist(SRuns,1:maxRun);
You could also obtain the same result with:
x = find(S==1)-(1:sum(S)) %give a specific value to each group of 1
h = histc(x,x) %compute the length of each group, you can also use histc(x,unique(x))
r = histc(h,1:max(h)) %count the occurence of each length
Result:
r =
4,3,1

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

Effiicient ways to count a streak of consecutive integers in 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.

Count the occurrence of consecutive 1s in 0-1 data in MATLAB

I have a set of 1s and 0s. How do I count the maximum number of consecutive 1s?
(For example, x = [ 1 1 0 0 1 1 0 0 0 1 0 0 1 1 1 ....]). Here the answer is 3 because the maximum number of times 1 occurs consecutively is 3.
I was looking at some search and count inbuilt function, however I have not been successful.
Try this:
max( diff( [0 (find( ~ (x > 0) ) ) numel(x) + 1] ) - 1)
Here's a solution but it might be overkill:
L = bwlabel(x);
L(L==0) = [];
[~,n] = mode(L)
Sometimes it's better to write your own function with loops ; most of the time it's cleaner and faster.
Another possibility:
x = randi([0 1], [1 100]); %# random 0/1 vector
d = diff([0 x 0]);
maxOccurence = max( find(d<0)-find(d>0) )
which is inspired by an answer to a somewhat similar question...
Cody Problem 15 is find maximum consecutive ones in a 'binary' string.
This works quite nicely. As you can tell I'm quite pleased with it!
Cody size 19
max(cellfun(#numel,strsplit(x,'0')));