Can I assign values to an array in a diagonal direction from a reference point on that array?

If I have an array of size NxN,called X, and I count through it using an (i,j) for loop like this
for i = 1:N
for j = 1:N
and im looking for a value in it like this
if(X(i,j) == 1)
I want to start where I find a 1 and change the diagonal lines around it to an 8. So far ive tried code similar to this and many things like it but it doesnt seem to work how I envision it
for i = 1:N
for j = 1:N
if(X(i,j) == 1)
X(i+1,j+1) = 8;
X(i-1,j-1) = 8;
X(i+1,j-1) = 8;
X(i-1,j+1) = 8;
end
end
end
I want the outcome to be something like
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
this turns into this
8 0 0 0 8
0 8 0 8 0
0 0 1 0 0
0 8 0 8 0
8 0 0 0 8

 Réponse acceptée

Perhaps:
X = [...
0 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 0 0
0 0 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 0
];
[R,C] = find(X);
for k = 1:numel(R)
r = R(k);
c = C(k);
X([r-1,r+1],[c-1,c+1]) = 8;
X([r-2,r+2],[c-2,c+2]) = 8;
end
giving:
>> X
X =
8 0 0 0 8 0
0 8 0 8 0 0
0 0 1 0 0 0
0 8 0 8 0 0
8 0 0 0 8 0
0 8 0 0 0 8
0 0 8 0 8 0
0 0 0 1 0 0
0 0 8 0 8 0
0 8 0 0 0 8

8 commentaires

Subscript indices must either be real positive integers or logicals. I get this error
@Shawn Blancett: please show the complete error message. This means all of the red text. You also need to show the code that you are actually using, including the input data.
It is very hard to diagnose an error without the error message and the code and data the produce the error. I would have to use my magic crystal ball, which is sadly a bit unreliable these days and needs to be serviced.
Sounds like you have a 1 somewhere very close to the edge in your input like at (2,2). Hence (2-2,2-2) becomes (0,0) which is not real positive.
@Ahmet Cecen: your magic crystal ball seems to be working well today :)
I guess there must be some edge-cases (literally!) that have to be taken care of:
X = [...
0 0 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 0
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 0 0 0
];
[Ir,Ic] = find(X);
[Sr,Sc] = size(X);
for k = 1:numel(Ir)
for n = 1:2
Vr = [Ir(k)-n,Ir(k)+n];
Vc = [Ic(k)-n,Ic(k)+n];
Jr = ismember(Vr,1:Sr);
Jc = ismember(Vc,1:Sc);
X(Vr(Jr),Vc(Jc)) = 8;
end
end
giving:
>> X
X =
0 8 0 0 0 8
0 0 8 0 8 0
0 0 0 1 0 0
0 0 8 0 8 0
0 8 0 0 0 8
0 0 0 8 0 0
8 0 8 0 0 0
0 1 0 0 0 0
8 0 8 0 0 0
0 0 0 8 0 0
Sorry about that, this is what I have so far with some things commented out
N = input('boardsize ');
X = zeros(N);
X(sub2ind([N,N], 1:N, randperm(N, N))) = 1;
%while(NQ<N)
for i = 1:N
for j = 1:N
if(X(i,j) == 1)
X(:,j) = 8;
X(i,j) = 1;
end
end
end
%index = find([X] == 1);
%NQ = numel(index);
disp(X)
@Shawn Blancett: your comment is totally unrelated to my answer. I showed you some code that works (did you look at my answer and comments?) and you showed me some totally unrelated code that apparently does not work.
Not only does the code I show you actually work, but it can be very easily adjusted to extend those lines of eights beyond just two in each direction.
Instead of wasting your own time by posting multiple almost-identical question but never actually describing what you want, we could all be much more productive if you gave an accurate specification of what you want. For example:
  • How many non-zeros can be in the matrix?
  • Are any locations excluded?
  • How long should the lines of eights extend?
  • etc.
Im trying to create a script that takes an input of N, where N is at minimum 8.
N will create an array of zeros(N) of size NxN so for example 8 0's
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 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 0 0
0 0 0 0 0 0 0 0
Then it will print out N amount of 1's on a different column and different row from one another
X = zeros(N);
X(sub2ind([N,N], 1:N, randperm(N, N))) = 1
After the 1's were placed I was looking to find the 1's and from where they were, change the diagonal lines around each of them ,reaching to the edge of the array and replacing the lines with 8's so in a 4x4
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0
would be
0 1 8 0
8 8 8 1
1 8 8 8
0 8 1 0 but also if the 1's were diagonal from eachother , it would be replaced to an 8 and not stay as a 1.
Which is what I think the lines you wrote were able to do. I wasent sure how long the diagonals of 8 reached when I had an 8x8 boardsize.
I wanted this to loop all together so that it had to count the number of 1's and after each iteration and print out the board at 1's == N
Sorry for the confusion, to clarify the bullet points. -Any number of non zeroes can be in the matrix, up to NxN -No location is excluded
N = input('boardsize ');
NQ = 0;
X = zeros(N);
X(sub2ind([N,N], 1:N, randperm(N, N))) = 1
%for i = 1:N (This is a loop that "attacks vertically but
% for j = 1:N But doesnt really change much because the
1's are already in unique columns)
% if(X(i,j) == 1)
% X(:,j) = 8;
% X(i,j) = 1;
% end
%end
%end
[Ir,Ic] = find(X);
[Sr,Sc] = size(X);
for k = 1:numel(Ir)
for n = 1:2
Vr = [Ir(k)-n,Ir(k)+n];
Vc = [Ic(k)-n,Ic(k)+n];
Jr = ismember(Vr,1:Sr);
Jc = ismember(Vc,1:Sc);
X(Vr(Jr),Vc(Jc)) = 8;
end
end
index = find([X] == 1);
NQ = numel(index);
disp(X)
What I have so far with the code you wrote for changing diagonals. NQ is a variable Im hoping will use as a while loop parameter if it all comes together
Given that you want to extend the eights all the way to the edge one easy strategy is to use linear indices, because it is easy to construct an vector of linear indices from the selected starting point: the trick is to remember that the step size is either N+1 for the diagonals, and N-1 for the antidiagonals. The end points are limited by 1 and end, and also by some mod-based limits to prevent the lines of eights wrapping around the top and bottom:
% Create a matrix:
N = 10;
X = zeros(N,N);
X([7,12,30,45,81,96]) = 1
% Fill with eights:
V = find(X);
for k = V(:).'
stp = N+1;
diagLo = k-stp*mod(k-1,N);
diagHi = k+stp*mod(N-k,N);
X(k-stp:-stp:max(diagLo,001)) = 8;
X(k+stp:+stp:min(diagHi,end)) = 8;
stp = N-1;
antiLo = k-stp*mod(N-k,N);
antiHi = k+stp*mod(k-1,N);
X(k-stp:-stp:max(antiLo,001)) = 8;
X(k+stp:+stp:min(antiHi,end)) = 8;
end
X
Giving:
X = % original matrix
0 0 0 0 0 0 0 0 1 0
0 1 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 0
0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1
1 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 0 0
0 0 1 0 0 0 0 0 0 0
X = % filled matrix
8 0 8 0 8 0 8 0 8 0
0 8 0 0 0 8 0 8 0 8
8 0 8 0 8 0 8 0 0 8
0 0 0 8 0 8 0 8 8 0
0 0 8 0 8 0 0 8 8 0
0 8 0 8 0 8 8 0 0 1
1 0 8 0 0 8 8 0 8 0
8 8 0 0 8 0 0 8 0 0
8 8 8 8 0 0 8 0 8 0
0 0 1 8 0 8 0 0 0 8
Note that while it might look at bit imposing, actually this this code is quite efficient and should scale well to larger matrix sizes: it does not create any large intermediate matrices (the four limit values are scalars), does not require expanding or enlarging the matrix, uses efficient linear indexing, and it iterates just once for each non-zero element in the input matrix.

Connectez-vous pour commenter.

Plus de réponses (4)

X0=logical(X);
[I,J]=find(X0);
[II,JJ]=ndgrid(1:N);
X=reshape(ismember(II(:)-JJ(:),I-J) + ismember(II(:)+JJ(:),I+J),N,N);
X(X>0)=8;
X(X0)=1;

2 commentaires

Hey is there anyway to adjust this so that if a 1 is found diagonally, it is also changed to an 8?
You mean that if two 1's lie on a common diagonal, they both become 8's?

Connectez-vous pour commenter.

Another option, with no issues near the edges. Requires the image processing toolbox:
se = min(1, eye(5) + fliplr(eye(5))); %The cross. Can be any pattern of 0s and 1s.
newX = 8 * imdilate(X, se); %replace each 1 by the se pattern and multiply by 8
newX(logical(X)) = 1; %set original 1s back to 1
s = size(X);
[~,d(:,2)] = spdiags(rot90(X));
[~,d(:,1)] = spdiags(X);
out = full(spdiags(ones(s(1),size(d,1)),d(:,1),s(1),s(2)) +...
rot90(spdiags(ones(s(2),size(d,1)),d(:,2),s(2),s(1)),-1));
k = max(out(:));
out(out < k & out > 0) = 8;
out(out == k) = 1;
or
[m,n] = size(X);
ii = find(X);
a = toeplitz(0:-1:1-m,0:n-1);
b = hankel(1:m,m + (0:n-1));
aa = a(ii);
bb = b(ii);
out = ((sum(reshape(aa,1,1,[]) == a,3) + ...
sum(reshape(bb,1,1,[]) == b,3)) > 0) + 7*X;
Yet another method:
[M,N] = size(X);
n = min(M,N);
XXX = repmat(X,3,3); % work with a big matrix so we don't have to worry about edge cases
[R,C] = find(X);
for k = 1:numel(R) % replace the diagonal spots with 8's in the big matrix
r = R(k) + M;
c = C(k) + N;
x = sub2ind(size(XXX),r+[-n:-1,1:n],c+[-n:-1,1:n]); % upper left to lower right diagonal
XXX(x) = 8;
x = sub2ind(size(XXX),r+[n:-1:1,-1:-1:-n],c+[-n:-1,1:n]); % lower left to upper right diagonal
XXX(x) = 8;
end
X = XXX(M+1:M+M,N+1:N+N); % extract the "original" matrix from the middle
Works for any rectangular matrix X, and overwrites all 1's that are on a diagonal of other 1's with 8's.

Catégories

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by