matlab - creating a single dimensional array from two dimensional parts - matlab

hi i have the following situation
h = [0,1,1,1;
0,0,0,0;
1,1,1,1];
i'll check incoming values which can range between 0 and rowsize of h, i.e. in this case 2,. so my options are 0,1,2.
now i want to create a single dimensional array (let's name it j) as follows
whenever the incoming value is 0
j = [0,1,1,1]
next time if the incoming value is 1
then j = [0,1,1,1,0,0,0,0]
and so on... how is it possible to achieve this in matlab? thanks!

Matlab, as you know, indexes from 1 so you will need to add 1 to the index 0,1,2 to get the row identifier for h. So if the input is 'index'
j = h(index+1,:)
Then, for the next index
j = [j h(index+1,:)]
and so on.

Try this (with x as your vector of incoming values):
j = reshape(h(x+1,:).',1,[]);
The above uses x+1 as an index to select copies of the rows, then transposes and reshapes the result into a single row vector. Here's a test:
>> h = [0 1 1 1; 0 0 0 0; 1 1 1 1];
>> x = [0 0 0];
>> j = reshape(h(x+1,:).',1,[])
j =
0 1 1 1 0 1 1 1 0 1 1 1

If the incoming value is x, you could do something like:
g = h.'
j = g(1:(x+1)*size(h,2))

Related

How can I randomize two binary vectors that are similar while making sure all possible combinations are respected?

Been trying to solve this simple problem for a while but just couldn't find the solution for the life of me...
I'm programming an experiment in PsychToolbox but I'll spare you the details, basically I have two vectors A and B of equal size with the same number of ones and zeroes:
A = [0 0 1 1]
B = [0 0 1 1]
Both vectors A and B must be randomized independently but in such a way that one combination of items between the two vectors is never repeated. That is, I must end up with this
A = [1 1 0 0]
B = [1 0 0 1]
or this:
A = [0 0 1 1]
B = [0 1 0 1]
but I should never end up with this:
A = [1 1 0 0]
B = [1 1 0 0]
or this
A = [0 1 0 1]
B = [0 1 0 1]
One way to determine this is to check the sum of items between the two vectors A+B, which should always contain only one 2 or only one 0:
A = [1 1 0 0]
B = [1 0 0 1]
A+B = 2 1 0 1
Been trying to make this a condition within a 'while' loop (e.g. so long as the number of zeroes in the vector obtained by A+B is superior to 1, keep randomizing A and B), but either it still produces repeated combination or it just never stops looping. I know this is a trivial problem but I just can't get my head around it somehow. Anyone care to help?
This is a simplified version of the script I got:
A = [1 1 0 0];
B = A;
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
while nnz(~(A+B)) > 1
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
end
Still, I end up with repeated combinations.
% If you are only looking for an answer to this scenario the easiest way is
% as follows:
A = [0 0 1 1];
B = [0 0 1 1];
nn = length(A);
keepset = [0 0 1 1;0 1 0 1];
keepset = keepset(:,randperm(nn))
% If you want a more general solution for arbitrary A & B (for example)
A = [0 0 0 1 1 1 2 2 2];
B = [0 0 0 1 1 1 2 2 2];
nn = length(A);
Ai = A(randperm(nn));
Bi = B(randperm(nn));
% initialize keepset with the first combination of A & B
keepset = [Ai(1);Bi(1)];
loopcnt = 0;
while (size(keepset,2) < nn)
% randomize the elements in A and B independently
Ai = A(randperm(nn));
Bi = B(randperm(nn));
% test each combination of Ai and Bi to see if it is already in the
% keepset
for ii = 1:nn
tstcombo = [Ai(ii);Bi(ii)];
matchtest = bsxfun(#eq,tstcombo,keepset);
matchind = find((matchtest(1,:) & matchtest(2,:)));
if isempty(matchind)
keepset = [keepset tstcombo];
end
end
loopcnt = loopcnt + 1;
if loopcnt > 1000
disp('Execution halted after 1000 attempts')
break
elseif (size(keepset,2) >= nn)
disp(sprintf('Completed in %0.f iterations',loopcnt))
end
end
keepset
It's much more efficient to permute the combinations randomly than shuffling the arrays independently and handling the inevitable matching A/B elements.
There are lots of ways to generate all possible pairs, see
How to generate all pairs from two vectors in MATLAB using vectorised code?
For this example I'll use
allCombs = combvec([0,1],[0,1]);
% = [ 0 1 0 1
% 0 0 1 1 ]
Now you just want to select some amount of unique (non-repeating) columns from this array in a random order. In all of your examples you select all 4 columns. The randperm function is perfect for this, from the docs:
p = randperm(n,k) returns a row vector containing k unique integers selected randomly from 1 to n inclusive.
n = size(allCombs,2); % number of combinations (or columns) to choose from
k = 4; % number of columns to choose for output
AB = allCombs( :, randperm(n,k) ); % random selection of pairs
If you need this split into two variables then you have
A = AB(1,:);
B = AB(2,:);
Here's a possible solution:
A = [0 0 1 1];
B = [0 0 1 1];
% Randomize A and B independently
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
% Keep randomizing A and B until the condition is met
while sum(A+B) ~= 1 && sum(A+B) ~= length(A)
ARand = randperm(length(A));
A = A(ARand);
BRand = randperm(length(B));
B = B(BRand);
end
This solution checks if the sum of the elements in A+B is either 1 or the length of A, which indicates that only one element in A+B is either a 0 or a 2, respectively. If either of these conditions is not met, the vectors A and B are randomized again.

Need help in creating a matrix with exactly one 1 in each row/column at different locations

This code generates a matrix having 1 in each row and this 1 may be on same locations in rows.
I want for each row the location of 1 must be different i.e 1 must not overlap in columns.
Suppose you want the result matrix to be of size [m, n].
First, randomly select , for each column, the index of the 1
idx = randi(m, 1, n);
Second, allocate an all-zero matrix of size [m,n]
res = zeros(m,n);
Finally, set the corresponding entries to one:
res( sub2ind([m,n], idx, 1:n) ) = 1;
An example result for [3,4] matrix:
res =
0 0 1 0
0 0 0 0
1 1 0 1

How can I vectorise this loop in MATLAB

I have a loop that iterates over a matrix and sets all rows and columns with only one non-zero element to all zeroes.
so for example, it will transform this matrix:
A = [ 1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1 ]
to the matrix:
A' = [ 1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1 ]
row/column 2 of A only has 1 non zero element in it, so every element in row/column 2 is set to 0 in A'
(it is assumed that the matrices will always be diagonally symmetrical)
here is my non-vectorised code:
for ii = 1:length(A)
if nnz(A(ii,:)) == 1
A(ii,:) = 0;
A(:,ii) = 0;
end
end
Is there a more efficient way of writing this code in MATLAB?
EDIT:
I have been asked in the comments for some clarification, so I will oblige.
The purpose of this code is to remove edges from a graph that lead to a vertex of degree 1.
if A is the adjacency matrix representing a undirected graph G, then a row or column of that matrix which only has one non-zero element indicates that row/column represents a vertex of degree one, as it only has one edge incident to it.
My objective is to remove such edges from the graph, as these vertices will never be visited in a solution to the problem I am trying to solve, and reducing the graph will also reduce the size of the input to my search algorithm.
#TimeString, i understand that in the example you gave, recursively applying the algorithm to your matrix will result in a zero matrix, however the matrices that I am applying it to represent large, connected graphs, so there will never be a case like that. In response to your question as to why I only check for how many elements in a row, but the clear both columns and rows; this is because the matrix is always diagonally symmetrical, so i know that if something is true for a row, so it will be for the corresponding column..
so, just to clarify using another example:
I want to turn this graph G:
represented by matrix:
A = [ 0 1 1 0
1 0 1 0
1 1 0 1
0 0 1 0 ]
to this graph G':
represented by this matrix:
A' = [ 0 1 1 0
1 0 1 0
1 1 0 0
0 0 0 0 ]
(i realise that this matrix should actually be a 3x3 matrix because point D has been removed, but i already know how to shrink the matrix in this instance, my question is about efficiently setting columns/rows with only 1 non-zero element all to 0)
i hope that is a good enough clarification..
Not sure if it's really faster (depends on Matlab's JIT) but you can try the following:
To find out which columns (equivalently, rows, since the matrix is symmetric) have more than one non zero element use:
sum(A ~= 0) > 1
The ~= 0 is probably not needed in your case since the matrix consists of 1/0 elements only (graph edges if I understand correctly).
Transform the above into a diagonal matrix in order to eliminate unwanted columns:
D = diag(sum(A~=0) > 1)
And multiply with A from left to zero rows and from right to zero columns:
res = D * A * D
Thanks to nimrodm's suggestion of using sum(A ~= 0) instead of nnz, i managed to find a better solution than my original one
to clear the rows with one element i use:
A(sum(A ~= 0) == 1,:) = 0;
and then to clear columns with one element:
A(:,sum(A ~= 0) == 1) = 0;
for those of you who are interested, i did a 'tic-toc' comparison on a 1000 x 1000 matrix:
% establish matrix
A = magic(1000);
rem_rows = [200,555,950];
A(rem_rows,:) = 0;
A(:,rem_rows) = 0;
% insert single element into empty rows/columns
A(rem_rows,500) = 5;
A(500,rem_rows) = 5;
% testing original version
A_temp = A;
for test = 1
tic
for ii = 1:length(A_temp)
if nnz(A_temp(ii,:)) == 1
A_temp(ii,:) = 0;
A_temp(:,ii) = 0;
end
end
toc
end
Elapsed time is 0.041104 seconds.
% testing new version
A_temp = A;
for test = 1
tic
A_temp(sum(A_temp ~= 0) == 1,:) = 0;
A_temp(:,sum(A_temp ~= 0) == 1) = 0;
toc
end
Elapsed time is 0.010378 seconds
% testing matrix operations based solution suggested by nimrodm
A_temp = A;
for test = 1
tic
B = diag(sum(A_temp ~= 0) > 1);
res = B * A_temp * B;
toc
end
Elapsed time is 0.258799 seconds
so it appears that the single line version that I came up with, inspired by nimrodm's suggestion, is the fastest
thanks for all your help!
Bsxfuning it -
A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
Sample run -
>> A
A =
1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1
>> A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
A =
1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1

Want to Create A Combination of Non-Decreasing Elements with Condition

I want to create a combination of non-decreasing elements. The combination will be like,
If i=10 and w=5, then the elements of the combination can be any value from 1 to w and the sum will be equal to i.
Possible combinations are like,
1+1+1+1+1+5
1+1+1+1+1+1+4
1+1+1+1+1+1+1+3
1+1+1+1+1+1+1+1+1+1
......
But 1+1+1+7 is not a desired combination because 7 is greater than w
How to get the combinations using MatLab? I need to get the combinations for higher values of i and w like may be i=20 and w=8.
Thank You
You can do it with a recursive function. The following code contains a wrapper function and the recursive function which does the actual work:
function result = partitions(s,M)
%// s: desired sum; M: maximum value
result = partitions_rec(s,1,M,s);
end
function mat = partitions_rec(s,m,M,n)
%// s: desired sum, m: minimum value; M: maximum value; n: number of entries
M = min(M,s);
if s==0
mat = zeros(1,n);
else
mat = [];
for ii = m:M;
aux = partitions_rec(s-ii,ii,M,n-1);
if size(aux,1)
mat = [ mat; ii*ones(size(aux,1),1) aux ];
end
end
end
end
Example:
>> result = partitions(5,3)
ans =
1 1 1 1 1
1 1 1 2 0
1 1 3 0 0
1 2 2 0 0
2 3 0 0 0
A 0 indicates no number. If you want to remove the zeros, you need to put the result in the form of a cell array, where each cell is a vector with nonzero values:
result_cell = arrayfun(#(ii) result(ii, logical(result(ii,:))), 1:size(result,1), 'uniformoutput', 0);
In the example, this would give
>> result_cell{:}
ans =
1 1 1 1 1
ans =
1 1 1 2
ans =
1 1 3
ans =
1 2 2
ans =
2 3

MATLAB: how to locate the nth appeared number in a loop?

For example,
for i = 1:10
x = i + 2
s(1:i) = (x > 4);
result s = [0 0 0 1 1 1 1 1 1 1]'
how to located the column and row of the first "1" appeared in s?
Look for a script.
Thanks!!
For a vector like s it's easy
find(s==1,1)
should do what you want.