本篇文章只對奇數階以及偶數階中階數n = 4K的魔方陣進行討論.下面就讓我們進入正題:
1 :魔方陣的相關資訊:(百度百科)
https://baike.baidu.com/item/%E9%AD%94%E6%96%B9%E9%98%B5/10973743?fr=aladdin
2 :奇數階和偶數階魔方陣的排列規律.(源自百度百科) (可跳至第三部分)
2.1 :奇數階魔方陣的排列規律如下:
⑴ :將1放在第一行中間一列;
⑵ :從2開始直到n×n止各數依次按下列規則存放;每一個數存放的行比前一個數的行數減1,列數加1(例如上面的三階魔方陣,5在4的上一行後一列;
⑶ :如果上一個數的行數為1,則下一個數的行數為n(指最下一行);例如1在第一行,則2應放在最下一行,列數同樣加1;
⑷ :當上一個數的列數為n時,下一個數的列數應為1,行數減去1。例如2在第3行最後一列,則3應放在第二行第一列;
⑸ :如果按上面規則確定的位置上已有數,或上一個數是第一行第n列時,則把下一個數放在上一個數的下面。例如按上面的規定,4應該放在第1行第2列,
但該位置已經被佔據,所以4就放在3的下面。
2.2 :階數為n = 4K 的魔方陣的排列規律如下:
(1) :先將整個方陣劃分成k*k個4階方陣,然後在每個4階方陣的對角線上做記號;
(2) :由左而右、由上而下,遇到沒有記號的位置才填數字,但不管是否填入數字,每移動一格數字都要加1;
(3) :自右下角開始,由右而左、由下而上,遇到沒有數字的位置就填入數字,但每移動一格數字都要加1。
3 :排列規律的解讀,程式碼實現以及實驗結果( ar 二維陣列 ,ROW ==行, COL == 列)
3.1 :奇數階魔方陣(n = 2*K + 1)
奇數階魔方陣的排列規律主要有以下三點:
3.1.1) : 數字1的位置是確定的,擺放在第一行中間的位置,即: ar[0][COL/2] == 1;
3.1.2)(重點) :假設當前擺放的數行列下標為 [preRow][preCol] , 則下一個數的擺放的位置的下標應為[preRow - 1] [preCol + 1] ,注意在這裡(preRow - 1)
以及(preCol + 1)的值都有可能越界. 因此,需要對變化後的值進行操作.我們以ROW=COL=3為例,假設上一個值的下標為 [0][3] ,
那麼下一個值的下標應為[2][1] ,他們與(preRow - 1)以及(preCol + 1)
即 [0 - 1][3 + 1]的關係,為[(-1 + 3)%3] [(3+1+3)%3], 即[(preRow - 1+ ROW)%ROW][(preCol + 1+ COL)%COL].
圖示:
,
2的擺放位置的下標應為[(0 -1 + 3)%3][(1+1)%3] == [2][2] ,
即:
3.1.3) : 若將要擺放的位置已有值存在,則將該值排放在上一個值的下一行.
例如:以三階魔方陣為例: 按照相應的規則擺放1,2,3
,
要擺放4的位置的下標通過計算可以得出是[0][1],但是該位置已有數值1,因此4要擺放在3的下一行,即[preRow - 1][preCol],
同時用3.1.2的方法對[preRow-1]進行 防越界處理: [(preRow - 1 + ROW)%ROW][preCol].
程式碼如下:
#include<assert.h> #include<stdio.h> void Magic_Square_1() { #define ROW 3 #define COL ROW // 等價於 #define COL 3 assert(ROW % 2 != 0); if (ROW % 2 == 0)//ROW & ! == 1 判斷是否為奇數 &(只有全為1,才為1) { return; } int preRow = 0; //記錄上一個數的行座標 int preCol = 0; //記錄上一個數的列座標 int ar[ROW][COL] = {}; ar[0][COL / 2] = 1; //先放1 preRow = 0; preCol = COL / 2; for (int i = 2; i <= ROW * COL; i++)//1已經放入所以從2開始進行 { if (ar[(preRow - 1 + ROW) % ROW][(preCol + 1) % COL] == 0) //判斷上一個數的上一行下一列是否有值若沒有則放入當前的i { preRow = (preRow - 1 + ROW) % ROW; preCol = (preCol + 1) % COL; } else { // 若有則i放在上一個數的下一行. preRow = (preRow + 1) % ROW; } ar[preRow][preCol] = i; } for (int i = 0; i < ROW; i++)//列印 { for (int j = 0; j < COL; j++) { printf("%3d", ar[i][j]); } printf("\n"); } #undef ROW //取消定義 #undef COL }
int main()
{
Maagic_Square_1();
return 0;
}
執行結果:
3.2 :偶數階魔方陣( n = 4*K )
偶數階魔方陣的排列規律主要有以下兩點:(以8階為例)
3.2.1) : 先將 1 - ROW*COL 的值按從上到下,從左到右的順序依次填入.接著將魔方陣分成k*k (在該例 k = 2)個4階魔方陣,並將4階魔方陣的對角線的數值取出(如下圖帶顏色部分的數值).
3.2.2) : 將取出來的值按照從大到小的順序(或從小到大的順序)排好,從二維陣列的[0][0]下標開始(從二維陣列[ROW - 1][COL - 1]的位置開始) 依次填入到陣列中空白的位置.
(重點)排列n= 4*k 階的魔方陣的關鍵是取出各個四階魔方陣的對角線的元素.我們可以發現:所有在對角線的元素的行列下標只差都滿足一定的規律:
即 |row - col|(絕對值) % 4 == 0 (對應圖中黃色部分)或者(row + col)%4 == 3(對應圖中紅色部分) .同時我們可以定義一個陣列br[ROW*ROW/2],
在第一步填入資料時對其行列下標進行判斷,若滿足對角線元素下標的特點,直接將數值存放到br中,同時對二維陣列相應的位置賦零值.
這樣br 中的數就是從小到大排列的,只需要在第二步時從二維陣列ar 的ar[ROW - 1][COL - 1]的位置開始,
依次將陣列br中的值填入到陣列中空白的位置.
程式碼如下:
#include<stdio.h> #include<assert.h> void Magic_Square_4K() { #define ROW 8 #define COL ROW int ar[ROW][COL] = {}; int br[ROW*ROW/2] = {};//用來儲存4階方陣對角線元素. int num = 1;//從1開始填入 int k = 0;//陣列br下標. for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { if((i-j)%4==3||(i-j)%4==0|| (j - i) % 4 == 3 || (j - i) % 4 == 0) {//先控制對角線元素為零,並將應該填在對角線的這些數記錄到br中. br[k] = num; k += 1; ar[i][j] = 0; } else { ar[i][j] = num; } num++; } } int tag = 0; for (int i = ROW - 1; i >= 0; i--)//將br中的數按照順序,從ar[ROW-1][COl-1]開始對ar中為零的元素賦值. { for (int j = COL - 1; j >= 0; j--) { if (ar[i][j] == 0) { ar[i][j] = br[tag]; tag += 1; } } } for (int i = 0; i < ROW; i++)//列印 { for (int j = 0; j < COL; j++) { printf("%4d", ar[i][j]); } printf("\n"); } #undef ROW #undef COL } int main() { Magic_Square_4K(); return 0; }
執行結果:
未完待續...