Construye la siguiente matriz simétrica
Mi mente está un poco nublada y no puedo identificar el patrón aquí que me permitirá escribir un código elegante para construir la siguiente matriz simétrica para un tamaño dado n:
Por ejemplo, si n=5:
{{0, f[1], f[6], f[9], f[5] },
{f[1], 0, f[2], f[7], f[10]},
{f[6], f[2], 0, f[3], f[8] },
{f[9], f[7], f[3], 0, f[4] },
{f[5], f[10], f[8], f[4], 0 }}
Y si n=9,
{{0, f[1], f[10], f[19], f[28], f[33], f[25], f[17], f[9] },
{f[1], 0, f[2], f[11], f[20], f[29], f[34], f[26], f[18]},
{f[10], f[2], 0, f[3], f[12], f[21], f[30], f[35], f[27]},
{f[19], f[11], f[3], 0, f[4], f[13], f[22], f[31], f[36]},
{f[28], f[20], f[12], f[4], 0, f[5], f[14], f[23], f[32]},
{f[33], f[29], f[21], f[13], f[5], 0, f[6], f[15], f[24]},
{f[25], f[34], f[30], f[22], f[14], f[6], 0, f[7], f[16]},
{f[17], f[26], f[35], f[31], f[23], f[15], f[7], 0, f[8] },
{f[9], f[18], f[27], f[36], f[32], f[24], f[16], f[8], 0 }}
¿Ves el patrón? La diagonal principal es cero. Luego, la diagonal secundaria se rellena en orden, seguida de la esquina superior derecha. Luego, se rellena la diagonal subprincipal, seguida de la diagonal superior derecha secundaria, y así sucesivamente ...
Respuestas
Aquí está mi modesto intento:
qdMat[n_Integer?Positive] := Module[{id, mm},
id = Riffle @@ Reverse[MapAt[Reverse, TakeDrop[Range[n - 1], Quotient[n - 1, 2]], -1]];
mm = TakeList[Array[f, Binomial[n, 2]], id][[InversePermutation[id]]];
mm = PadRight[PadLeft[Reverse[Flatten[mm, {{2}, {1}}], 2], {Automatic, n}], {n, n}];
mm + Transpose[mm]]
Por ejemplo,
qdMat[6]
{{0, f[1], f[7], f[13], f[11], f[6]},
{f[1], 0, f[2], f[8], f[14], f[12]},
{f[7], f[2], 0, f[3], f[9], f[15]},
{f[13], f[8], f[3], 0, f[4], f[10]},
{f[11], f[14], f[9], f[4], 0, f[5]},
{f[6], f[12], f[15], f[10], f[5], 0}}
qdMat[9]
{{0, f[1], f[10], f[19], f[28], f[33], f[25], f[17], f[9]},
{f[1], 0, f[2], f[11], f[20], f[29], f[34], f[26], f[18]},
{f[10], f[2], 0, f[3], f[12], f[21], f[30], f[35], f[27]},
{f[19], f[11], f[3], 0, f[4], f[13], f[22], f[31], f[36]},
{f[28], f[20], f[12], f[4], 0, f[5], f[14], f[23], f[32]},
{f[33], f[29], f[21], f[13], f[5], 0, f[6], f[15], f[24]},
{f[25], f[34], f[30], f[22], f[14], f[6], 0, f[7], f[16]},
{f[17], f[26], f[35], f[31], f[23], f[15], f[7], 0, f[8]},
{f[9], f[18], f[27], f[36], f[32], f[24], f[16], f[8], 0}}
Si determinamos los elementos en la primera fila, las filas restantes se obtienen por simple rotación + relleno y sumando 1 a la fila anterior rotada / rellenada:
ClearAll[firstRow, rotatePad, spiralMat]
firstRow = Module[{rng = Range[0, Floor[(# - 1)/2]]},
1 + Join[# If[OddQ @ #, Most @ rng, rng] , (# - 1) Reverse @ Rest @ rng]] &;
rotatePad = Fold[PadLeft, RotateRight[#, #2], Length[#] + {-#2, 1}] &;
spiralMat = MapIndexed[rotatePad[#, #2[[1]] - 1]&] @ NestList[#+1&, firstRow @ #, #-1]&;
Ejemplos:
Row[MatrixForm[spiralMat[#]] & /@ Range[5, 9], Spacer[5]]
MatrixForm[spiralMat[9] /. x_Integer?Positive :> f[x]]
MatrixForm[# + Transpose @ # & @ spiralMat[9] /. x_Integer?Positive :> f[x]]
Debido a que las diagonales cambian de la esquina a cerca de la diagonal, no hay una solución bonita (o al menos no fácil). El siguiente código hará el truco. Sin embargo, tendrás que hacer el triángulo inferior tú mismo.
n=9;
m = ConstantArray[0, {n, n}];
initialxLeft = 2;
initialxRight = n;
leftOrRightToggle = "Left";
counter = 1;
While[initialxLeft <= initialxRight,
If[leftOrRightToggle == "Left",
{x, y} = {initialxLeft, 1};
initialxLeft++;
leftOrRightToggle = "Right",
{x, y} = {initialxRight, 1};
initialxRight--;
leftOrRightToggle = "Left"];
While[x <= n,
m[[y, x]] = counter;
counter++;
x++;
y++;
];
];
m
$$\left( \begin{array}{ccccccccc} 0 & f[1] & f[10] & f[19] & f[28] & f[33] & f[25] & f[17] & f[9] \\ 0 & 0 & f[2] & f[11] & f[20] & f[29] & f[34] & f[26] & f[18] \\ 0 & 0 & 0 & f[3] & f[12] & f[21] & f[30] & f[35] & f[27] \\ 0 & 0 & 0 & 0 & f[4] & f[13] & f[22] & f[31] & f[36] \\ 0 & 0 & 0 & 0 & 0 & f[5] & f[14] & f[23] & f[32] \\ 0 & 0 & 0 & 0 & 0 & 0 & f[6] & f[15] & f[24] \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & f[7] & f[16] \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & f[8] \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{array} \right)$$
Qué hace: empezar con algún punto $(x,y)$. Mueva el punto a lo largo de una diagonal y use un contador para realizar un seguimiento del recuento actual. Cuando golpea el borde, vuelva a la fila superior. leftOrRightToggle realiza un seguimiento de si debe comenzar desde la diagonal principal o desde la esquina. initialxleft e initialxRight realizan un seguimiento de la posición x inicial para comenzar desde la diagonal principal o la esquina respectivamente. Una vez que initialxleft e initialxRight se encuentren, habrá terminado.