# Take both of the diagonals through an element in a matrix

I have a matrix like this:

Reverse@Array[List, {8, 8}] // MatrixForm


I need a function that given an element of this matrix will return all the elements corresponding to the two diagonals that run through it.

The first diagonal can be found like this:

Diagonal[matrix, j-i]


where {i,j} is the element. How can I get the diagonal that runs orthogonally to this through the same given element {i,j}?

(It is somewhat related to this question but my problem is more that I can't figure out which diagonal I want once I've reversed the matrix.)

-
You mean without Reverse, right? – Szabolcs Mar 1 '14 at 20:50
@Szabolcs There is a reason why my matrix is reversed but I will gladly accept answers that use a matrix that wasn't reversed to begin with. – C. E. Mar 1 '14 at 20:55
Sorry for being unclear. I was asking, are you looking for answers which don't use Reverse? I posted an answer which does use reverse. – Szabolcs Mar 1 '14 at 21:33
@Szabolcs Your solution is fine, actually it is the same kind of solution I attempted but I somehow never got it to work. Look forward to my +1. – C. E. Mar 1 '14 at 21:34

As you said, to get the main diagonal through $(i,j)$, you use

Diagonal[mat, j - i]


To get the other one, you can use

Diagonal[Reverse[mat, 2], (Dimensions[mat][[2]] - j + 1) - i]


Check correctness visually:

{i, j} = {3, 5}
MatrixForm[mat /. ((# -> Style[#, Red]) & /@ Diagonal[mat, j - i])]
MatrixForm[
mat /. ((# -> Style[#, Red]) & /@
Diagonal[Reverse[mat, 2], (Dimensions[mat][[2]] - j + 1) - i])]


-
Ahh... come on. One minute faster?? – halirutan Mar 1 '14 at 21:37
@halirutan 1 minute and 53 seconds! :D – Szabolcs Mar 1 '14 at 21:40

The relation for the indices of matrix diagonals is easy to infer, and you can observe this from the following image:

If $A_{i_0,j_0}=A_{5,3}$ is the chosen element, then the diagonal elements have indices satisfying $i-j = i_0-j_0$ and the skew diagonal elements have indices satisfying $i+j=i_0+j_0$.

The problem is now almost solved! We just feed these exact rules to SparseArray:

Clear@orthoDiags
orthoDiags[{p_, q_}, n_] :=
SparseArray[{
{i_, j_} /; j + i == p + q :> 1,
{i_, j_} /; i - j == p - q :> 1},
{n, n}
]


and use Pick to extract the elements:

Flatten@Pick[mat, orthoDiags[{5, 3}, 8], 1]


-
+1, had same idea, I think it quite elegant since it can be easily generalized to other "shapes". Well done. – ciao Mar 1 '14 at 22:08

There are many solutions. I like one based on FrobeniusSolve (see e.g. this answer How to find lattice points on a line segment?):

d[i_Integer, j_Integer] /; i > 0 && j > 0 :=
DeleteCases[ FrobeniusSolve[{1, 1}, i + j], {___, 0, ___}]


Then

matrix[[#1, #2]]& @@@ d[3, 4]

{{8, 6}, {7, 5}, {6, 4}, {5, 3}, {4, 2}, {3, 1}}


yields that second diagonal orthogonal to Diagonal[matrix, 4 - 3].
But as far as you work with matrices like matrix you needn't use neiter Apply nor Part, one has only to put adequate initial values (equations) to FrobeniusSolve.

As an alternative to d function one can use diag based on Array:

diag[i_, j_] := Array[{# + i, j - #} &, i + j - 1, -i + 1]

-

May late entry (called away). Short and sweet I think.

Edit: Ooops, did not read OP closely enough - this works for nXm shapes, not nXmXo... I'll leave answer for searchers with the former unless OP wants removal. See update below...

xDiag[mat_, {er_, ec_}] :=
SparseArray[{r_, c_} :> mat[[r, c]] /; Abs[r - er] == Abs[c - ec],
Dimensions[mat]]["NonzeroValues"]

Row[{(test = Table[10 x + y, {x, 1, 5}, {y, 1, 5}]) // MatrixForm
, xDiag[test, {2, 3}]},"  "]


After discovering the flaw in the above (shape limited to nXm, and fails with zero elements in diagonals) came up with one I like even better. It is shape agnostic, that is, it works with any array shape treating "diagonals" at the top level:

(* cross diagonals *)
Diag2[mat_, {er_, ec_}] :=
Join @@ Cases[
ReplacePart[mat, {r_, c_, ___} /; Abs[r - er] != Abs[c - ec] -> Sequence[]], {__}, {1}]

(* tests *)
Column[{Column[{(test1 = Table[10 x + y, {x, 1, 5}, {y, 1, 5}]) //
MatrixForm, xDiag2[test1, {3, 2}]}, Left, 1],
Column[{(test2 = Array[List, {5, 5}]) // MatrixForm,
xDiag2[test2, {3, 2}]}, Left, 1]
Column[{(test3 = Array[List, {5, 5, 2}]) // MatrixForm,
xDiag2[test3, {3, 2}]}, Left, 1]}, Left, 2]

(* results *)


-

As far as I can see, using Diagonal it should be something like this:

extract[m_, {i_, j_}] :=
{Diagonal[m, j - i], Diagonal[Reverse[m], (i + j) - (Length[m] + 1)]}


And then:

m = Array[List, {8, 8}];
m // MatrixForm


you have:

extract[m,{6,2}]//Column
(* {{5,1},{6,2},{7,3},{8,4}}
{{7,1},{6,2},{5,3},{4,4},{3,5},{2,6},{1,7}} *)

-