Counting entries equal to zero in GNU Octave - algorithm

Is there any command in GNU Octave that allows me to count the zero (without counting the nonzero) entries in a matrix?

There are may ways, I'll show you two below.
a = rand (5,5) > 0.5
a =
0 0 0 1 1
1 1 0 1 0
0 1 0 1 1
0 0 0 1 0
1 1 0 1 1
numel (find (a==0))
ans = 12
This is faster for very large matrices (see below)
numel (a) - nnz (a)
ans = 12
Speed test for large matrices:
a = rand (1e6, 1e6) > 0.5;
tic
numel (find (a==0))
toc
tic
numel (a) - nnz (a)
toc
which gives
ans = 499566
Elapsed time is 0.060837 seconds.
ans = 499566
Elapsed time is 0.0187149 seconds.

Related

How to replace kth diagonal in a matrix in octave?

I am trying to replace the sub and super diagonals of a matrix in Octave.
This is the code I am using:
A=[-3 -2 -1 0 1 2 3;0.1 0.2 0.2 0.5 0.6 -0.1 0]'
P=zeros(4,4)
for (k=1:7)
j=A(k,1)
diag(P,j)=A(k,2)
end
This is the error I got: diag(0,_): subscripts must be either integers 1 to (2^63)-1 or logicals
But all the little parts are okay. diag(P,-3) works fine, but when I ask to replace in the loop it refuses!
What can I do about it? Is this: diag(P,j)=e, not the right code to substitute super and sub diagonals?
The reason you're getting an error is that diag(P,j) is not a reference to the diagonal of P, it is a function that returns the values on that diagonal. So what you're doing is assigning the value A(k,2) to the return value of the function and, since it's never assigned to a variable name, the value is lost and nothing changes.
To fix your loop, you would need to provide indices into P and assign to those. One way is to use logical indexing to tell MATLAB which values in P to change. For example,
P = zeros(4)
M = logical(diag([1,1,1], -1))
P(M) = 3
gives us
P =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
M =
0 0 0 0
1 0 0 0
0 1 0 0
0 0 1 0
P =
0 0 0 0
3 0 0 0
0 3 0 0
0 0 3 0
The unfortunate part is that we can't specify both which diagonal we want to create and the size of the resulting matrix, so we have to calculate the number of elements on the diagonal before creating it.
A=[-3 -2 -1 0 1 2 3;0.1 0.2 0.2 0.5 0.6 -0.1 0].'
n=4; % Number of rows/columns in P...
% If we want a non-square matrix, we'll have to do more math
P=zeros(n);
for k=1:2*n-1 % Remove hardcoded values to make the code more general.
j=A(k,1);
diag_length = n-abs(j);
M=diag(true(1,diag_length),j); % Create logical array with true on jth diagonal
P(M)=A(k,2);
end
The result is:
P =
0.5000 0.6000 -0.1000 0
0.2000 0.5000 0.6000 -0.1000
0.2000 0.2000 0.5000 0.6000
0.1000 0.2000 0.2000 0.5000
Another approach is to use spdiags. One of the uses of spdiags takes the columns of one matrix and uses them to build the diagonals of the output matrix. You pass the indices of the diagonals to set, and the matrix of values for each of the diagonals, along with the matrix size.
If we only pass one value for each diagonal, spdiags will only set one value, so we'll have to duplicate the input vector n times. (spdiags will happily throw away values, but won't fill them in.)
A=[-3 -2 -1 0 1 2 3;0.1 0.2 0.2 0.5 0.6 -0.1 0].'
n = 4;
diag_idx = A(:,1).'; % indices of diagonals
diag_val = A(:,2).'; % corresponding values
diag_val = repmat(diag_val, n, 1); % duplicate values n times
P = spdiags(diag_val, diag_idx, n, n);
P = full(P);
That last line is because spdiags creates a sparse matrix. full turns it into a regular matrix. The final value of P is what you'd expect:
P =
0.5000 0.6000 -0.1000 0
0.2000 0.5000 0.6000 -0.1000
0.2000 0.2000 0.5000 0.6000
0.1000 0.2000 0.2000 0.5000
Of course, if you're into one-liners, you can combine all of these commands together.
P = full(spdiags(repmat(A(:,2).', n, 1), A(:,1).', n, n));

Find maximum covered elements without block by obstacles in path

Given MXN matrix where matrix elements are either "." or "*". Where . is representing road and * is representing block or wall. Person can move adjacent forward, down and diagonally, we need to find maximum "." covered by person without blocked by wall. Example(in image)
Can you please suggest me efficient algorithm to approach this problem?
You have to do this: https://en.wikipedia.org/wiki/Flood_fill
Take the biggest flood you can do.
You go through your matrix and find a '.'
Do a flood from that point. The amount of elements you flood the area you always compare it with the maximum you already found. To make this easy you can flood with a letter or a number or whatever you want but not with '.'. What you add instead of '.' consider it as a wall or a '*' so you don't try to flood that area again and again.
Continue to go through the matrix and try to find the next '.'. All the previous '.' where flooded so you won't consider the same area twice.
Redo 2 until you can't find any more '.'. The maximum will contain your answer.
When you have the answer you can go back in the Matrix and you already know the letter or number you flooded the area with the maximum result so you can print the biggest area.
Are you looking for the exact path or only the number of cases?
Edit: here a smallp Python script which creates a random matrix and count the number of cases in each zone defined by your "walls".
import numpy as np
matrix = np.random.randint(2, size=(10, 10))
print(matrix)
M, N = matrix.shape
walked = []
zonesCount = []
def pathCount(x, y):
if x < 0 or y < 0 or x >= M or y >= N:
return 0
if matrix[x, y] == 1: # I replaced * by 1 and . by 0 for easier generation
return 0
if (x, y) in walked:
return 0
walked.append((x, y))
count = 1
for i in [x - 1, x, x + 1]:
for j in [y - 1, y, y + 1]:
if (i, j) != (x, y):
count += pathCount(i, j)
return count
for x in range(M):
for y in range(N):
if not (x, y) in walked:
zonesCount.append(pathCount(x, y))
print('Max zone count :', max(zonesCount))
And here is the result:
[[0 0 1 0 0 0 1 0 1 0]
[1 0 1 0 0 0 1 0 1 1]
[0 1 0 0 1 0 0 1 1 1]
[0 0 1 0 0 0 1 1 0 1]
[1 0 1 1 1 1 0 1 1 0]
[1 0 1 1 1 1 0 1 1 0]
[0 0 0 1 1 1 0 0 0 0]
[1 0 0 1 1 0 0 1 1 0]
[0 1 0 1 0 0 1 0 1 1]
[0 1 1 0 0 0 1 0 1 0]]
Max zone count : 50

How to rearrange the elements of a matrix in Matlab according to the order of elements in another matrix?

I have a vector A in Matlab of dimension (mxn)x1 composed by real numbers greater or equal then zero, e.g. m=3,n=4 A=[1;0;0;0;0.4;0.7;0.5;0.6;0.8;0;1;6] which looks like
A=[1
0
0
---
0
0.4
0.7
---
0.5
0.6
0.8
---
0
1
6]
We can see that A is composed by n subvectors of dimension m. I have a vector B of dimension gx1, with g greater or equal than m, composed by ones and zeros such that the total number of ones is equal to m, e.g. g=9 B=[1;0;0;0;0;0;0;1;1] which looks like
B=[1
0
0
0
0
0
0
1
1]
I want to create a matrix C of dimension gxn in which the entries of each subvector of A are placed in correspondence of the ones in g for each column of B, e.g.
C=[1 | 0 | 0.5 | 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
0 | 0.4| 0.6 | 1
0 | 0.7| 0.8 | 6]
Loops are fine only if very fast. Real dimensions of matrices are very large (e.g. mxn=100000, g=50000)
Approach #1 (bsxfun based linear indexing)
C = zeros(g,n) %// Pre-allocate
idx = bsxfun(#plus,find(B),[0:n-1]*g) %// Indices where A elements are to be put
C(idx)= A %// Put A elements
Approach #2 (Direct replacement)
C = zeros(g,n)
C(find(B),:) = reshape(A,m,[])
Pre-allocation: For faster pre-allocation you can do this instead in both of the above mentioned approaches -
C(g,n) = 0;
You can also try the repmat and logical indexing approach. First, reshape your data so that it's in the right matrix form, so in your case 3 x 4, then use B as a logical mask and replicate it for as many times as we have columns in your matrix, then do an assignment. You would have to allocate a matrix that is the size of your desired output before doing this assignment. Something like this:
%// Your example data
m = 3; n = 4;
A = [1;0;0;0;0.4;0.7;0.5;0.6;0.8;0;1;6];
B = logical([1 0 0 0 0 0 0 1 1]).';
%// Relevant code
Am = reshape(A, m, n);
Bm = repmat(B, 1, n);
C = zeros(numel(B), 4);
C(Bm) = Am;
C is the desired result, where we get:
C =
1.0000 0 0.5000 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
0 0.4000 0.6000 1.0000
0 0.7000 0.8000 6.0000
My gut feeling is that this will be slower than the bsxfun approach, but the above is more readable if you're not familiar with how bsxfun works.

script for calculating boundaries between elements of matrix

The following script gives me sum of boundaries of each element of matrix. boundaries are calculated about elements with value 1 that are next to each other. product of this summation named contact perimeter.
but Is there any other way that I can summarize or vectorize my raw and simple script?
I ask this request because my real matrix is very large and use of "for" increase the time of calculation.
Thank you.
a1=[1 1 0 1 0 1;
0 1 1 0 0 1;
1 1 0 1 0 1;
1 1 0 0 1 0;
0 0 0 1 1 1]
m=5
n=6
cmp=zeros(m,n)
cmp1=zeros(m,n)
for i=1:m-1
for j=1:n
if a1(i,j)==a1(i+1,j) && a1(i,j)==1
cmp(i,j)=1
end
end
for i=1:m
for j=1:n-1
if a1(i,j)==a1(i,j+1) && a1(i,j)==1
cmp1(i,j)=1
end
end
end
end
cmtotal=cmp+cmp1
pc=sum(sum(cmtotal))
This should be pretty efficient one -
%// Case1 and case 2 matches
case1_matches = a1(1:end-1,:) == a1(2:end,:) & a1(1:end-1,:)==1
case2_matches = a1(:,1:end-1) == a1(:,2:end) & a1(:,1:end-1)==1
%// Get sum of those matches for the final output, equivalent to your pc
out = sum(case1_matches(:)) + sum(case2_matches(:))
You can replace the sum(..(:)) with nnz(), but I doubt that it will be any better than sum in terms of runtime performance in accordance with benchmarks of sum against nnz.

Counting subrows in each row of a matrix in Matlab?

I need an algorithm in Matlab which counts how many adjacent and non-overlapping (1,1) I have in each row of a matrix A mx(n*2) without using loops. E.g.
A=[1 1 1 0 1 1 0 0 0 1; 1 0 1 1 1 1 0 0 1 1] %m=2, n=5
Then I want
B=[2;3] %mx1
Specific case
Assuming A to have ones and zeros only, this could be one way -
B = sum(reshape(sum(reshape(A',2,[]))==2,size(A,2)/2,[]))
General case
If you are looking for a general approach that must work for all integers and a case where you can specify the pattern of numbers, you may use this -
patt = [0 1] %%// pattern to be found out
B = sum(reshape(ismember(reshape(A',2,[])',patt,'rows'),[],2))
Output
With patt = [1 1], B = [2 3]
With patt = [0 1], B = [1 0]
you can use transpose then reshape so each consecutive values will now be in a row, then compare the top and bottom row (boolean compare or compare the sum of each row to 2), then sum the result of the comparison and reshape the result to your liking.
in code, it would look like:
A=[1 1 1 0 1 1 0 0 0 1; 1 0 1 1 1 1 0 0 1 1] ;
m = size(A,1) ;
n = size(A,2)/2 ;
Atemp = reshape(A.' , 2 , [] , m ) ;
B = squeeze(sum(sum(Atemp)==2))
You could pack everything in one line of code if you want, but several lines is usually easier for comprehension. For clarity, the Atemp matrix looks like that:
Atemp(:,:,1) =
1 1 1 0 0
1 0 1 0 1
Atemp(:,:,2) =
1 1 1 0 1
0 1 1 0 1
You'll notice that each row of the original A matrix has been broken down in 2 rows element-wise. The second line will simply compare the sum of each row with 2, then sum the valid result of the comparisons.
The squeeze command is only to remove the singleton dimensions not necessary anymore.
you can use imresize , for example
imresize(A,[size(A,1),size(A,2)/2])>0.8
ans =
1 0 1 0 0
0 1 1 0 1
this places 1 where you have [1 1] pairs... then you can just use sum
For any pair type [x y] you can :
x=0; y=1;
R(size(A,1),size(A,2)/2)=0; % prealocarting memory
for n=1:size(A,1)
b=[A(n,1:2:end)' A(n,2:2:end)']
try
R(n,find(b(:,1)==x & b(:,2)==y))=1;
end
end
R =
0 0 0 0 1
0 0 0 0 0
With diff (to detect start and end of each run of ones) and accumarray (to group runs of the same row; each run contributes half its length rounded down):
B = diff([zeros(1,size(A,1)); A.'; zeros(1,size(A,1))]); %'// columnwise is easier
[is js] = find(B==1); %// rows and columns of starts of runs of ones
[ie je] = find(B==-1); %// rows and columns of ends of runs of ones
result = accumarray(js, floor((ie-is)/2)); %// sum values for each row of A

Resources