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$
  • $\begingroup$ You mean without Reverse, right? $\endgroup$ – Szabolcs Mar 1 '14 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. Mar 1 '14 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 Mar 1 '14 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. Mar 1 '14 at 21:34
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])]

| improve this answer | |
$\endgroup$
  • $\begingroup$ Ahh... come on. One minute faster?? $\endgroup$ – halirutan Mar 1 '14 at 21:37
  • $\begingroup$ @halirutan 1 minute and 53 seconds! :D $\endgroup$ – Szabolcs Mar 1 '14 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]

| improve this answer | |
$\endgroup$
  • $\begingroup$ +1, had same idea, I think it quite elegant since it can be easily generalized to other "shapes". Well done. $\endgroup$ – ciao Mar 1 '14 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]
| improve this answer | |
$\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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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