0%

【算法导论】幻方算法

​ 说起幻方,大家应该在小学时候就已经接触过了,最简单的就是九宫格,射雕英雄传中的那段至今还记得:戴九履一,左三右七,二四为肩,六八为足。下面我们就来看看这个有趣的问题。

​ 幻方可以分为:奇数阶幻方、双偶阶幻方、单偶阶幻方

奇数阶幻方

​ 上面所说的九宫格就是典型的奇数阶幻方,奇数阶幻方值得是阶数为奇数的幻方。其最经典的填法是罗伯法。首先 把$1$(或最小的数)放在第一行正中;按以下规律排列剩下的$(n^2-1)$个数,具体步骤为:

(1)每一个数放在前一个数的右上一格;

(2)如果这个数所要放的格已经超出了顶行那么就把它放在底行,仍然要放在右一列;

(3)如果这个数所要放的格已经超出了最右列那么就把它放在最左列,仍然要放在上一行;

(4)如果这个数所要放的格已经超出了顶行且超出了最右列,那么就把它放在底行且最左列;

(5)如果这个数所要放的格已经有数填入,那么就把它放在前一个数的下一行同一列的格内。

上述步骤可以总结为七言绝句:

先填上行正中央, 依次斜填切莫忘。 上格没有顶格填, 顶格没有底格放。—奇幻七绝

下面有人通过作图可以很好的解释这几句话,现借鉴如下:

图1

从上面的图可以看出,该图与我们前面的九宫格口诀不相符,上下颠倒了。但是这都是对的,本质上没有区别。

双偶数阶幻方

所谓双偶阶幻方就是当$n$可以被$4$整除时的偶阶幻方,即$4K$阶幻方。其最经典的填法为海尔法,下面以$8$阶幻方为例,具体的填法为:

(1)先把数字按顺序填。然后,按$4\times 4$把它分割成$4$块(如图)
图2

(2)每个小方阵对角线上的数字(如左上角小方阵部分),换成和它互补的数。

图3

单偶数阶幻方

所谓单偶阶幻方就是当$n$不可以被$4$整除时的偶阶幻方,即$4K+2$阶幻方。如$(n=6, 10,\cdots)$的幻方。其经典的填法为斯特拉兹法,以$10$阶幻方为例,具体的步骤如下:

1)把魔方阵分为$A,B,C,D$四个象限,这样每一个象限肯定是奇数阶。用罗伯法,依次在$A$象限,$D$象限,$B$象限,$C$象限按奇数阶幻方的填法填数。

图4

(2)在$A$象限的中间行、中间格开始,按自左向右的方向,标出$k$格。$A$象限的其它行则标出最左边的$k$格。将这些格,和$C$象限相对位置上的数互换位置。

图5

(3)在$B$象限所有行的中间格,自右向左,标出$k-1$格。(注:$6$阶幻方由于$k-1=0$,所以不用再作$B$、$D$象限的数据交换),将这些格,和$D$象限相对位置上的数互换位置。

图6

具体的程序实现如下:

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include<stdio.h>

//注意由于matrix大小(可以更改)给定,能最大生成10阶幻方
bool check(int matrix[10][10],int n)//判断是否为幻方
{
int sum=0;
int temp=0;
int i=0,j=0,k=0;
for( i=0;i<n;i++)
sum=sum+matrix[0][i];//得到一行或列的总和
for(j=1;j<n;j++)//检查行
{
temp=0;
for(k=0;k<n;k++)
temp=temp+matrix[j][k];
if(temp!=sum)
return false;

}
for( j=0;j<n;j++)//检查列
{
temp=0;
for(k=0;k<n;k++)
temp=temp+matrix[k][j];
if(temp!=sum)
return false;
}
temp=0;
for(i=0;i<n;i++)
temp=temp+matrix[i][i];//检查主对角线
if(temp!=sum)
return false;

temp=0;
for(i=0;i<n;i++)
temp=temp+matrix[i][n-1-i];//检测副对角线
if(temp!=sum)
return false;
printf("该方阵为幻方!\n");
return true;

}

void Odd(int n,int matrix[10][10])//奇数阶幻方
{
int i=0,j=n/2;
int number=1;
for(int k=0;k<n*n;k++)
{
matrix[i][j]=number;

i--;
j++;
number++;

if(i<0&&j<n)//出上界
{
i=n-1;

}
else if(i>=0&&j>=n)//出右界
{
j=0;
}
else if(i<0&&j>=n)//右、上出界
{

if(matrix[n-1][0]!=0)//底格放
{
i=i+2;
j=j-1;
}
else
{
i=n-1;
j=0;
}

}
if(matrix[i][j]!=0)//底格放
{
i=i+2;
j=j-1;
}
}
}


void DoubleEven(int n,int matrix[10][10])//双偶数阶幻方
{

int number=1;
int temp=0;
int i=0,j=0,k=0;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
matrix[i][j]=number++;//初始化

for(i=0;i<n;i=i+4)
for(j=0;j<n;j=j+4)
for(k=0;k<4;k++)
{
matrix[i+k][j+k]=n*n+1-matrix[i+k][j+k];//每个对角线的数换成互补的数
matrix[i+k][j+3-k]=n*n+1-matrix[i+k][j+3-k];
}

}

void SingleEven(int n,int matrix[10][10])//单偶数阶幻方
{
int degree=n/2;
int flag=n/4;
int i=0,j=0,k=0;
int temp=0;

int matrix1[10][10]={0};//将大矩阵化为A B C D四个小矩阵
int matrix2[10][10]={0};
int matrix3[10][10]={0};
int matrix4[10][10]={0};

Odd(degree,matrix1);//对每一个矩阵进行奇数幻方算法
for(i=0;i<degree;i++)
for(j=0;j<degree;j++)
{
matrix2[i][j]=matrix1[i][j]+degree*degree;
matrix3[i][j]=matrix1[i][j]+degree*degree*2;
matrix4[i][j]=matrix1[i][j]+degree*degree*3;
}

for(i=0;i<degree;i++)//对A C矩阵按照规则进行数据交换
for(j=0;j<flag;j++)
if(i!=(degree/2))
{
temp=matrix1[i][j];
matrix1[i][j]=matrix4[i][j];
matrix4[i][j]=temp;
}
else
{
temp=matrix1[i][j+degree/2];
matrix1[i][j+degree/2]=matrix4[i][j+degree/2];
matrix4[i][j+degree/2]=temp;
}
for(i=0;i<degree;i++)//对B D矩阵按照规则进行数据交换
for(j=0;j<flag-1;j++)
{
temp=matrix2[i][j+degree/2];
matrix2[i][j+degree/2]=matrix3[i][j+degree/2];
matrix3[i][j+degree/2]=temp;
}


//将新的四个矩阵赋给幻方矩阵matrix
for(i=0;i<degree;i++)
{
for(j=0;j<degree;j++)
matrix[i][j]=matrix1[i][j];

for(k=0;k<degree;k++)
matrix[i][j+k]=matrix3[i][k];

}
for(i=0;i<degree;i++)
{
for(j=0;j<degree;j++)
matrix[i+degree][j]=matrix4[i][j];

for(k=0;k<degree;k++)
matrix[i+degree][j+k]=matrix2[i][k];

}

}


void main()
{
int matrix[10][10]={0};
int n; printf("%d",6%2);
printf("请输入幻方的阶数:");
scanf("%d",&n);


if(n%2!=0)
Odd(n,matrix);
else if(n%4!=0)
SingleEven(n,matrix);
else
DoubleEven(n,matrix);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
printf("%d ",matrix[i][j]);
printf("\n");
}

check(matrix,n);//检测是否为幻方
}
坚持原创技术分享,您的支持将鼓励我继续创作!