Matlab: construct a binary matrix satisfying horizontal and vertical constraints - matlab

I want to initiate a mxn binary matrix. The summation of each column of the matrix equals to a given value s(j), for j=1..n. In addition, the summation of each row of the matrix should be within the range of given bounds: lhs is dl(i) and rhs is du(i), for i=1..m.
Now I can only randomly generate binary columns, in each of which the sum of ones equals to s(j) of that column, such as the following codes.
xij = zeros(m,n);
for j=1:n
randRows=randperm(m); %a row vector containing a random permutation of the integers from 1 to m inclusive.
rowsWithOne=randRows(1:sj(j)); %row indices having 1, and sum up to sj
xij(rowsWithOne,j)=1;
end
However, xij usually doesn't satisfy the horizontal constraints. I was thinking I should create a matrix first meets the row constraints lhs (lower bound) dl(i), then, column constraint s(j), and finally fill the offsets to meet rhs du(i), but I don't know how to implement it in Matlab. Is there any ideas to create xij?
Thanks in advance.

There are a couple things to first take into account, mainly if the problem can even be solved with the given constraints. First, you must check that the sum of s(j), sum(s) is greater than the sum of the lower bound constraints, sum(dl), and also less than the sum of the upper bound constraints, sum(du).
A simple if statement should be able to check this. After this has been done, the following code should be one solution to the problem, but given the nature of the problem, there will probably not be a unique solution.
%initialize counters
x = 1;
y = 1;
%make sure the lower bounds are first satisfied
for k = 1:nrows
while (dl(k) > 0)
if (s(y) > 0)
xij(k,y) = 1;
dl(k) = dl(k)-1;
du(k) = du(k)-1;
s(y) = s(y)-1;
end
y = y+1;
end
y = 1;
end
%make sure the columns all add to their specified values
for k = 1:ncols
while (s(k) > 0)
if (xij(x,k) == 0 && du(x) > 0)
xij(x,k) = 1;
du(x) = du(x)-1;
s(k) = s(k)-1;
end
x = x+1;
end
x = 1;
end
The first for loop adds 1s along the rows such that each row satisfies the minimum constraint. The next for loop adds 1s along the columns such that each column adds up to the desired value. The if statement in the first for loop ensures that a 1 isn't added if that column has already reached its desired value, and the if statement in the second for loop ensures that a 1 isn't added to a row that would put it over the maximum value of the row.
In order to keep track of the amount of ones, the du is lowered by 1 every time a 1 is added to the corresponding row, and likewise the desired value s(j) is subtracted by 1 every time a 1 is added to that column of the xij matrix.

%Create an initial xij matrix, each column of which consists of a random binary xij sequence, but sum of ones in each column equals to sj
Init_xij = zeros(nShift,nCombo);
for j=1:nCombo
randRows=randperm(nShift); %a row vector containing a random permutation of the integers from 1 to nShift inclusive.
rowsWithOne=randRows(1:sj(j)); %row indices having 1, and sum up to sj
Init_xij(rowsWithOne,j)=1;
end
%Adjust the initial xij matrix to make it feasible, satisfying horizontal
%LHS (dli) and RHS (dui) constraints
Init_xij_Feasible=Init_xij;
k=1;
while k
RowSum=sum(Init_xij_Feasible,2); %create a column vector containing the sum of each row
CheckLB=lt(RowSum,dli); %if RowSum <dli, true
CheckUB=gt(RowSum,dui); %if RowSum >dui, true
if ~any(CheckLB)&&~any(CheckUB) %if any element in CheckLB and CheckUB is zero
break,
else
[~,RowIdxMin]=min(RowSum);
[~,RowIdxMax]=max(RowSum);
ColIdx=find(Init_xij_Feasible(RowIdxMax,:) & ~Init_xij_Feasible(RowIdxMin,:),1); % returns the first 1 column index corresponding to the nonzero elements in row RowIdxMax and zero elements in row RowIdxMin.
%swap the min and max elements
[Init_xij_Feasible(RowIdxMin,ColIdx),Init_xij_Feasible(RowIdxMax,ColIdx)]=deal(Init_xij_Feasible(RowIdxMax,ColIdx),Init_xij_Feasible(RowIdxMin,ColIdx));
end
k=k+1;
end

Related

Is there a way to use "for loop" in creating a matrix

I'm a total beginner when it comes to MATLAB, so I have a question for this. How am I supposed to write this code out and if by any chance, can someone be kind enough to write out the code because I've been struggling with this. Use loops to create 3 x 5 matrix in which the value of each element is half of its row number plus three times its column number. for instance, the value of element (2,5) is: 1/22+35
The nested loops control the indexing of the matrix. The outer for-loop traverses through the rows of the matrix and the inner for-loop traverses through the columns of the matrix.
The second part of this question requires using a combination of the looping/scanning variables Row and Column to set the value of the matrix:
Matrix Value = (Row ÷ 2) + (3 × Column)
Number_Of_Rows = 3;
Number_Of_Columns = 5;
Matrix = zeros(Number_Of_Rows,Number_Of_Columns);
%Running through the array indices using two loops%
for Row = 1: Number_Of_Rows
for Column = 1: Number_Of_Columns
%Evaluating the value based on the current row and column index%
Matrix(Row,Column) = (Row/2) + (3*Column);
end
end
Matrix
Result:
Looping Methodology:
Variable Matrix Opened In Workspace:
Here's an intuitive way to do this:
% Initialize row num and column num
row = 3;
column = 5;
% H is the matrix of desire
% Initialize it as a 3*5 zero matrix
H = zeros(3,5);
% Outer loop, over column index
% Remember Matlab's index start with 1 not 0
for c = 1:column
% Inner Loop, over row index
for r = 1:row
% The algorithm of each element in the matrix
H(r,c) = 0.5*r+3*c;
end
end

Mean value of each column of a matrix

I have a 64 X 64 matrix that I need to find the column-wise mean values for.
However, instead of dividing by the total number of elements in each column (i.e. 64), I need to divide by the total number of non-zeros in the matrix.
I managed to get it to work for a single column as shown below. For reference, the function that generates my matrix is titled fmu2(i,j).
q = 0;
for i = 1:64
if fmu2(i,1) ~= 0;
q = q + 1;
end
end
for i = 1:64
mv = (1/q).*sum(fmu2(i,1));
end
This works for generating the "mean" value of the first column. However, I'm having trouble looping this procedure so that I will get the mean for each column. I tried doing a nested for loop, but it just calculated the mean for the entire 64 X 64 matrix instead of one column at a time. Here's what I tried:
q = 0;
for i = 1:64
for j = 1:64
if fmu2(i,j) ~= 0;
q = q +1;
end
end
end
for i = 1:64
for j = 1:64
mv = (1/q).*sum(fmu2(i,j));
end
end
Like I said, this just gave me one value for the entire matrix instead of 64 individual "means" for each column. Any help would be appreciated.
For one thing, do not call the function that generates your matrix in each iteration of a loop. This is extremely inefficient and will cause major problems if your function is complex enough to have side effects. Store the return value in a variable once, and refer to that variable from then on.
Secondly, you do not need any loops here at all. The total number of nonzeros is given by the nnz function (short for number of non-zeros). The sum function accepts an optional dimension argument, so you can just tell it to sum along the columns instead of along the rows or the whole matrix.
m = fmu2(i,1)
averages = sum(m, 1) / nnz(m)
averages will be a 64-element array with an average for each column, since sum(m, 1) is a 64 element sum along each column and nnz(m) is a scalar.
One of the great things about MATLAB is that it provides vectorized implementations of just about everything. If you do it right, you should almost never have to use an explicit loop to do any mathematical operations at all.
If you want the column-wise mean of non-zero elements you can do the following
m = randi([0,5], 5, 5); % some data
avg = sum(m,1) ./ sum(m~=0,1);
This is a column-wise sum of values, divided by the column-wise number of elements not equal to 0. The result is a row vector where each element is the average of the corresponding column in m.
Note this is very flexible, you could use any condition in place of ~=0.

MATLAB - Check matrix to see if two consecutive numbers = 0

I have a matrix being created in MATLAB but need to check if any two consecutive numbers (by row) = 0 and if it does output with a yes or no without showing the answers. Ive put my code below my final loop is returning errors and im not too sure how to go about this.
%%Input positive integer
n=input('Give a positive integer greater than 1: ');
%%Error for below 1
if n<=1
error('The value given is less than or equal to 1')
end
%%loop for vector v
for i=1:n
v(i)=2^i*3;
end
%display vector
v
A=randi([-5,5],n,n);
A
x = 1;
consecutive = false;
for i = 1:25
if (A(x) + A(x+1) = 0);
consecutive = true;
end
end
There's a lot wrong with the code of your final loop:
You set x to 1 and use it as an index into A, but never change it from 1.
As Amit points out, you are using = (used for assignment) when you should be using == (the equality operator).
As Gondrian points out, you are testing if a sum is equal to zero, but from your description it sounds like you should be testing if each value is zero.
Your loop iterates 25 times (i = 1:25), but it's not clear why, since your matrix A is of size n-by-n.
Instead of using a for loop, it's possible to do this with indexing instead. Since you are checking for consecutive zeroes by row, this is what it would look like:
zeroPairs = (A(:, 1:(n-1)) == 0) & (A(:, 2:n) == 0);
consecutive = any(zeroPairs(:));
The term A(:, 1:(n-1)) gets all of the "left" values in each pairwise comparison and the term A(:, 2:n) gets all of the "right" value. These are compared to 0, then combined. This creates an n-by-(n-1) matrix zeroPairs where a value of true indicates where a pair of consecutive zeroes occurs. This matrix is reshaped into a column vector and any is used to check if a value of true is present anywhere.
You will need to change your if block as follows.
if (A(x) + A(x+1) == 0);
consecutive = true;
end
Notice the == instead of just =. The first one is for comparison and the second one is for assignment. This will get rid of the error that you are currently getting. There may be other issues in the algorithm of your code but I did not examine or try to fix that.
btw:
'if any two consecutive numbers (by row) = 0'
If I understand you right, you should try 'if A(x) == 0 && A(x+1) == 0';
because '(A(x) + A(x+1) == 0)' would be true for -5 and 5, as it equals to 0. But -5 and 5 are not two consecutive zeros.
(or even look at the 'diff' function. It will return a 0 if two following numbers are the same)
To show a vectorized, more Matlab-like approach:
v = 0; % sought value
C = 2; % desired number of consecutive values in a row
consecutive = nnz(conv2(double(A==v), ones(1,C))==C)>0;
This compares each entry of A with the value v, and then applies 2D convolution with a row vector of C ones. Any C horizontally-consecutive entries of A with value v will produce an entry equal to C in the convolution result. So we check if the number of such entries is positive.

delete elements from a matrix and calculate mean

I have an N-by-M-Matrix as input called GR wich consists of the following numbers: -3,0,2,4,7,10,12
And I have to return a vector. If M=1, then it should just return the input.
If M>1 It should remove the lowest number from the matrix and then calculate the mean of the remaining numbers.
However, if one of the numbers in the row is -3, it should return the value -3 in the output.
My thoughts of the problem:
Is it possible to make a for loop?
for i=1:length(GR(:,1))
If length(GR(1,:))==1
GR=GR
end
If length(GR(1,:))>1
x=min(GR(i,:))=[] % for removing the lowest number in the row
GR=sum(x)/length(x(i,:))
I just don't have any Idea of how to detect if any of the numbers in the row is -3 and then return that value instead of calculating the mean and when I tried to delete the lowest number in the matrix using x=min(GR(i,:)) matlab gave me this error massage 'Deletion requires an existing variable.'
I put in a break function. As soon as it detects a -3 value it breaks from the loop. Same goes for the other function.
Note that it is an i,j (M*N) matrix. So you might need to change your loop.
for i=1:length(GR(:,1))
if GR(i,1)==-3
GR=-3
break
end
If length(GR(1,:))==1
GR=GR
break
end
If length(GR(1,:))>1
x=min(GR(i,:))=[] % for removing the lowest number in the row
GR=sum(x)/length(x(i,:))
end
end
you can use Nan's, nanmean, any, and dim argument in these functions:
% generate random matrix
M = randi(3);
N = randi(3);
nums = [-3,0,2,4,7,10,12];
GR = reshape(randsample(nums,N*M,true),[N M]);
% computation:
% find if GR has only one column
if size(GR,2) == 1
res = GR;
else
% find indexes of rows with -3 in them
idxs3 = any(GR == -3,2);
% the (column) index of the min. value in each row
[~,minCol] = min(GR,[],2);
% convert [row,col] index pair into linear index
minInd = sub2ind(size(GR),1:size(GR,1),minCol');
% set minimum value in each row to nan - to ignore it on averaging
GR(minInd) = nan;
% averaging each rows (except for the Nans)
res = nanmean(GR,2);
% set each row with (-3) in it to (-3)
res(idxs3) = -3;
end
disp(res)

How to make cell array same length after padding zeros

This is data matrix having 2*159 cells
In the data matrix so many columns ( 2 vectors) having different length . I want to pad zeros which have the minimum length. Number of zeros should be
[max(length(Vector 1))-min(length(Vector 2))]
Now I want add zeros in the cell which have minimum length. and want to make length equal in each column if column have same length than no problem.
n = max(max(cellfun(#(x)size(x,2),data)))
cellfun(#(x)[x, zeros(1,n-numel(x))], data, 'uni', 0)
The above was finding one single max value to pad the cells to. I realise now that you actually want this max calculated on a per column basis:
n = max(cellfun(#(x)size(x,2),data))
cellfun(#(x,n)[x, zeros(1,n-numel(x))], data, repmat(num2cell(n),2,1), 'uni', 0)
At this point it might be more intuitive to just use a loop
for col = 1:size(data,2)
n = max(size(data{1,col},2),size(data{2,col},2));
for row = 1:2
x = data{row,col};
data{row,col} = [x, zeros(1,n-numel(x))];
end
end