Create a matrix with sequencial values padded with zeros (without loops) - matlab

I have two matrices aand b of 1 column and n rows.
These matrices represent min and max values.
From these I need to create a n by m matrix.
m is the max value of [a ; b]
The result must contain each value between a(:) and b(:), padded with zeros.
note: it's easy to do with a for loop, but I want to avoid loops.
Exemple :
Starting with these two matrices :
>> a = [3 ; 5 ; 2 ; 7 ; 4]
a =
3
5
2
7
4
>> b = [7 ; 7 ; 5 ; 8 ; 4]
b =
5
7
4
8
4
I want to end with this matrix :
result =
3 4 5 6 7 0 0 0
5 6 7 0 0 0 0 0
2 3 4 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
So far I have this :
>> result = zeros(size(a,1), max([a ; b]));
>> rows = [1:size(a,1)]
rows =
1 2 3 4 5
>> index = sub2ind(size(result), rows, b - a + 1)
>> result(index) = b
result =
0 0 0 0 7 0 0 0
0 0 7 0 0 0 0 0
0 0 0 5 0 0 0 0
0 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
>> result(:,1) = a
result =
3 0 0 0 7 0 0 0
5 0 7 0 0 0 0 0
2 0 0 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0

I'd solve this with a simple loop, which is also probably the fastest solution
a = [3 ; 5 ; 2 ; 7 ; 4]
b = [7 ; 7 ; 5 ; 8 ; 4]
nSteps = b-a+1;
nRows = size(a,1);
result = zeros(nRows, max([a ; b]));
for iRow = 1:nRows
result(iRow,1:nSteps(iRow)) = a(iRow):b(iRow);
end
result =
3 4 5 6 7 0 0 0
5 6 7 0 0 0 0 0
2 3 4 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0
In case you don't need readability, here's a non-loop solution:
result = ones(nRows, max([a ; b]));
result(:,1) = a;
result = cumsum(result,2);
result(bsxfun(#gt,result,b))=0
result =
3 4 5 6 7 0 0 0
5 6 7 0 0 0 0 0
2 3 4 5 0 0 0 0
7 8 0 0 0 0 0 0
4 0 0 0 0 0 0 0

Some problems aren't worth the effort of vectorising:
a = [3 ; 5 ; 2 ; 7 ; 4];
b = [7 ; 7 ; 5 ; 8 ; 4];
cols = max(max(a),max(b))
result = zeros(length(a),cols)
for i = 1:length(a)
A = a(i):b(i)
result(i,:) = padarray(A, [0 cols-length(A)], 0, 'post');
end

Related

Generating unitriangular matrices in Octave|MATLAB [duplicate]

Suppose I have an NxN matrix A, an index vector V consisting of a subset of the numbers 1:N, and a value K, and I want to do this:
for i = V
A(i,i) = K
end
Is there a way to do this in one statement w/ vectorization?
e.g. A(something) = K
The statement A(V,V) = K will not work, it assigns off-diagonal elements, and this is not what I want. e.g.:
>> A = zeros(5);
>> V = [1 3 4];
>> A(V,V) = 1
A =
1 0 1 1 0
0 0 0 0 0
1 0 1 1 0
1 0 1 1 0
0 0 0 0 0
I usually use EYE for that:
A = magic(4)
A(logical(eye(size(A)))) = 99
A =
99 2 3 13
5 99 10 8
9 7 99 12
4 14 15 99
Alternatively, you can just create the list of linear indices, since from one diagonal element to the next, it takes nRows+1 steps:
[nRows,nCols] = size(A);
A(1:(nRows+1):nRows*nCols) = 101
A =
101 2 3 13
5 101 10 8
9 7 101 12
4 14 15 101
If you only want to access a subset of diagonal elements, you need to create a list of diagonal indices:
subsetIdx = [1 3];
diagonalIdx = (subsetIdx-1) * (nRows + 1) + 1;
A(diagonalIdx) = 203
A =
203 2 3 13
5 101 10 8
9 7 203 12
4 14 15 101
Alternatively, you can create a logical index array using diag (works only for square arrays)
diagonalIdx = false(nRows,1);
diagonalIdx(subsetIdx) = true;
A(diag(diagonalIdx)) = -1
A =
-1 2 3 13
5 101 10 8
9 7 -1 12
4 14 15 101
>> tt = zeros(5,5)
tt =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
>> tt(1:6:end) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 3 0 0
0 0 0 3 0
0 0 0 0 3
and more general:
>> V=[1 2 5]; N=5;
>> tt = zeros(N,N);
>> tt((N+1)*(V-1)+1) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 3
This is based on the fact that matrices can be accessed as one-dimensional arrays (vectors), where the 2 indices (m,n) are replaced by a linear mapping m*N+n.
>> B=[0,4,4;4,0,4;4,4,0]
B =
0 4 4
4 0 4
4 4 0
>> v=[1,2,3]
v =
1 2 3
>> B(eye(size(B))==1)=v
%insert values from v to eye positions in B
B =
1 4 4
4 2 4
4 4 3
A = zeros(7,6);
V = [1 3 5];
[n m] = size(A);
diagIdx = 1:n+1:n*m;
A( diagIdx(V) ) = 1
A =
1 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 1 0
0 0 0 0 0 0
0 0 0 0 0 0
I'd use sub2ind and pass the diagonal indices as both x and y parameters:
A = zeros(4)
V=[2 4]
idx = sub2ind(size(A), V,V)
% idx = [6, 16]
A(idx) = 1
% A =
% 0 0 0 0
% 0 1 0 0
% 0 0 0 0
% 0 0 0 1
Suppose K is the value. The command
A=A-diag(K-diag(A))
may be a bit faster
>> A=randn(10000,10000);
>> tic;A(logical(eye(size(A))))=12;toc
Elapsed time is 0.517575 seconds.
>> tic;A=A+diag((99-diag(A)));toc
Elapsed time is 0.353408 seconds.
But it consumes more memory.
I use this small inline function in finite difference code.
A=zeros(6,3);
range=#(A,i)[1-min(i,0):size(A,1)-max(i+size(A,1)-size(A,2),0 ) ];
Diag=#(A,i) sub2ind(size(A), range(A,i),range(A,i)+i );
A(Diag(A, 0))= 10; %set diagonal
A(Diag(A, 1))= 20; %equivelent to diag(A,1)=20;
A(Diag(A,-1))=-20; %equivelent to diag(A,-1)=-20;
It can be easily modified to work on a sub-range of the diagonal by changing the function range.

Block matrix creation

I have two matrices A and D with sizes mxm. I want to create with these two a block matrix B size mn x mn . For example if n=5 then the output will be
B= D A A A A
A D 0 0 0
A 0 D 0 0
A 0 0 D 0
A 0 0 0 D
I have managed to create this form with many for loops but I would like a quicker solution with functions provided by matlab.
This should do the trick:
m = 3;
n = 5;
mn = m*n;
A_val = 4;
D_val = 2;
% Just an example, you could use rand(m) instead...
A = repmat(A_val,m);
D = repmat(D_val,m);
D_cell = repmat({D},1,n);
B = blkdiag(D_cell{:});
idx_1 = 1:m;
idx_2 = (m+1):mn;
B(idx_2,idx_1) = repmat(A,n-1,1);
B(idx_1,idx_2) = repmat(A,1,n-1);
Output:
B =
2 2 2 4 4 4 4 4 4 4 4 4 4 4 4
2 2 2 4 4 4 4 4 4 4 4 4 4 4 4
2 2 2 4 4 4 4 4 4 4 4 4 4 4 4
4 4 4 2 2 2 0 0 0 0 0 0 0 0 0
4 4 4 2 2 2 0 0 0 0 0 0 0 0 0
4 4 4 2 2 2 0 0 0 0 0 0 0 0 0
4 4 4 0 0 0 2 2 2 0 0 0 0 0 0
4 4 4 0 0 0 2 2 2 0 0 0 0 0 0
4 4 4 0 0 0 2 2 2 0 0 0 0 0 0
4 4 4 0 0 0 0 0 0 2 2 2 0 0 0
4 4 4 0 0 0 0 0 0 2 2 2 0 0 0
4 4 4 0 0 0 0 0 0 2 2 2 0 0 0
4 4 4 0 0 0 0 0 0 0 0 0 2 2 2
4 4 4 0 0 0 0 0 0 0 0 0 2 2 2
4 4 4 0 0 0 0 0 0 0 0 0 2 2 2
Average tic-toc performance over 1000 iterations 0.00018 seconds.
For more details about the employed functions, refer to the following links:
blkdiag
repmat
It is easy to do with kronecker product kron:
m = 3; % size of the blocks
n = 5; % number of blocks
A = rand(m); % insert you matrices here
D = rand(m);
maskA = zeros(n); % maskA is the block structure of A
maskA(1,:) = 1;
maskA(:,1) = 1;
maskA(1,1) = 0;
maskD = eye(n); %maskD is the block structure of D
B = kron(maskA,A) + kron(maskD,D);
Here is a way using cell2mat:
C = {zeros(size(A)), D , A};
mat = ones(n);
mat(1:n+1:end)=2;
mat(1,2:end)= 3;
mat(2:end,1)= 3;
out = cell2mat(C(mat));
Here's a way:
A = [10 20; 30 40]; % square matrix
D = [50 60; 70 80]; % square matrix
n = 5; % positive integer
tmp_A = repmat({A}, 1, n-1);
tmp_D = repmat({D}, 1, n-1);
result = [D, horzcat(tmp_A{:}); vertcat(tmp_A{:}), blkdiag(tmp_D{:})]

Matlab Find number of consecutive zeros

I want to find the lengths of series zeros in a matrix
A = [0 0 0 3 1 4 6 0 9 1 0 0 0 0 0 0 1 5 2 1 1;2 3 1 0 0 4 6 0 0 0 2 3 8 6 0 0 0 0 0 1 1]
I need result gives seriesZeros = [3 1 6;2 3 5] and also [rows,cols] from series of zeros value
thank you very much...
You could do this as follows:
A = [0 0 0 3 1 4 6 0 9 1 0 0 0 0 0 0 1 5 2 1 1;
2 3 1 0 0 4 6 0 0 0 2 3 8 6 0 0 0 0 0 1 1];
[N,~] = size(A);
% pad A==0 with zeros, and calculate diff for each row
A2 = diff([zeros(N,1) A==0 zeros(N,1)],[],2);
out_mtx = [];
for row_i = 1:size(A2,1)
row = A2(row_i, :);
zero_lengths = find(row == -1) - find(row == 1);
out_mtx(end+1,:) = zero_lengths;
end
out_mtx
Which gives
out_mtx =
3 1 6
2 3 5

Finding the non-intersecting rows in a matrix

I want to find the non-intersecting rows in a large matrix. As an example:
A=[1 5 3; 3 4 5; 7 9 10;4 5 6;11 2 8; 3 5 10]
In this matrix, the non-intersecting rows are: [1 5 3], [11 2 8] and [7 9 10]. How can I program this in Matlab in a fast way?
If I may bsxfun -
M = squeeze(any(bsxfun(#eq,A,permute(unique(A),[3 2 1])),2))
[~,row_idx] = max(M,[],1)
out = A(sum(M,2).' == histc(row_idx,1:size(A,1)),:)
Sample step-by-step run -
A =
1 5 3
3 4 5
7 9 10
4 5 6
11 2 8
3 5 10
M =
1 0 1 0 1 0 0 0 0 0 0
0 0 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0 1 1 0
0 0 0 1 1 1 0 0 0 0 0
0 1 0 0 0 0 0 1 0 0 1
0 0 1 0 1 0 0 0 0 1 0
row_idx =
1 5 1 2 1 4 3 5 3 3 5
out =
1 5 3
7 9 10
11 2 8
You can look for rows that adding them to union of previous rows increases the number of elements in the union by the number of columns (i.e. all elements in that row are new):
B = [];
C = zeros(1,size(A,1));
for k=1:size(A,1),
B1 = union(B, A(k,:));
C(k) = numel(B1)-numel(B);
B=B1;
end
result = A(C==size(A,2),:);

How to assign values to a MATLAB matrix on the diagonal?

Suppose I have an NxN matrix A, an index vector V consisting of a subset of the numbers 1:N, and a value K, and I want to do this:
for i = V
A(i,i) = K
end
Is there a way to do this in one statement w/ vectorization?
e.g. A(something) = K
The statement A(V,V) = K will not work, it assigns off-diagonal elements, and this is not what I want. e.g.:
>> A = zeros(5);
>> V = [1 3 4];
>> A(V,V) = 1
A =
1 0 1 1 0
0 0 0 0 0
1 0 1 1 0
1 0 1 1 0
0 0 0 0 0
I usually use EYE for that:
A = magic(4)
A(logical(eye(size(A)))) = 99
A =
99 2 3 13
5 99 10 8
9 7 99 12
4 14 15 99
Alternatively, you can just create the list of linear indices, since from one diagonal element to the next, it takes nRows+1 steps:
[nRows,nCols] = size(A);
A(1:(nRows+1):nRows*nCols) = 101
A =
101 2 3 13
5 101 10 8
9 7 101 12
4 14 15 101
If you only want to access a subset of diagonal elements, you need to create a list of diagonal indices:
subsetIdx = [1 3];
diagonalIdx = (subsetIdx-1) * (nRows + 1) + 1;
A(diagonalIdx) = 203
A =
203 2 3 13
5 101 10 8
9 7 203 12
4 14 15 101
Alternatively, you can create a logical index array using diag (works only for square arrays)
diagonalIdx = false(nRows,1);
diagonalIdx(subsetIdx) = true;
A(diag(diagonalIdx)) = -1
A =
-1 2 3 13
5 101 10 8
9 7 -1 12
4 14 15 101
>> tt = zeros(5,5)
tt =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
>> tt(1:6:end) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 3 0 0
0 0 0 3 0
0 0 0 0 3
and more general:
>> V=[1 2 5]; N=5;
>> tt = zeros(N,N);
>> tt((N+1)*(V-1)+1) = 3
tt =
3 0 0 0 0
0 3 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 3
This is based on the fact that matrices can be accessed as one-dimensional arrays (vectors), where the 2 indices (m,n) are replaced by a linear mapping m*N+n.
>> B=[0,4,4;4,0,4;4,4,0]
B =
0 4 4
4 0 4
4 4 0
>> v=[1,2,3]
v =
1 2 3
>> B(eye(size(B))==1)=v
%insert values from v to eye positions in B
B =
1 4 4
4 2 4
4 4 3
A = zeros(7,6);
V = [1 3 5];
[n m] = size(A);
diagIdx = 1:n+1:n*m;
A( diagIdx(V) ) = 1
A =
1 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 1 0
0 0 0 0 0 0
0 0 0 0 0 0
I'd use sub2ind and pass the diagonal indices as both x and y parameters:
A = zeros(4)
V=[2 4]
idx = sub2ind(size(A), V,V)
% idx = [6, 16]
A(idx) = 1
% A =
% 0 0 0 0
% 0 1 0 0
% 0 0 0 0
% 0 0 0 1
Suppose K is the value. The command
A=A-diag(K-diag(A))
may be a bit faster
>> A=randn(10000,10000);
>> tic;A(logical(eye(size(A))))=12;toc
Elapsed time is 0.517575 seconds.
>> tic;A=A+diag((99-diag(A)));toc
Elapsed time is 0.353408 seconds.
But it consumes more memory.
I use this small inline function in finite difference code.
A=zeros(6,3);
range=#(A,i)[1-min(i,0):size(A,1)-max(i+size(A,1)-size(A,2),0 ) ];
Diag=#(A,i) sub2ind(size(A), range(A,i),range(A,i)+i );
A(Diag(A, 0))= 10; %set diagonal
A(Diag(A, 1))= 20; %equivelent to diag(A,1)=20;
A(Diag(A,-1))=-20; %equivelent to diag(A,-1)=-20;
It can be easily modified to work on a sub-range of the diagonal by changing the function range.