n階幻方的填法(n≥3)

from:
http://www.91linux.com/html/article/program/cpp/20090824/17854.html 

幻方,亦稱縱橫圖。臺灣稱爲魔術方陣。將自然數1,2,3,……n*n排列成一個n*n方陣,使得每行、每列以及兩對角線上的各個數之和都相等,等於n/2*(n*n+1),這樣的方陣稱爲幻方。
例如:把1,2,3,4,5,6,7,8,9填入3*3的格子,使得:每行、每列、兩條對角線的和是15。

8 1 6
3 5 7
4 9 2

n是它的階數,比如上面的幻方是3階。n/2*(n*n+1)爲幻方的變幻常數。數學上已經證明,對於n>2,n階幻方都存在。
目前填寫幻方的方法,是把幻方分成了三類,每類又有各種各樣的填寫方法。這裏對於這三類幻方,僅舉出一種方便手工填寫的方法。

1、奇數階幻方
n爲奇數 (n=3,5,7,9,11……) (n=2*k+1,k=1,2,3,4,5……)

奇數階幻方最經典的填法是羅伯特法(也有人稱之爲樓梯方)。填寫方法是這樣:

把1(或最小的數)放在第一行正中; 按以下規律排列剩下的n*n-1個數: 
(1)、每一個數放在前一個數的右上一格;
(2)、如果這個數所要放的格已經超出了頂行那麼就把它放在底行,仍然要放在右一列; 
(3)、如果這個數所要放的格已經超出了最右列那麼就把它放在最左列,仍然要放在上一行;
(4)、如果這個數所要放的格已經超出了頂行且超出了最右列,那麼就把它放在前一個數的下一行同一列的格內; 
(5)、如果這個數所要放的格已經有數填入,處理方法同(4)。

這種寫法總是先向“右上”的方向,象是在爬樓梯。


2、雙偶階幻方
n爲偶數,且能被4整除 (n=4,8,12,16,20……) (n=4k,k=1,2,3,4,5……)

先說明一個定義:
互補:如果兩個數字的和,等於幻方最大數和最小數的和,即 n*n+1,稱爲互補。

先看看4階幻方的填法:將數字從左到右、從上到下按順序填寫:

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

這個方陣的對角線,已經用藍色標出。將對角線上的數字,換成與它互補的數字。
這裏,n*n+1 = 4*4+1 = 17;
把1換成17-1 = 16;把6換成17-6 = 11;把11換成17-11 = 6……換完後就是一個四階幻方。

16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1

對於n=4k階幻方,我們先把數字按順序填寫。寫好後,按4*4把它劃分成k*k個方陣。因爲n是4的倍數,一定能用4*4的小方陣分割。然後把每個小方陣的對角線,象製作4階幻方的方法一樣,對角線上的數字換成互補的數字,就構成幻方。 下面是8階幻方的作法:
(1) 先把數字按順序填。然後,按4*4把它分割成2*2個小方陣

1 2 3 4 5 6 7 8
9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32
33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48
49 50 51 52 53 54 55 56
57 58 59 60 61 62 63 64

(2) 每個小方陣對角線上的數字,換成和它互補的數。

64 2 3 61 60 6 7 57
9 55 54 12 13 51 50 16
17 47 46 20 21 43 42 24
40 26 27 37 36 30 31 33
32 34 35 29 28 38 39 25
41 23 22 44 45 19 18 48
49 15 14 52 53 11 10 56
8 58 59 5 4 62 63 1

3、單偶階幻方
n爲偶數,且不能被4整除 (n=6,10,14,18,22……) (n=4k+2,k=1,2,3,4,5……)
這是三種裏面最複雜的幻方。

以n=10爲例。這時,k=2

(1) 把方陣分爲A,B,C,D四個象限,這樣每一個象限肯定是奇數階。用樓梯法,依次在A象限,D象限,B象限,C象限按奇數階幻方的填法填數。

                   
                   
    A         B    
                   
                   
                   
                   
    C         D    
                   
                   

 

17 24 1 8 15 67 74 51 58 65
23 5 7 14 16 73 55 57 64 66
4 6 13 20 22 54 56 63 70 72
10 12 19 21 3 60 62 69 71 53
11 18 25 2 9 61 68 75 52 59
92 99 76 83 90 42 49 26 33 40
98 80 82 89 91 48 30 32 39 41
79 81 88 95 97 29 31 38 45 47
85 87 94 96 78 35 37 44 46 28
86 93 100 77 84 36 43 50 27 34

(2) 在A象限的中間行、中間格開始,按自左向右的方向,標出k格。A象限的其它行則標出最左邊的k格。


>>>

17 24 1 8 15 67 74 51 58 65
23 5 7 14 16 73 55 57 64 66
4 6 13 20 22 54 56 63 70 72
10 12 19 21 3 60 62 69 71 53
11 18 25 2 9 61 68 75 52 59
92 99 76 83 90 42 49 26 33 40
98 80 82 89 91 48 30 32 39 41
79 81 88 95 97 29 31 38 45 47
85 87 94 96 78 35 37 44 46 28
86 93 100 77 84 36 43 50 27 34

(3) 將這些格,和C象限相對位置上的數,互換位置。

92 99 1 8 15 67 74 51 58 65
98 80 7 14 16 73 55 57 64 66
4 6 88 95 22 54 56 63 70 72
85 87 19 21 3 60 62 69 71 53
86 93 25 2 9 61 68 75 52 59
17 24 76 83 90 42 49 26 33 40
23 5 82 89 91 48 30 32 39 41
79 81 13 20 97 29 31 38 45 47
10 12 94 96 78 35 37 44 46 28
11 18 100 77 84 36 43 50 27 34

(4) 在B象限任一行的中間格,自右向左,標出k-1列。(注:6階幻方由於k-1=0所以不用再作B、D象限的數據交換)

             <<<   

92 99 1 8 15 67 74 51 58 65
98 80 7 14 16 73 55 57 64 66
4 6 88 95 22 54 56 63 70 72
85 87 19 21 3 60 62 69 71 53
86 93 25 2 9 61 68 75 52 59
17 24 76 83 90 42 49 26 33 40
23 5 82 89 91 48 30 32 39 41
79 81 13 20 97 29 31 38 45 47
10 12 94 96 78 35 37 44 46 28
11 18 100 77 84 36 43 50 27 34

(5) 將B象限標出的這些數,和D象限相對位置上的數進行交換,即可完成。

 

92 99 1 8 15 67 74 26 58 65
98 80 7 14 16 73 55 32 64 66
4 6 88 95 22 54 56 38 70 72
85 87 19 21 3 60 62 44 71 53
86 93 25 2 9 61 68 50 52 59
17 24 76 83 90 42 49 51 33 40
23 5 82 89 91 48 30 57 39 41
79 81 13 20 97 29 31 63 45 47
10 12 94 96 78 35 37 69 46 28
11 18 100 77 84 36 43 75 27 34
 
下面是我寫的3(2n+1),和4(4n)得,2n不是4n的沒寫
 
 

#include <stdio.h>
#include <stdlib.h>

#define N 3

void count_n(int n, int A[N][N])
{
  int i = 0;
  int j = n/2;
  int tmp_i = 0;
  int tmp_j = 0;
  int count = 1;
  int largest = n*n;
  
  while(count<=largest)
   {
     if(A[i][j]==0)
     {
       A[i][j] = count;
       count++;
       tmp_i = i;
       tmp_j = j;
       i = (n+i-1)%n;//右上方

       j = (j+1)%n;
     }
     else
     {
       i = (tmp_i+1)%n;//右上方有值了,就取正下方

       j = tmp_j;
     }
   }
}

int main(int argc, char *argv[])
{
  int i;
  int j;
  int A[N][N];
  
  for(i=0;i<N;i++)
   for(j=0;j<N;j++)
     A[i][j] = 0;
      
  count_n(N, A);
  
  for(i=0;i<N;i++)
  {
   for(j=0;j<N;j++)
     printf("%d/t",A[i][j]);
   printf("/n");
  }
  
  system("PAUSE");    
  return 0;
}

 

 

 

#include <stdio.h>
#include <stdlib.h>

#define N 4
 
void count_n(int n, int A[N][N])
{
  int i;
  int j;
  int total = n*n +1;
  
  for(i=0;i<N;i++)
    for(j=0;j<N;j++)
     {
      A[i][j] = 4*i+j+1;
      if(i==j || i+j== n-1)
       A[i][j] = total - A[i][j];
     }
}

int main(int argc, char *argv[])
{
   int i;
   int j;
  int A[N][N];
  
  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章