Produce Dürer's magic square
Pyth, 18 bytes
c4.PC"H#ût"_S16
Run the code.
c4.PC"H#ût"_S16
C"H#ût" Convert the packed string to the number 1122196781940
.P _S16 Take that-numbered permutation of the reversed range [16,15,...,1]
c4 Chop into piece of length 4
Reversing the range was meant to lower the permutation index, since the output starts with 16, but I think it only broke even.
This beat out a more boring strategy of converting the table directly to base 17 and then a string (link) for 20 bytes:
c4jC"úz(ás¸H"17
Jelly, 16 15 bytes
4Œ!.ịm0µZḂÞ’×4+
Try it online!
Background
If we subtract 1 from the numbers in the square and divide them by 4 (computing quotient and remainder), a pattern becomes apparent.
quotients and remainders quotients remainders
3 3 0 2 0 1 3 0 3 0 0 3 3 2 1 0
1 0 2 1 2 2 1 3 1 2 2 1 0 1 2 3
2 0 1 1 1 2 2 3 2 1 1 2 0 1 2 3
0 3 3 2 3 1 0 0 0 3 3 0 3 2 1 0
The remainder matrix follows an obvious pattern and is easy to generate. The quotient matrix can be obtained by transposing the remainder matrix and swapping the middle rows.
How it works
4Œ!.ịm0µZḂÞ’×4+ Main link. No arguments.
4Œ! Compute the array of all permutations of [1, 2, 3, 4], in
lexicographical order.
.ị Take the permutations at the indices adjacent to 0.5, i.e., the
ones at indices 0 ([4, 3, 2, 1]) and 1 ([1, 2, 3, 4]).
m0 Concatenate the resulting [[4, 3, 2, 1], [1, 2, 3, 4]] with a
reversed copy, yielding the matrix
M := [[4, 3, 2, 1], [1, 2, 3, 4], [1, 2, 3, 4], [4, 3, 2, 1]].
µ Begin a new, monadic chain. Argument: M
Z Zip/transpose M, yielding the matrix
[[4, 1, 1, 4], [3, 2, 2, 3], [2, 3, 3, 2], [1, 4, 4, 1]].
ḂÞ Sort the rows by the lexicographical order of their parities,
yielding [[4, 1, 1, 4], [2, 3, 3, 2], [3, 2, 2, 3], [1, 4, 4, 1]].
’ Subtract 1 to yield the matrix of quotients, i.e.,
[[3, 0, 0, 3], [1, 2, 2, 1], [2, 1, 1, 2], [0, 3, 3, 0]].
×4+ Multiply the quotient by 4 and add the result to M (remainders).
Jelly, 15 bytes
“¡6ṡƘ[²Ḳi<’ḃ⁴s4
TryItOnline!
Pretty boring, sorry:
Prep: Took the square, read it by rows, converted from bijective base 16, convert that to base 250, looked up the code page indexes for those "digits" (¡6ṡƘ[²Ḳi<
).
Jelly then reads the indexes to make a base 250 number, converts to bijective base 16 (ḃ⁴
) and splits into chunks of size 4 (s4
).
If we are allowed to output a different orientation, upside-down is possible in 14:
“#⁷ƙ¤ṆWȷỤ’ḃ⁴s4
Test
In theory, given enough memory for 16!
integers the following would give us the correct orientation in 14:
⁴Œ!“ŒCġŀḌ;’ịs4
This would create all permutations of [1,16] with ⁴Œ!
and pick the value at index 19800593106060 (1-based) with ị
using the base 250 representation ŒCġŀḌ;
and split it into chunks of length 4 with s4
.
Since then I have added four new atoms (Œ?
, Œ¿
, œ?
, and œ¿
) to Jelly to address such situations.
The monad Œ?
takes an integer (or iterable of integers) and returns the shortest possible permutation of running natural numbers which would have the given index (or indexes) in a list of lexicographically sorted list of all permutations of those numbers.
...and it does so without creating any permutation lists.
As such the following would now work for 12 (obviously non-competing):
“ŒCġŀḌ;’Œ?s4
Give It A Go!