10
$\begingroup$

I have a matrix like this:

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

matrix

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.)

$\endgroup$
4
  • $\begingroup$ You mean without Reverse, right? $\endgroup$
    – Szabolcs
    Commented Mar 1, 2014 at 20:50
  • $\begingroup$ @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. $\endgroup$
    – C. E.
    Commented Mar 1, 2014 at 20:55
  • $\begingroup$ 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. $\endgroup$
    – Szabolcs
    Commented Mar 1, 2014 at 21:33
  • $\begingroup$ @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. $\endgroup$
    – C. E.
    Commented Mar 1, 2014 at 21:34

5 Answers 5

8
$\begingroup$

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])]

$\endgroup$
2
  • $\begingroup$ Ahh... come on. One minute faster?? $\endgroup$
    – halirutan
    Commented Mar 1, 2014 at 21:37
  • $\begingroup$ @halirutan 1 minute and 53 seconds! :D $\endgroup$
    – Szabolcs
    Commented Mar 1, 2014 at 21:40
11
$\begingroup$

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]

$\endgroup$
1
  • $\begingroup$ +1, had same idea, I think it quite elegant since it can be easily generalized to other "shapes". Well done. $\endgroup$
    – ciao
    Commented Mar 1, 2014 at 22:08
7
$\begingroup$

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]
$\endgroup$
5
$\begingroup$

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}]},"  "]

enter image description here

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 *)

enter image description here

$\endgroup$
3
$\begingroup$

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

Mathematica graphics

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}} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.