magic hypercubes
is.magichypercube.RdReturns TRUE if a hypercube is semimagic, magic, perfect
Usage
is.semimagichypercube(a, give.answers=FALSE, func=sum, boolean=FALSE, ...)
is.diagonally.correct(a, give.answers = FALSE, func=sum, boolean=FALSE, ...)
is.magichypercube(a, give.answers = FALSE, func=sum, boolean=FALSE, ...)
is.perfect(a, give.answers = FALSE, func=sum, boolean=FALSE)
is.latinhypercube(a, give.answers=FALSE)
is.alicehypercube(a,ndim,give.answers=FALSE, func=sum, boolean=FALSE)Arguments
- a
The hypercube (array) to be tested
- give.answers
Boolean, with
TRUEmeaning to also return the sums- func
Function to be applied across each dimension
- ndim
In
is.alicehypercube(), dimensionality of subhypercube to take sums over. See the details section- boolean
Boolean, with
TRUEmeaning that the hypercube is deemed magic, semimagic, etc, if all applications offuncevaluate toTRUE. IfbooleanisFALSE, the hypercube is magic etc if all applications offuncare identical- ...
Further arguments passed to
func()
Details
(Although apparently non-standard, here a hypercube is defined to have dimension \(d\) and order \(n\)—and thus has \(n^d\) elements).
A semimagic hypercube has all “rook's move” sums equal to the magic constant (that is, each \(\sum a[i_1,i_2,\ldots,i_{r-1},,i_{r+1}, \ldots,i_d]\) with \(1\leqslant r\leqslant d\) is equal to the magic constant for all values of the \(i\)'s). In
is.semimagichypercube(), ifgive.answersisTRUE, the sums returned are in the form of an array of dimensionc(rep(n,d-1),d). The firstd-1dimensions are the coordinates of the projection of the summed elements onto the surface hypercube. The last dimension indicates the dimension along which the sum was taken over.Optional argument
func, defaulting tosum(), indicates the function to be taken over each of theddimensions. Currently requiresfuncto return a scalar.A Latin hypercube is one in which each line of elements whose coordinates differ in only one dimension comprises the numbers \(1\) to \(n\) (or \(0\) to \(n-1\)), not necessarily in that order. Each integer thus appears \(n^{d-1}\) times.
A magic hypercube is a semimagic hypercube with the additional requirement that all \(2^{d-1}\) long (ie extreme point-to-extreme point) diagonals sum correctly. Correct diagonal summation is tested by
is.diagonally.correct(); by specifying a function other thansum(), criteria other than the diagonals returning the correct sum may be tested.An Alice hypercube is a different generalization of a semimagic square to higher dimensions. It is named for A. M. Hankin (“Alice”), who originally suggested it.
A semimagic hypercube has all one-dimensional subhypercubes (ie lines) summing correctly. An Alice hypercube is one in which all
ndim-dimensional subhypercubes have the same sum, wherendimis a fixed integer argument. Thus, ifais a hypercube of size \(n^d\),is.alicehypercube(a,ndim)returnsTRUEif alln^{d-ndim}subhypercubes have the same sum.For example, if
ais four-dimensional with dimension \(5\times 5\times 5\times 5\) thenis.alicehypercube(a,1)isTRUEif and only ifais a semimagic hypercube: all \({4\choose 1}5^3=500\) one-dimensional subhypercubes have the same sum. Thenis.alicehypercube(a,2)isTRUEif all 2-dimensional subhypercubes (ie all \({4\choose 2}\times 5^2=150\) of the \(5\times 5\) squares, for examplea[,2,4,]anda[1,1,,]) have the same sum. Thenis.alicehypercube(a,3)means that all 3d subhypercubes (ie all \({4\choose 3}\times 5^1=20\) of the \(5\times 5\times 5\) cubes, for examplea[,,1,]anda[4,,,]) have the same sum. For any hypercubea,is.alicehypercube(a,dim(a))returnsTRUE.A semimagic hypercube is an Alice hypercube for any value of
ndim.A perfect magic hypercube (use
is.perfect()) is a magic hypercube with all nonbroken diagonals summing correctly. This is a seriously restrictive requirement for high dimensional hypercubes. As yet, this function does not take agive.answersargument.A pandiagonal magic hypercube, also Nasik hypercube (or sometimes just a perfect hypercube) is a semimagic hypercube with all diagonals, including broken diagonals, summing correctly. This is not implemented.
The terminology in this area is pretty confusing.
In is.magichypercube(), if argument give.answers=TRUE
then a list is returned. The first element of this list is Boolean
with TRUE if the array is a magic hypercube. The second
element and third elements are answers
fromis.semimagichypercube() and is.diagonally.correct()
respectively.
In is.diagonally.correct(), if argument
give.answers=TRUE, the function also returns an array of
dimension c(q,rep(2,d)) (that is, \(q\times 2^d\)
elements), where \(q\) is the length of func() applied to a
long diagonal of a (if \(q=1\), the first dimension is
dropped). If \(q=1\), then in dimension d having index 1
means func() is applied to elements of a with the
\(d^{\rm th}\) dimension running over 1:n; index 2
means to run over n:1. If \(q>1\), the index of the first
dimension gives the index of func(), and subsequent dimensions
have indices of 1 or 2 as above and are interpreted in the same way.
An example of a function for which these two are not identical is given below.
If func=f where f is a function returning a vector of
length i, is.diagonally.correct() returns an array
out of dimension c(i,rep(2,d)), with
out[,i_1,i_2,...,i_d] being f(x) where x is the
appropriate long diagonal. Thus the \(2^d\) equalities
out[,i_1,i_2,...,i_d]==out[,3-i_1,3-i_2,...,3-i_d] hold if and
only if identical(f(x),f(rev(x))) is TRUE for each long
diagonal (a condition met, for example, by sum() but not by the
identity function or function(x){x[1]}).
References
R. K. S. Hankin 2005. “Recreational mathematics with R: introducing the magic package”. R news, 5(1)
Richards 1980. “Generalized magic cubes”. Mathematics Magazine, volume 53, number 2, (March).
Note
On this page, “subhypercube” is restricted to
rectangularly-oriented subarrays; see the note at subhypercubes.
Not all subhypercubes of a magic hypercube are necessarily magic! (for
example, consider a 5-dimensional magic hypercube a. The square
b defined by a[1,1,1,,] might not be magic: the diagonals
of b are not covered by the definition of a magic hypercube).
Some subhypercubes of a magic hypercube are not even semimagic: see
below for an example.
Even in three dimensions, being perfect is pretty bad. Consider a
\(5\times5\times 5\) (ie three dimensional), cube. Say
a=magiccube.2np1(2). Then the square defined by
sapply(1:n,function(i){a[,i,6-i]}, simplify=TRUE), which is a
subhypercube of a, is not even semimagic: the rowsums are
incorrect (the colsums must sum correctly because a is magic).
Note that the diagonals of this square are two of the “extreme
point-to-point” diagonals of a.
A pandiagonal magic hypercube (or sometimes just a perfect hypercube) is semimagic and in addition the sums of all diagonals, including broken diagonals, are correct. This is one seriously bad-ass requirement. I reckon that is a total of \(\frac{1}{2}\left( 3^d-1\right)\cdot n^{d-1}\) correct summations. This is not coded up yet; I can't see how to do it in anything like a vectorized manner.
Examples
library(abind)
is.semimagichypercube(magiccube.2np1(1))
#> [1] TRUE
is.semimagichypercube(magichypercube.4n(1,d=4))
#> [1] TRUE
is.perfect(magichypercube.4n(1,d=4))
#> [1] FALSE
# Now try an array with minmax(dim(a))==FALSE:
a <- abind(magiccube.2np1(1),magiccube.2np1(1),along=2)
is.semimagichypercube(a,g=TRUE)$rook.sums
#> [[1]]
#> [,1] [,2] [,3]
#> [1,] 42 42 42
#> [2,] 42 42 42
#> [3,] 42 42 42
#> [4,] 42 42 42
#> [5,] 42 42 42
#> [6,] 42 42 42
#>
#> [[2]]
#> [,1] [,2] [,3]
#> [1,] 84 84 84
#> [2,] 84 84 84
#> [3,] 84 84 84
#>
#> [[3]]
#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] 42 42 42 42 42 42
#> [2,] 42 42 42 42 42 42
#> [3,] 42 42 42 42 42 42
#>
# is.semimagichypercube() takes further arguments:
mymax <- function(x,UP){max(c(x,UP))}
not_mag <- array(1:81,rep(3,4))
is.semimagichypercube(not_mag,func=mymax,UP=80) # FALSE
#> [1] FALSE
is.semimagichypercube(not_mag,func=mymax,UP=81) # TRUE
#> [1] TRUE
a2 <- magichypercube.4n(m=1,d=4)
is.diagonally.correct(a2)
#> [1] TRUE
is.diagonally.correct(a2,g=TRUE)$diag.sums
#> , , 1, 1
#>
#> [,1] [,2]
#> [1,] 514 514
#> [2,] 514 514
#>
#> , , 2, 1
#>
#> [,1] [,2]
#> [1,] 514 514
#> [2,] 514 514
#>
#> , , 1, 2
#>
#> [,1] [,2]
#> [1,] 514 514
#> [2,] 514 514
#>
#> , , 2, 2
#>
#> [,1] [,2]
#> [1,] 514 514
#> [2,] 514 514
#>
## To extract corner elements (note func(1:n) != func(n:1)):
is.diagonally.correct(a2,func=function(x){x[1]},g=TRUE)$diag.sums
#> , , 1, 1
#>
#> [,1] [,2]
#> [1,] 1 13
#> [2,] 4 16
#>
#> , , 2, 1
#>
#> [,1] [,2]
#> [1,] 49 61
#> [2,] 52 64
#>
#> , , 1, 2
#>
#> [,1] [,2]
#> [1,] 193 205
#> [2,] 196 208
#>
#> , , 2, 2
#>
#> [,1] [,2]
#> [1,] 241 253
#> [2,] 244 256
#>
#Now for a subhypercube of a magic hypercube that is not semimagic:
is.magic(allsubhypercubes(magiccube.2np1(1))[[10]])
#> [1] FALSE
data(hendricks)
is.perfect(hendricks)
#> [1] TRUE
#note that Hendricks's magic cube also has many broken diagonals summing
#correctly:
a <- allsubhypercubes(hendricks)
ld <- function(a){length(dim(a))}
jj <- unlist(lapply(a,ld))
f <- function(i){is.perfect(a[[which(jj==2)[i]]])}
all(sapply(1:sum(jj==2),f))
#> [1] TRUE
#but this is NOT enough to ensure that it is pandiagonal (but I
#think hendricks is pandiagonal).
is.alicehypercube(magichypercube.4n(1,d=5),4,give.answers=TRUE)
#> $answer
#> [1] TRUE
#>
#> $alice.sums
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 131200 131200 131200 131200 131200
#> [2,] 131200 131200 131200 131200 131200
#> [3,] 131200 131200 131200 131200 131200
#> [4,] 131200 131200 131200 131200 131200
#>