How to impose a condition creating a matrix in matlab - matlab

I want to create a matrix with all the possible combinations of 10 numbers between 0 and 100, with intervals of 5, that its sum be equal to 100. I mean something like this:
(0 0 0 0 0 0 0 0 0 10 90; 10 10 10 10 10 10 10 10 20 0;...)
I use "allcomb.m" to create something like all the possible numbers that are between 0 and 100, with intervals of 5. However, this matrix is so big, and that implies that Matlab doesn't create it. I was thinking that, if I have that matrix, I could reduce it using a condition but this is impossible because I never get the matrix.
So, the question is how I can modify the allcomb's code with the condition in the same code or maybe, and better, another way to create the matrix that I purpose.

Be warned that even the result matrix is very large - to be precise, it has 10,015,005 rows and ten columns, and (if stored as a double) takes up about 1GB of space. On my machine it takes about ten minutes to compute. Nevertheless, it is computable, and the following function computes it.
function w = allconstrainedcombinations(n,k)
if n == 1
w = k;
else
t = nchoosek(n+k-1,k); # Total number of rows
w = zeros(t,n); # Pre-allocate
r = 1; # Current row
for v = 0:k
u = allconstrainedcombinations(n-1,k-v);
m = size(u,1);
w(r:r+m-1,1) = v;
w(r:r+m-1,2:end) = u;
r = r + m;
end
end
end
To get the result you want, you should call
>> x = allconstrainedcombinations(10,20) * 5;
Here's the result for a small example:
>> allconstrainedcombinations(3,2)
ans =
0 0 2
0 1 1
0 2 0
1 0 1
1 1 0
2 0 0

Related

Generating a Matrix from a given vector

Im trying to generate a specific type o matrix in Matlab.
I need to modify it for specific types of data with the following rules:
First I have to choose a grade g (max 6 let's say) then I have to choose the number of elements per row n (max 18) ;
These numbers are the powers of a specific polynomial of grade g ;
The sum per row in the matrix is not allowed to be bigger than the chosen g grade ;
The biggest element per row is the chosen g.
For g = 2, n = 2 the matrix will look like this:
A = [0 0;
0 1;
1 0;
0 2;
2 0;
1 1]
For g = 2, n = 3 the matrix will look like this:
A = [0 0 0;
0 0 1;
0 0 2;
0 1 0;
0 2 0;
1 0 0;
2 0 0;
0 1 1;
1 0 1;
1 1 0]
How can I generate all possible combinations of an array elements?
Ex : given v = [0 1 2];
Result = [0 0 0;
0 0 1;
0 1 0;
0 1 1;
1 0 0;
1 0 1;
1 1 0;
1 1 1;
0 0 2;
0 2 0;
2 0 0;
2 0 1;
2 1 1;
2 1 2;
...]
and so on...
I've tried this with perms, nchoosek, repelem, repmat, for-loops, unique, matrix concatenations, everything but I couldn't be able to find and algorithm.
You can first generate all n permutations of [0..g] with repetition and rearrangement, then select allowed combinations:
% all n permutations of [0..g] (with repetition and rearrangement)
powers = unique(nchoosek(repmat(0:g, 1, n), n), 'row');
% allowed set of powers
powers = powers(sum(powers, 2) <= g, :);
As I already said in comments, the above code is extremely time and memory inefficient. For example when I run it for g=6 and n=9, MATLAB gives the following error:
Error using zeros Requested 23667689815x9 (1587.0GB) array exceeds
maximum array size preference.
...
To reduce memory consumption, you can do the following:
% all n permutations of [0..g] (with repetition)
gPerms = nmultichoosek(0:g, n);
% allowed set of powers
allowed = gPerms(sum(gPerms, 2) <= g, :);
% all permutations of [1..n] (no repetition)
nPerms = perms(1:n);
% all n permutations of [0..g] (with repetition and rearrangement)
arranges = arrayfun(#(i) allowed(:, nPerms(i, :)), ...
1:size(nPerms, 1), 'UniformOutput', false)';
powers = cell2mat(arranges);
% unique set of permutations
powers = unique(powers, 'rows');
In the above code, first n permutations with repetition of g is generated using #knedlsepp's implementation. The filtered to keep only combinations that their sums is less than or equal to g. In next step all rearrangements of these combinations are calculated. It still takes more than 13 seconds here to find 5005 combinations for the g=6 and n=9 case.

Merge two matrices in matlab

I have two matrices. One is of size 1,000,000 x 9 and the other is 500,000 x 9.
The columns have the same meaning and the first 7 columns have the function of a key. Correspondingly, the last two columns have data character. There are many overlapping key values in both of the matrices and I would like to have a big matrix to compare the values. This big matrix should be of dimension 1,000,000 x 11.
For example:
A = [0 0 0 0 0 0 0 10 20; 0 0 0 0 0 0 1 30 40];
B = [0 0 0 0 0 0 0 50 60];
A merged matrix would look like this:
C = [0 0 0 0 0 0 0 10 20 50 60; 0 0 0 0 0 0 1 30 40 0 0];
As you can see, the first row of C has columns 8, 9 from matrix A and columns 10,11 from matrix B. The second row uses the columns 8, 9 from matrix A and 0,0 for the last to columns because there is no corresponding entry in matrix B.
I have accomplished this task theoretically, but it is very, very slow. I use loops a lot. In any other programming language, I would sort both tables, would iterate both of the tables in one big loop keeping two pointers.
Is there a more efficient algorithm available in Matlab using vectorization or at least a sufficiently efficient one that is idiomatic/short?
(Additional note: My largest issue seems to be the search function: Given my matrix, I would like to throw in one column vector 7x1, let's name it key to find the corresponding row. Right now, I use bsxfun for that:
targetRow = data( min(bsxfun(#eq, data(:, 1:7), key), [], 2) == 1, :);
I use min because the result of bsxfun is a vector with 7 match flags and I obviously want all of them to be true. It seems to me that this could be bottleneck of a Matlab algorithm)
Maybe with ismember and some indexing:
% locates in B the last ocurrence of each key in A. idxA has logicals of
% those keys found, and idxB tells us where in B.
[idxA, idxB] = ismember(A(:,1:7), B(:,1:7),'rows');
C = [ A zeros(size(A, 1), 2) ];
C(idxA, 10:11) = B(idxB(idxA), 8:9); % idxB(idxA) are the idxB != 0
I think this does what you want, only tested with your simple example.
% Initial matrices
A = [0 0 0 0 0 0 0 10 20;
0 0 0 0 0 0 1 30 40];
B = [0 0 0 0 0 0 0 50 60];
% Stack matrices with common key columns, 8&9 or 10&11 for data columns
C = [[A, zeros(size(A,1),2)]; [B(:,1:7), zeros(size(B,1),2), B(:,8:9)]];
% Sort C so that matching key rows will be consecutive
C = sortrows(C,1:7);
% Loop through rows
curRow = 1;
lastRow = size(C,1) - 1;
while curRow < lastRow
if all(C(curRow,1:7) == C(curRow+1,1:7))
% If first 7 cols of 2 rows match, take max values (override 0s)
% It may be safer to initialise the 0 columns to NaNs, as max will
% choose a numeric value over NaN, and it allows your data to be
% negative values.
C(curRow,8:11) = max(C(curRow:curRow+1, 8:11));
% Remove merged row
C(curRow+1,:) = [];
% Decrease size counter for matrix
lastRow = lastRow - 1;
else
% Increase row counter
curRow = curRow + 1;
end
end
Answer:
C = [0 0 0 0 0 0 0 10 20 50 60
0 0 0 0 0 0 1 30 40 0 0]

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

Create the following matrix by typing one command. Do not type all individual elements explicitly

I want to create the following matrix in MATLAB:
M= [ 0 0 1 10 20
0 0 3 8 26
0 0 5 6 32
0 0 0 0 0]
but I don't want to input all elements manually.
I tried M (1:3,3:5)=[x;y;z]
where
x is the linspace of 1 to 5
y is the linspace of 10 to 6
z is the linspace of 20 to 32
but it doesn't work (the last row of zeros is missing). How can I create M in a smart way?
I'm assuming that this is some programming assignment, because otherwise I don't see a reason why this needs to be done in a single line. I'm also assuming that concatenating a vector of zeros is unwanted. Having said that here are several suggestions:
Suppose that your vectors are defined like so:
x = 1:2:5;
y = 10:-2:6;
z = 20:6:32;
The "cleanest" way (under the assumption of no-zero-vector-concatenation) is probably:
M = subsasgn(zeros(4,5),substruct('()',{1:3,3:5}),[x',y',z']);
Alternatively, if using external functions, you can use the insertrows submission on FEX:
M = insertrows(insertrows([(x)',(y)',(z)'],0,4)',0,[0,0])';
With two commands, and assuming that M doesn't exist, you can do:
M(2:4,3:5)=([fliplr(x)',fliplr(y)',fliplr(z)']);
M = flipud(M);
If you are looking for one-liner that requires no other input(s) and creates the linspace values internally, here's one -
M(1:4,3:5)=[bsxfun(#plus,[1 10 20],bsxfun(#times,[2 -2 6],[0:2]'));zeros(1,3)]
Output -
M =
0 0 1 10 20
0 0 3 8 26
0 0 5 6 32
0 0 0 0 0
Strange question, but here you go, in one command. This assumes M is previously non-existent, and that x, y and z are defined as x = 1:2:5; y = 10:-2:6; z = 20:6:36;:
M(1:4,3:5) = [[x;y;z].'; zeros(1,3)];
You can of course avoid x, y and z by defining their values on the fly:
M(1:4,3:5) = [[1:2:5; 10:-2:6; 20:6:36].'; zeros(1,3)];

Randomly select 30% elements in a matrix using MATLAb

I need to do the following for each of elements of a matrix of size mXn:
1. flip a coin with a 0.3 probability of success.
2. if its a success set the element to zero.
3. else move to the next element.
I used the following code, but it does not give any output and produces NaN, C is the matrix of size mXn:
index = (rand(size(C)<=0.3));
one_index = find(index ==1);
C(one_index) = 0;
The problem is this statement
index = (rand(size(C)<=0.3));
You've messed up the parentheses so you're trying to compare if size(C) <= 0.3. This returns [0 0], causing rand to create an empty matrix.
Also, the call to find is unnecessary.
C = magic(4);
index = rand(size(C)) <= 0.3;
C(index) = 0
C =
16 2 3 13
0 11 10 8
9 7 6 0
4 0 15 1