Round three: Jessica vs Battletanks
**tl;dr:
Right now, I need help making my dimensions match. My function flags me at "if attacked == 0"
What I am trying to make it do is read if the value at that location is a zero or not. If it's a zero, then there is no ship there, so it's a 'miss'. If there is a number there (4, 3,2 or 1) then it's a hit.
Basically what I need to do for this next problem is figure out how to write a function that simulates a game of battletanks (or battleship).
http://en.wikipedia.org/wiki/Battleship_(game) in case you've never played
In this function, I feed in three inputs: two arrays of tank locations (so player1 and player2) and a string of the moves made (they are all separated by a space). The field is an 8x8 array and the moves are indicated by strings such as 'A7 H6' etc etc. A7 means to hit the spot at column 1, row 6. 'H6' means to hit column 8, row 6. I have to reflect that in my function.
What I need to output is: the array of the victor's tanks, a vector that details the winner's hits(in order they were made) and a vector displaying the winner (or the string 'Cease Fire!')
function[winner_field, winner_hits, tanks_destroyed] = battleTanks(player1_tanks, player2_tanks, battle_str)
There are four types of tanks in the game:
Heavy tanks are represented by the number '4' and are 3x2
Medium tanks are represented by the number '3' are are 2x2
Light tanks are represented by the number '1' are a 2x1
Tank destroyers are represented by the number '2' and are 3x1
Empty spaces are represented by a '0'
Note:Tanks can be aligned vertically or horizontally
The first player always makes the first move, and it alternates
Not all tanks have to be played, however if you only play one tank and it gets destroyed, you lose
I need to keep track of the order where ships get hit, and then which ones get destroyed
You can make the same move over again
In my output arrays, any place where a tank has been hit but not destroyed should be represented by a 0. So there may be "jagged tanks"
How to win:
If a player destroys all of the player's tanks, they win
If neither side has lost all their tanks after all moves have been played, the winner is determined by the number of hits scored on the enemy fleet (NOT the number of tanks they destroyed).
There can be a tie, in which case In the event of this, the outputs of the function will become the secondary outputs 1) the 1st players final 8x8 array, 2) the 2nd players final 8x8 array, and 3) a string, 'Cease fire!'.
I apologize in advance for the length of the question, and my code.
function[winner_field, winner_hits, tanks_destroyed] = battleTanks(player1_tanks, player2_tanks, battle_str)
player1_moves = mod(battle_str, 2) == 1;
player2_moves = mod(battle_str, 2) == 0; %// Player 2 is the even moves
[player1_moves, player2_moves] = strtok(battle_str);
player2_moves = char(player2_moves);
player1_moves = char(player1_moves);
column = upper(player1_moves(1)) - 64;
row = player1_moves(2) - 48; %// Does the conversion so my funciton knows that A7 means column 1, row 7
attacked = find(player1_moves);
attacked2 = find(player2_moves);
winner_hits = []; %// Empty vector to populate with answers
tanks_destroyed = []; %// Same as above
Counter1 = 0; %// My counter for hits, just in case they both run out of moves before finishing
Counter2 = 0;
Tanks_destroyed1 = 0; %// Counts how many tanks are destroyed by player 1
Tanks_destroyed2 = 0;
heavy_tank1 = find(player1_tanks, 4); %// locates the tanks
heavy_tank2 = find(player2_tanks, 4);
medium_tank1 = find(player1_tanks, 3);
medium_tank2 = find(player2_tanks, 3);
tank_destroyer1 = find(player1_tanks, 2);
tank_destroyer2 = find(player2_tanks, 2);
light_tank1 = find(player1_tanks, 1);
light_tank2 = find(player2_tanks, 1);
if attacked == 0 %// If they hit a zero, it counts as a mis
attack = 'missed';
else
attack = 'hit'; %// if they find a number, they hit a tank
end
hit = 'hit'; %// Translates the string into a variable I can use
missed = 'missed';
if attacked == hit %// Does the counter
Counter1 = Counter1 + 1;
elseif attacked2 == hit
Counter2 = Counter2 + 1;
else
Counter1 = Counter1 + 0;
end
for field = 1:length(player1_tanks)
if heavy_tank1 == 6 %// Counts the destroyed tanks
tank = 'destroyed';
Tanks_destroyed2 = Tanks_destroyed2 + 1;
elseif tank_destroyer1 == 3
tank = 'destroyed';
Tanks_destroyed2 = Tanks_destroyed2 + 1;
elseif medium_tank1 == 4
tank = 'destroyed';
Tanks_destroyed2 = Tanks_destroyed2 + 1;
elseif light_tank1 == 2
tank = 'destroyed';
Tanks_destroyed2 = Tanks_destroyed2 + 1;
else
tank = 'missed';
end
end
for field = 1:length(player2_tanks)
if heavy_tank2 == 6
tank = 'destroyed';
Tanks_destroyed1 = Tanks_destroyed1 + 1;
elseif tank_destroyer2 == 3
tank = 'destroyed';
Tanks_destroyed1 = Tanks_destroyed1 + 1;
elseif medium_tank2 == 4
tank = 'destroyed';
Tanks_destroyed1 = Tanks_destroyed1 + 1;
elseif light_tank2 == 2
tank = 'destroyed';
Tanks_destroyed1 = Tanks_destroyed1 + 1;
else
tank = 'missed';
end
end
if Tanks_destroyed1 == player2_tanks %// Determines the winner
winner_field = player1_tanks;
elseif Tanks_destroyed2 == player1_tanks
winner_field = player2_tanks;
elseif Counter1 > Counter2
winner_field = player1_tanks;
elseif Counter2 > Counter1
winner_field = player2_tanks;
else
winner_field = 'Cease Fire!';
end
if winner_field == player1_tanks
winner_hits = hits;
end
end
This code is really killing my wrists, so I apologize for others who wrists may be hurt. Though I am probably over complicating it.
Testcases
[results1, winHits1, winDestroy1] = battleTanks(battleAP1,battleAP2,moveA)
results1 should be the same as resultsA (which is saved in battleTanks.mat)
winHits1 = [1 1 2 2 2 3 3 3 3]
winDestroy1 = [1 2 3]
battleAP1, battleAP2 and moveA are all files include with the HW problem. I'm going to try and access them to get out their information.
So what I need help with basically is figuring out how to format my output statements and making the function realize that A = column 1.
battleAP1:
0 0 0 0 0 0 0 0
0 0 0 3 3 0 0 0
0 2 0 3 3 0 0 0
0 2 0 4 4 4 0 0
0 2 0 4 4 4 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0
battleAP2
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 3 3 0
0 0 4 4 0 3 3 0
0 0 4 4 0 0 0 0
0 0 4 4 2 2 2 0
0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 0
MoveA: 'A5 H7 C3 G1 D3 F7 G6 C2 H6 D4 G7 H2 G8 H4 E5 D8 F6 B6 E6 A5 F5 G2 H3'
Regarding your particular question in the comments, if I have an input A7 and I want to convert it to a set of indices, one way to do this is to split it up and then subtract the ASCII offset. Google ASCII table if you don't understand the numbers I'm using. Basically when you do match with a char and a number in matlab, it automatically converts the char to its ASCII number.
input = 'A7';
column = upper(input(1)) - 64; %upper guarantees that my letter will be upper case
row = input(2) - 48;
Related
my problem is: I have to make a code that takes a matrix 6x6 of 0's and 1's and check certain conditions in each position of the matrix to iterate through it, and create a matrix with the new values based on the conditions, that are based in the neighbor elements of the center value. The conditions are the following:
if the center value is 1 it has 3 options:
-If in the neighbor cells there is only one cell containing a number 1, or cero cells containing a number 1 the center value converts from 1 to 0.
-If the neighbor cells of the center value contains in total 4 or more 1's, it converts from 1 to 0.
-And if the neighbor cells contain in total 2 or 3 number 1's, it keeps being 1.
Now, if the center value is 0, it has 1 option:
-If the neighbor cells contain 3 1's in total (tops), it converts from 0 to 1.
-Else, it keeps being 0
The matrix is the following:
A = [0 1 0 1 1 0; 1 0 1 0 0 1; 1 1 0 1 1 1; 0 0 1 0 0 0; 0 1 0 0 0 1; 1 1 1 0 0 1]
So for example, the number 1 in the position A(5,2) should become 0, because it has 4 1's surrounding it, and another example, the zero in A(4,6) should become 1, because it has 3 1's surrounding it.
I made 2 loops using 1 for the rows and another for the columns to iterate through each value of the matrix, but in the conditional I don't know how to check the values of the surrounding cells or neighbor cells
I'm new to Matlab, if you can help me it would be great! Thanks.
Edit: added code the code I've made until now.
[f,c]=size(A)
for i=1:f
for j=1:c
if A(i,:)==1
if A(i+1,j+1)==1 && A(i+1,j-1)==1 && A(i-1,j-1)==1
A(i,:)=1;
end
elseif A(i,:)==0
if A(i+1,j+1)==1 && A(i+1,j-1)==1 && A(i-1,j-1)==1
A(i,:)=1;
end
end
end
end
I tried to set the conditions to the (i-1,j-1),(i+1,j+1),(i+1,j),(i,j+1),(i-1,j),(i,j-1) but I think that will not work because I'm checking coditions as a group not counting the total numbers of 1 in the neighbors
Edit 2: been thinking how to solve it and I thought that I could make a variable that counts the quantity of 1's in the surroundings of the center value, but it gives me an error that it exceeds the dimensions of the matrix in line 50, even after I made considerations for the first column, row and last column, row.
This is the complete code:
[f,c] = size(gen0);
nrogen = input('Introduzca el número de generaciones de bacterias que quiera crecer: ');
if isempty(nrogen) || ~isnumeric(nrogen) || ~isscalar(nrogen) || round(nrogen)~=nrogen || nrogen < 0
disp('Número no pertinente, intente de nuevo');
return
end
nac = 0;
mue = 0;
neigh = 0;
for k=1:nrogen
for i=1:f
for j=1:c
if i>1 && i<6
if gen0(i+1,j+1)==1
neigh=neigh+1;
end
if gen0(i,j+1)==1
neigh=neigh+1;
end
if gen0(i+1,j)==1
neigh=neigh+1;
end
if gen0(i-1,j-1)==1
neigh=neigh+1;
end
if gen0(i,j-1)==1
neigh=neigh+1;
end
if gen0(i-1,j)==1
neigh=neigh+1;
end
elseif i==1
if gen0(i+1,j+1)==1
neigh=neigh+1;
end
if gen0(i,j+1)==1
neigh=neigh+1;
end
if gen0(i+1,j)==1
neigh=neigh+1;
end
elseif i==6
if gen0(i-1,j-1)==1
neigh=neigh+1;
end
if gen0(i,j-1)==1
neigh=neigh+1;
end
if gen0(i-1,j)==1
neigh=neigh+1;
end
if gen0(i,:)==1
if neigh==2 || neigh==3
gen0(i,:)=1;
elseif neigh==1 || neigh==0
gen0(i,:)=0;
end
end
end
end
disp(gen0);
end
if gen0(i,:)==1
if neigh==2 || neigh==3
gen0(i,:)=1;
elseif neigh==1 || neigh==0 || neigh>3
gen0(i,:)=0;
end
end
end
Nested loops is a sensible option. However, what you are doing is basically an image filter, something that is already implemented by the function imfilter, so Why not to take advantage of that and simplify your life? What you want to do can be accomplished with:
%Creating the filter that adds up all neighbors
filter=ones(3,3);
filter(2,2)=0;
%Apply the filter to compute the sum of all neighboring elements
onesCount=imfilter(A,filter);
%Creating the rules in arrays that contains result for a given count of ones (+1)
%ones count 0 1 2 3 4 5 6 7 8 9
onesRule = [0 0 1 1 0 0 0 0 0 0];
zeroRule = [0 0 0 1 0 0 0 0 0 0];
%Initializing output matrix
out=zeros(size(A));
%Assigning values to the cells with ones
outForOnes=onesRule(onesCount+1);
out(A==1)=outForOnes(A==1);
%Assigning values to the cells with zeros
%(if you want you can skip this step initializing out=outForZeros)
outForZeros=zeroRule(onesCount+1);
out(A==0)=outForZeros(A==0);
Otherwise, if you want to keep the nested loops, instead of dealing with the exceptions of index out of bounds, I would suggest to pad A with zeros all around. So if A size is n by m (6 by 6 in this case) you do:
B=zeros(n+2,m+2);
B(2:end-1,2:end-1)=A;
A=B;
And then you loop i and j between 2:n+1 and 2:m+1
In my opinion, using two nested loops in this case is a good approach. Retrieving the surrounding values of a matrix element is always tricky, but it can be accomplished with some efforts. Here is the solution I propose you:
A = [
0 1 0 1 1 0;
1 0 1 0 0 1;
1 1 0 1 1 1;
0 0 1 0 0 0;
0 1 0 0 0 1;
1 1 1 0 0 1
];
A_rows = size(A,1);
A_cols = size(A,2);
for i = 1:A_rows
for j = 1:A_cols
% Retrieve the current center...
value = A(i,j);
% Retrieve the neighboring column and row offsets...
c = bsxfun(#plus,j,[-1 0 1 -1 1 -1 0 1]);
r = bsxfun(#plus,i,[-1 -1 -1 0 0 1 1 1]);
% Filter the invalid positions...
idx = (c > 0) & (c <= A_cols) & (r > 0) & (r <= A_rows);
% Transform the valid positions into linear indices...
idx = (((idx .* c) - 1) .* A_rows) + (idx .* r);
idx = reshape(idx.',1,numel(idx));
% Filter the invalid linear indices...
idx = idx(idx > 0);
% Find the center neighbors and their sum...
neighbors = A(idx);
neighbors_sum = sum(neighbors);
% Apply the transformation criterions to A...
if (value == 0)
if (neighbors_sum == 3)
A(i,j) = 1;
end
else
if (neighbors_sum <= 1) || (neighbors_sum >= 3)
A(i,j) = 0;
end
end
end
end
The final output for the given example is:
A =
0 1 1 0 0 0
0 0 0 1 0 1
0 0 1 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 1 1 0 0 0
I just have a few doubts about the whole process you described.
The first one concerns the criterions to apply when the center value is equal to 1. Two of them seem to be contradictory:
-If the neighbor cells of the center value contains in total 3 or more 1's, it converts from 1 to 0.
-And if the neighbor cells contain in total 2 or 3 number 1's, it keeps being 1.
When the neighbors sum is equal to 3... which condition should be applied? The first one or the second one?
The second one concerns the original matrix A. Should it be updated inside the loop? I mean, in the current code, the values of A change when certain conditions are met... but this also means that the conditions are influenced by the outcome of the previous iterations. Maybe your goal is to keep A static while updating a clone of it instead?
Anyway, both issues are easy to deal with and you should be able to adapt my code to your needs without any problem.
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
How do we count number of rising edges in a clock cycle using MATLAB code?
I have tried this-
counter = o ;
for (clock > 0)
counter = counter +1;
end
There are a lot of problems with your code as discussed in the comments, but assuming that you have a signal that changes between two states, for example
clock = [0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1]
then this simple expression will count the number of rising edges:
sum(diff(clock) > 0)
Without knowing more about the type of data that you are processing then it is difficult to provide a better answer, and indeed this will only work where every positive transition is considered a rising edge, i.e. there is no noise in the signal.
If you really want to use a for loop, then try this:
counter = 0;
for find(clock > 0)
counter = counter +1;
end
But this will not count the rising edges, rather the number of instances where clock > 0, which could also be achieved using just counter = sum(clock > 0). Note that in the for loop, if clock is a column vector then you will need to use for find(clock > 0)' instead of for find(clock > 0) (note the tick at the end of the line).
UPDATE to address your comment.
% Initialise clocks
x = [0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0];
y = [0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0];
% Initialise counter
count = zeros(size(y));
counter = 0;
% Loop through signals (note that x and y should be the same length)
for i = 2:numel(y)
% Find rising edge on x
if x(i - 1) == 0 && x(i) == 1
counter = 0;
end
% Find rising edge on y
if y(i - 1) == 0 && y(i) == 1
counter = counter + 1;
end
% Store current value of counter for display
count(i) = counter;
end
figure;
subplot(3, 1, 1);
stairs(x);
ylabel('x');
subplot(3, 1, 2);
stairs(y);
ylabel('y');
subplot(3, 1, 3);
stairs(count);
ylabel('counter');
This will give the following output:
I have a matrix A with size 10x100 as shown below. What I want to do is:
I'll work row by row in which for each row I'll check the data of
each coloumn in this row
Let's say I'm now in the first col cell in the first row. I'll check if the value is zero I'll move to the next col, and so on till I found a col having a non-zero value and save its col number e.g. col 3 "this means that col 1&2 were zeros"
Now I'm in the first non zero col in row1, I'll move to the next col till I find a col with zero value. I'll fetch the col just before this zero one which must be a non-zero one and save it. e.g col 7 "this means that col4&5&6 are non-zeros and col8 is zero"
Now I want to save the median middle col between this two columns e.g col3 and col7 then the middle col is col5 so I'll save the index row1_col5. if there are two middle values then any of them is fine.
I'll then move to the next col till I find a non-zero col "do the
same steps from 2-->5" till the first row is finished.
Move to the next row and start over again from step 2-->5.
There are two rules: -The first one is that I'll get the middle index of non-zero consecutive values only if there is a minimum of 3 non-zero consecutive values, if there are two non-zero consecutive value then the middle will not be calculated -The second one is that if the number of zero consecutive values are less than 3 then they will be ignored and will be considered as non-zero values. e.g in the below example the first row middle values are col5 and col11. In row2 col5 is counted, while no cols in row3 satisfy this conditions , and in row4 col6 or col7 will be counted.
After finishing all the rows want to have a vector or array holding the positions of all the middle indexes e.g row1_col5 row1_col17 row2_col_10 and so on.
example:
A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
0 0 0 5 1 1 1 1 0 0 0 1;
0 3 4 1 0 3 1 2 0 0 1 3;
0 0 0 0 1 3 4 5 0 0 0 0];
for the first row the middle value will be 5 and 11 and so on
So if anyone could please advise how can I do this with least processing as this can be done using loops but if there is more efficient way of doing it? Please let me know if any clarification is needed.
Now you have clarified your question (again...) here is a solution (still using a for loop...). It includes "rule 7" - excluding runs of fewer than three elements; it also includes the second part of that rule - runs of fewer than three zeros don't count as zero. The new code looks like this:
A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
0 0 0 5 1 1 1 1 0 0 0 1;
0 3 4 1 0 3 1 2 0 0 1 3;
0 0 0 0 1 3 4 5 0 0 0 0];
retVal = cell(1, size(A, 1));
for ri = 1:size(A,1)
temp = [1 0 0 0 A(ri,:) 0 0 0 1]; % pad ends with 3 zeros + 1
% so that is always a "good run"
isz = (temp == 0); % find zeros - pad "short runs of 0" with ones
diffIsZ = diff(isz);
f = find(diffIsZ == 1);
l = find(diffIsZ == -1);
shortRun = find((l-f)<3); % these are the zeros that need eliminating
for ii = 1:numel(shortRun)
temp(f(shortRun(ii))+1:l(shortRun(ii))) = 1;
end
% now take the modified row:
nz = (temp(4:end-3)~=0);
dnz = diff(nz); % find first and last nonzero elements
f = find(dnz==1);
l = find(dnz==-1);
middleValue = floor((f + l)/2);
rule7 = find((l - f) > 2);
retVal{ri} = middleValue(rule7);
end
You have to use a cell array for the return value since you don't know how many elements will be returned per row (per your updated requirement).
The code above returns the following cell array:
{[5 11], [6], [7], [7]}
I appear still not to understand your "rule 7", because you say that "no columns in row 3 satisfy this condition". But it seems to me that once we eliminate the short runs of zeros, it does. Unless I misinterpret how you want to treat a run of non-zero numbers that goes right to the edge (I assume that's OK - which is why you return 11 as a valid column in row 1; so why wouldn't you return 7 for row 3??)
Try this:
sizeA = size(A);
N = sizeA(1);
D = diff([zeros(1, N); (A.' ~= 0); zeros(1,N)]) ~= 0;
[a b] = find(D ~= 0);
c = reshape(a, 2, []);
midRow = floor(sum(c)/2);
midCol = b(1:2:length(b))
After this, midRow and midCol contain the indices of your centroids (e.g. midRow(1) = 1, midCol(1) = 4 for the example matrix you gave above.
If you don't mind using a for loop:
A = [ 0 0 1 1 1 0 1;
0 0 0 0 0 0 0;
0 1 1 1 1 0 0;
0 1 1 1 0 1 1;
0 0 0 0 1 0 0]; % data
sol = repmat(NaN,size(A,1),1);
for row = 1:size(A,1)
[aux_row aux_col aux_val] = find(A(row,:));
if ~isempty(aux_col)
sol(row) = aux_col(1) + floor((find(diff([aux_col 0])~=1,1)-1)/2);
% the final 0 is necessary in case the row of A ends with ones
% you can use either "floor" or "ceil"
end
end
disp(sol)
Try it and see if it does what you want. I hope the code is clear; if not, tell me
I have a vector a = 1111000011100001110000100100 and I have to compute two values based on it: p00 and p11.
p00 is the number of times 00 is occurring in the vector, divided by the total number of zeros. For example, in the above code then number of times 00 is occurring is 8/16 (total number of zeros).
Similarly, p11 is the number of occurrences of 11 divided by the total number of ones.
How can this be implemented in Matlab?
The safest and most generic way to do it is using regular expressions, because of the way they match runs.
a = [1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 0]
s = char(a + '0');
p00 = numel(regexp(s, '00')) / sum(a == 0)
p11 = numel(regexp(s, '11')) / sum(a == 1)
NOTE:
I was tempted to do something along the following lines:
a = [1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 0]
n = numel(a);
p00 = sum(a(1:n-1) == 0 & a(2:n) == 0) / sum(a == 0)
p11 = sum(a(1:n-1) == 1 & a(2:n) == 1) / sum(a == 1)
But this won't give the correct result, because it counts the sequence 0 0 0 0 as 3, rather than 2.
I would add the vector to itself shifted with one element to the right. The number of two-s will be the number of 11-s. The number of 0-s will be the number of 00-s. I think this is a natural solution in MATLAB.
Alternatively you could implement finite state machines to parse your vector.
a2 = a(2:end)+a(1:end-1);
p11 = length(find(a2 == 2))/length(find(a));
p00 = length(find(a2 == 0))/length(find(a==0));
The proposed solution was wrong!!!
Here is one that should work, but is not very efficient (but faster than the regexp solution):
d0=0; i=1;
while i<length(a)
if (a(i) == 0 & a(i)==a(i+1)) d0 = d0+1; i = i+1; end;
i=i+1;
end
p00 = d0/sum(a == 0)