Module magic_square
[hide private]
[frames] | no frames]

Source Code for Module magic_square

  1  # ============= 
  2  # magic_square_ 
  3  # =============  
  4  # 
  5  # *Simple operations with magic squares.* 
  6  # 
  7  # Copyright (c) 2007 `Alec Mihailovs`_ <alec@mihailovs.com> 
  8  #     All rights reserved. Licensed under the `MIT License`_ . 
  9  # 
 10  # .. _magic_square: http://mihailovs.com/Alec/Python/magic_square.html 
 11  # 
 12  # .. _`Alec Mihailovs`: http://mihailovs.com/Alec/  
 13  #      
 14  # .. _`MIT License`: http://opensource.org/licenses/mit-license.php 
 15  # 
 16  ######################################################################## 
 17  r""" 
 18  *Simple operations with magic squares.* 
 19   
 20  **Prerequisites:** 
 21      - NumPy_ 
 22   
 23  **Functions:** 
 24      - `ismagic(A)` -- test whether *A* is a magic square. 
 25      - `magic(N)` -- create an *N* by *N* magic square. 
 26      - `magic_constant(A)` -- calculate the magic constant of *A*. 
 27   
 28  **Examples:** 
 29      >>> from magic_square import * 
 30      >>> print magic(3) 
 31      [[8 1 6] 
 32       [3 5 7] 
 33       [4 9 2]] 
 34      >>> magic_constant() 
 35      15 
 36      >>> ismagic(magic(4)) 
 37      True 
 38      >>> magic_constant() 
 39      34 
 40      >>> magic_constant([1, 1, 1, 1]) 
 41      2 
 42      >>> ismagic([[1, 2], [3, 4]]) 
 43      False 
 44       
 45  **Notes:** 
 46      (1) Function `magic(N)` produces the same magic squares as Matlab  
 47      and Octave command ``magic(N)``.  The speed of calculations for *N*  
 48      close to 1000 is about 100--200 times faster than in Octave. 
 49   
 50      (2) Integer arithmetic in NumPy_ is done modulo ``2**32``.  That  
 51      can give a false positive in `ismagic(A)` for integer arrays with 
 52      overflowing in row, column, or diagonal sums.  To avoid that and to 
 53      avoid wrong answers in `magic_constant(A)`, in such cases the  
 54      array's ``dtype`` should be changed to ``'int64'``, or if  
 55      ``'int64'`` also overflows, to ``'object'``. 
 56   
 57  **Screenshots:**  
 58   
 59      That's how it looks in SAGE_: 
 60   
 61      |SAGE| 
 62   
 63      And that's how it looks in IDLE_: 
 64   
 65      |IDLE| 
 66   
 67  **Author:** 
 68      `Alec Mihailovs`_ <alec@mihailovs.com>  
 69   
 70  **Last Updated:** 
 71      February 22, 2007. 
 72   
 73  .. _NumPy: http://www.scipy.org/Download 
 74   
 75  .. _SAGE: http://sage.math.washington.edu/sage/ 
 76   
 77  .. _IDLE: http://www.python.org/idle/ 
 78   
 79  .. |SAGE| image:: sage.png  
 80   
 81  .. |IDLE| image:: idle.png 
 82   
 83  .. _`Alec Mihailovs`: http://mihailovs.com/Alec/ 
 84  """ 
 85   
 86  __version__ = "0.2" 
 87  """Development Status :: 3 - Alpha""" 
 88   
 89  __author__ = "Alec Mihailovs <alec@mihailovs.com>" 
 90  """ 
 91  `Alec Mihailovs`_ <alec@mihailovs.com> 
 92   
 93  .. _`Alec Mihailovs`: http://mihailovs.com/Alec/ 
 94  """ 
 95   
 96  __docformat__ = "restructuredtext" 
 97  """http://docutils.sourceforge.net/rst.html""" 
 98   
 99  from numpy import arange, asarray, flipud, r_, tile  
100  from math import sqrt 
101   
102  _constant = None    # to avoid an exception in magic_square._constant 
103  """Last calculated magic constant.""" 
104   
105 -def ismagic(A):
106 r""" 107 Test whether the given array is a magic square. 108 109 **Input:** 110 *A* -- 2D array, or a sequence that can be interpreted as such. 111 112 **Output:** 113 ``bool`` or ``NotImplementedType`` -- ``True`` if *A* is a 114 magic square, ``NotImplemented`` if the number of dimensions of 115 *A* is not 2 or 1, or the size is not a perfect square in the 116 1D case, and ``False`` otherwise. 117 118 **Examples:** 119 >>> from magic_square import * 120 >>> ismagic(magic(3)) 121 True 122 >>> ismagic([1, 1, 1, 1]) 123 True 124 >>> ismagic([[8, 1, 6], [3, 5, 7], [4, 9, 2]]) 125 True 126 >>> ismagic(1) # 0 dimensions 127 NotImplemented 128 >>> ismagic('[[1]]') # a string gives 0 dimensions 129 NotImplemented 130 >>> ismagic([[[1]]]) # 3 dimensions 131 NotImplemented 132 >>> ismagic(array([[1, 2], [3, 4]])) 133 False 134 135 **Notes:** 136 Integer arithmetic in NumPy_ is done modulo ``2**32`` as in the 137 following example: 138 139 >>> from numpy import array 140 >>> array([2**16]) 141 array([65536]) 142 >>> _*_ 143 array([0]) 144 145 That can give a false positive in `ismagic(A)` for integer 146 arrays with overflowing in row, column, or diagonal sums. 147 To avoid that, in such cases the array's ``dtype`` should be 148 changed to either ``'int64'``, or ``'object'``, see 149 `magic_constant(A)` Notes. 150 151 .. _NumPy: http://www.scipy.org/Download 152 """ 153 global _constant 154 _constant = None # may be commented out if desirable 155 a = asarray(A) 156 if a.ndim == 2: 157 m = flipud(a).trace() 158 t = (r_[a.sum(axis=0), a.sum(axis=1), a.trace()] == m).all() 159 if t == True: # not "is" because t is a NumPy boolean 160 _constant = m 161 return True # not "return t", 162 else: # to make sure that 163 return False # the return value is of the bool type 164 elif a.ndim == 1: 165 s = sqrt(a.size) 166 if a.size == s*s: 167 return ismagic(a.reshape(s,s)) 168 else: 169 return NotImplemented 170 else: 171 return NotImplemented
172
173 -def magic_constant(A=None):
174 r""" 175 Magic constant of the magic square. 176 177 **Input:** 178 *A* -- 2D array, or a sequence that can be interpreted as such. 179 If not entered, the last constructed `magic(n)` or last array 180 *A* tested in `ismagic(A)` is used. 181 182 **Output:** 183 ``dtype`` of the array, or Python ``long``, or ``NoneType`` -- 184 the magic constant if the array is a magic square, or ``None`` 185 otherwise. Python ``long`` can occur if *A* is ``None`` and the 186 magic constant is calculated for the last constructed 187 `magic(n)` with large *n*. 188 189 **Examples:** 190 >>> from magic_square import * 191 >>> magic_constant([1, 1, 1, 1]) 192 2 193 >>> print magic_constant([1, 2, 3, 4]) 194 None 195 >>> ismagic(magic(6)) 196 True 197 >>> magic_constant() 198 111 199 >>> a = magic(5000) 200 >>> magic_constant() 201 62500002500L 202 203 **Notes:** 204 Integer arithmetic in NumPy_ is done modulo ``2**32``. That 205 makes `magic_constant(A)` to return wrong answers for integer 206 arrays with overflowing in row, column, or diagonal sums. For 207 example, 208 209 >>> magic_constant(magic(5000)) 210 -1924506940 211 >>> ismagic(magic(5000)) 212 True 213 >>> magic_constant() 214 -1924506940 215 216 Note that 217 218 >>> 62500002500L % 2**32 == -1924506940 % 2**32 219 True 220 221 To avoid such wrong answers, the array's ``dtype`` can be 222 changed to ``'int64'``, or if ``'int64'`` also overflows, to 223 ``'object'`` (that one significantly slows down the 224 calculations.) In this example, 225 226 >>> from numpy import array 227 >>> magic_constant(array(magic(5000), dtype='int64')) 228 62500002500 229 >>> magic_constant(array(magic(5000), dtype='object')) # long 230 62500002500L 231 232 .. _NumPy: http://www.scipy.org/Download 233 """ 234 if A is None or ismagic(A) is True: # avoiding NotImplemented 235 return _constant
236
237 -def magic(N):
238 r""" 239 Create an *N* by *N* magic square. 240 241 **Input:** 242 *N* -- an integer in some form, may be float or quotted. 243 244 **Output:** 245 an ``'int32'`` *N* by *N* array -- the same magic square as in 246 Matlab and Octave ``magic(N)`` commands. In particular, the 247 Siamese method is used for odd *N* (but with a different 248 implementation.) 249 250 **Examples:** 251 >>> from magic_square import * 252 >>> magic(4) 253 array([[16, 2, 3, 13], 254 [ 5, 11, 10, 8], 255 [ 9, 7, 6, 12], 256 [ 4, 14, 15, 1]]) 257 >>> magic_constant() 258 34 259 >>> magic(5.0) # can be float 260 array([[17, 24, 1, 8, 15], 261 [23, 5, 7, 14, 16], 262 [ 4, 6, 13, 20, 22], 263 [10, 12, 19, 21, 3], 264 [11, 18, 25, 2, 9]]) 265 >>> print magic('6') # can be quotted 266 [[35 1 6 26 19 24] 267 [ 3 32 7 21 23 25] 268 [31 9 2 22 27 20] 269 [ 8 28 33 17 10 15] 270 [30 5 34 12 14 16] 271 [ 4 36 29 13 18 11]] 272 >>> magic(2) # consistent with Octave 273 Traceback (most recent call last): 274 TypeError: No such magic squares exist. 275 >>> magic(0) 276 array([], shape=(0, 0), dtype=int32) 277 >>> magic_constant() # the empty sum is 0 278 0 279 280 **Notes:** 281 The calculations for *n* close to 1000 are about 100--200 282 times faster than in Octave. 283 """ 284 global _constant 285 n = int(N) 286 if n < 0 or n == 2: # consistent with Octave 287 raise TypeError("No such magic squares exist.") 288 elif n%2 == 1: 289 m = n>>1 290 b = n*n + 1 291 _constant = n*b>>1 292 return (tile(arange(1,b,n),n+2)[m:-m-1].reshape(n,n+1)[...,1:]+ 293 tile(arange(n),n+2).reshape(n,n+2)[...,1:-1]).transpose() 294 elif n%4 == 0: 295 b = n*n + 1 296 _constant = n*b>>1 297 d=arange(1, b).reshape(n, n) 298 d[0:n:4, 0:n:4] = b - d[0:n:4, 0:n:4] 299 d[0:n:4, 3:n:4] = b - d[0:n:4, 3:n:4] 300 d[3:n:4, 0:n:4] = b - d[3:n:4, 0:n:4] 301 d[3:n:4, 3:n:4] = b - d[3:n:4, 3:n:4] 302 d[1:n:4, 1:n:4] = b - d[1:n:4, 1:n:4] 303 d[1:n:4, 2:n:4] = b - d[1:n:4, 2:n:4] 304 d[2:n:4, 1:n:4] = b - d[2:n:4, 1:n:4] 305 d[2:n:4, 2:n:4] = b - d[2:n:4, 2:n:4] 306 return d 307 else: 308 m = n>>1 309 k = m>>1 310 b = m*m 311 d = tile(magic(m), (2,2)) # that changes the _constant 312 _constant = _constant*8 - n - m 313 d[:m, :k] += 3*b 314 d[m:,k:m] += 3*b 315 d[ k, k] += 3*b 316 d[ k, 0] -= 3*b 317 d[m+k, 0] += 3*b 318 d[m+k, k] -= 3*b 319 d[:m,m:n-k+1] += b+b 320 d[m:,m:n-k+1] += b 321 d[:m, n-k+1:] += b 322 d[m:, n-k+1:] += b+b 323 return d
324 325 ################################################################## 326 # Python 2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit 327 # (Intel)] on win32 328 # 329 # >>> from magic_square import * 330 # >>> from time import clock 331 # >>> t=clock(); a=magic(1000); clock()-t 332 # 0.0191592494101839 333 # >>> t=clock(); a=magic(1001); clock()-t 334 # 0.018718461322123403 335 # >>> t=clock(); a=magic(1002); clock()-t 336 # 0.027449660797152831 337 # >>> t=clock(); ismagic(a); clock()-t 338 # True 339 # 0.021589410496389405 340 ################################################################# 341 # $ ipython 342 # Python 2.5 (r25:51908, Jan 11 2007, 22:47:00) 343 # IPython 0.7.3.svn -- An enhanced Interactive Python. 344 # 345 # In [1]: from magic_square import * 346 # 347 # In [2]: time a=magic(1000) 348 # CPU times: user 0.02 s, sys: 0.00 s, total: 0.02 s 349 # Wall time: 0.02 350 # 351 # In [3]: time a=magic(1001) 352 # CPU times: user 0.00 s, sys: 0.01 s, total: 0.01 s 353 # Wall time: 0.02 354 # 355 # In [4]: time a=magic(1002) 356 # CPU times: user 0.00 s, sys: 0.02 s, total: 0.02 s 357 # Wall time: 0.03 358 # 359 # In [5]: time ismagic(a) 360 # CPU times: user 0.01 s, sys: 0.00 s, total: 0.01 s 361 # Wall time: 0.02 362 ################################################################ 363 # $ octave 364 # GNU Octave, version 2.1.73 (i686-pc-cygwin). 365 # 366 # octave:1> t=cputime();a=magic(1000);cputime()-t 367 # ans = 2 368 # octave:2> t=cputime();a=magic(1001);cputime()-t 369 # ans = 4.1410 370 # octave:3> t=cputime();a=magic(1002);cputime()-t 371 # ans = 4.9840 372 ################################################################ 373