实验需求:
所谓幻方,就是一个n行n列的正方形,共有n2个格子,将1、2、3、……、n2这些数字放到这些格子里,使其每行的和、每列的和及两条对角线的和都是一个相同的数S,S称为幻和。当n为奇数时,称为奇数阶幻方,当n为偶数时,称为偶阶幻方。当n可被4整除时,称方为双偶幻方。当n不可被4整除时,称为单偶幻方。
多少年来,许多数学家都在研究这个古老而有趣的问题,试图找出一般的解法,但一般都是针对当n是奇数和n是4的倍数的情况。
当n是奇数时的算法:
首先,将1放在第一行中间一个格子里。
其次,依次将后一个数放到前一个数的右上格,如:将2放到1的右上格。将3放到2的右上格等等。可能出现下面的情况。
①若右上格从上面超出,则将后一数放到与右上格同列的最后一行。
②若右上格从右面超出,则将后一数放到与右上格同行的最后一列。
③若右上格既从右面超出又从上面超出,则将后一数放到前一数的下面。
④若右上格已被数字填充,则将后一数放到前一数的下面
依以上法则,你可以很快的写出奇数阶幻方!当然,这中写法只是其中一个答案,而不是唯一答案。
一、奇数阶幻方的制作
1.连续摆数法
例:一个5×5 格子,由最上面一行中间一格开始,依次填1,2,3等等。下一个格子填在左上位置。但是要注意两点:
-
- 出了幻方的范围,右边接到左边,下边接到上边。
- 某一格右上已经有了数字,改填在这个格子的下面一格,然后延续前面的方法。
-
17
24
1
8
15
23
5
7
14
16
4
6
13
20
22
10
12
19
21
3
11
18
25
2
9
也不一定按照斜上方写字,可以走马步,或其他方法。下面用的是马步,得到的是泛对角幻方。
8
17
1
15
24
11
25
9
18
2
19
3
12
21
10
22
6
20
4
13
5
14
23
7
16
哪些“步子”是可行的,是需要注意的一个问题。
2.阶梯法
例:以5阶为例。
第一步:画一个9×9的方格。如下斜着填数字。注意中间的5×5格子才是要作的幻方的位置,已经涂成了黄色。
5
4
10
3
9
15
2
8
14
20
1
7
13
19
25
6
12
18
24
11
17
23
16
22
21
第二步:黄色范围以外的数字,平移到黄色格子中没有数字的位置。
3
16
9
22
15
20
8
21
14
2
7
25
13
1
19
24
12
5
18
6
11
4
17
10
23
去掉外围的格子,就得到所要作的幻方。
3
16
9
22
15
20
8
21
14
2
7
25
13
1
19
24
12
5
18
6
11
4
17
10
23
二、偶数阶幻方的制作
偶数阶幻方的制作比较复杂,下面介绍几个方法。
1.对称法,适用于双偶数阶幻方
例:以8阶幻方为例。
第一步:在左上4×4格子中,取一半的格子,要求每行每列都取到2个。如下图黄色格子所示。
第二步:按照左右对称、上下对称、中心对称的方法把这8个格子扩充为32个格子。
第三步:从左上角开始,从左到右从上到下,从1开始填数。不过只填没有选中的格子(即没有涂黄色的格子)
2
4
5
7
9
11
14
16
17
19
22
24
26
28
29
31
34
36
37
39
41
43
46
48
49
51
54
56
58
60
61
63
第四步:从右下角开始从右到左从下到上在选中的格子里填进刚才没有填的数字。
64
2
62
4
5
59
7
57
9
55
11
53
52
14
50
16
17
47
19
45
44
22
42
24
40
26
38
28
29
35
31
33
32
34
30
36
37
27
39
25
41
23
43
21
20
46
18
48
49
15
51
13
12
54
10
56
8
58
6
60
61
3
63
1
制作成功。
这个方法可以产生不同的双偶数阶幻方。
思考:能否算出这个方法可以产生多少个8阶幻方?
2.斯特雷奇法,适用于单偶数幻方
例:设阶数n=2(2m+1)=6,m=1。
第一步:把方阵分为4个小方阵,位置依次为A左上,B右下,C右上,D左下。
用连续摆数法,把1-a^2放在A中成第一个幻方;把a^2+1~2a^2放在B中成第二个幻方。把2a^2+1~3a^2放在C中成第三个幻方。把3a^2+1~4a^2放在D中成第四个幻方。
8
1
6
26
19
24
3
5
7
21
23
25
4
9
2
22
27
20
35
28
33
17
10
15
30
32
34
12
14
16
31
36
29
13
18
22
第二步:在A的各行左起取m个方格,但中间一行从第二格开始。与D中相应位置对换。
8
1
6
26
19
24
3
5
7
21
23
25
4
9
2
22
27
20
35
28
33
17
10
15
30
32
34
12
14
16
31
36
29
13
18
22
第三步:在C的各行右起取m-1个方格,与B中相应位置对换。此例m-1=0,无需交换。
35
1
6
26
19
24
3
32
7
21
23
25
31
9
2
22
27
20
8
28
33
17
10
15
30
5
34
12
14
16
4
36
29
13
18
22
3.LUX方法 这是剑桥大学康韦教授发明的方法
例:设阶数n=2(2m+1)=10,m=2。
第一步:任取一个2m+1 阶幻方,例如5阶幻方。如下。
1
23
16
4
21
15
14
7
18
11
24
17
13
9
2
20
8
19
12
6
5
3
10
22
25
第二步:在上面的m+1行 (此处为3行)的每个格子里填入一个字母L;接下去一行填字母U,余下m-1 行填字母X。最后把中间的一个L 与它下面的一个U 交换一下。
1L
23L
16L
4L
21L
15L
14L
7L
18L
11L
24L
17L
13U
9L
2L
20U
8U
19L
12U
6U
5X
3X
10X
22X
25X
第三步:作一个10×10方格,设想为每2×2为一个单位,每个单位相应于上面一个格子。对应于5阶幻方中数字1的单位填1,2,3,4。对应于5阶幻方中数字2的填5,6,7,8。等等。但是标有字母L 的按照“右上-左下-右下-左上”次序;标有字母U 的按照“左上-左下-右下-右上”次序;标有字母X 的按照“左上-右下-左下-右上”次序。
4
1
92
89
64
61
16
13
84
81
2
3
90
91
62
63
14
15
82
83
60
57
56
53
28
25
72
69
44
41
58
59
54
55
26
27
70
71
42
43
96
93
68
65
49
52
36
33
8
5
94
95
66
67
50
51
34
35
6
7
77
80
29
32
76
73
45
48
21
24
78
79
30
31
74
75
46
47
22
23
17
20
9
12
37
40
85
88
97
100
19
18
11
10
39
38
87
86
99
98
当然,这个方法也产生很多幻方,并不唯一。
三、代码实现
头文件:fantasy.h
#ifndef FANTASY
#define FANTASY
#include <iostream>
using namespace std;
struct Fantasy{
};
void getSingleFantasy(int num);
int ** getSingleFantasy(int num,int startNum);
void getDoubleFantasy(int num);
void SingleDoubleFantasy(int **arr,int num);
void DoubleDoubleFantasy(int **arr,int num);
void showArrayList(int **arr,int num);
#endif
源文件:fantasy.cpp
#include "fantasy.h"
//奇数阶幻方 连续摆数法
void getSingleFantasy(int num){
int **arr = new int*[num];
for(int i = 0; i < num; i++){
arr[i] = new int [num];
}
arr[0][num/2] = 1;
int preIndex = 0,afterIndex = num/2;
for(int j=2;j<=num*num;j++){
int tempre=preIndex,temafter=afterIndex;
if(preIndex-1<0)
preIndex = num-1;
else
preIndex = preIndex - 1;
if((afterIndex+1)>=num)
afterIndex = 0;
else
afterIndex +=1;
if(arr[preIndex][afterIndex]<0)
arr[preIndex][afterIndex] = j;
else{
if(tempre+1==num)
tempre = 0;
else{
preIndex = tempre+1;
afterIndex = temafter;
}
arr[preIndex][afterIndex] = j;
}
}
showArrayList(arr,num);//输出arr
}
//双偶数幻方专用的连续摆数法
int ** getSingleFantasy(int num,int startNum){
int **arr = new int*[num];
for(int i = 0; i < num; i++){
arr[i] = new int [num];
}
arr[0][num/2] = startNum;
int preIndex = 0,afterIndex = num/2;
int size = 0;
if(startNum==1)
size = num*num;
else if(startNum == num*num+1)
size = 2*num*num;
else if(startNum == 2*num*num+1)
size = 3*num*num;
else if(startNum == 3*num*num+1)
size = 4*num*num;
for(int j=startNum+1;j<=size;j++){
int tempre=preIndex,temafter=afterIndex;
if(preIndex-1<0)
preIndex = num-1;
else
preIndex = preIndex - 1;
if((afterIndex+1)>=num)
afterIndex = 0;
else
afterIndex +=1;
if(arr[preIndex][afterIndex]<0)
arr[preIndex][afterIndex] = j;
else{
if(tempre+1==num)
tempre = 0;
else{
preIndex = tempre+1;
afterIndex = temafter;
}
arr[preIndex][afterIndex] = j;
}
}
//showArrayList(arr,num);//输出arr
return arr;
}
void getDoubleFantasy(int num){
int **arr = new int*[num];
for(int i = 0; i < num; i++){
arr[i] = new int [num];
}
if(num%4==0){
cout<<"使用双偶数幻方"<<endl;
DoubleDoubleFantasy(arr,num);
}else{
cout<<"使用单偶数幻方"<<endl;
SingleDoubleFantasy(arr,num);
}
}
//单偶数阶幻方 斯特雷奇法
void SingleDoubleFantasy(int **arr,int num){
num = num/2;
for(int i=0;i<4;i++){
int **tempArr = new int*[num];
for(int j = 0; j < num; j++){
tempArr[j] = new int [num];
}
tempArr = getSingleFantasy(num,i*num*num+1);//调用连续摆数法,用tempArr接收
int startHeight = 0,startWidth = 0;
if(i==0){
startHeight = 0;
startWidth = 0;
}else if(i==1){
startHeight = num;
startWidth = num;
}else if(i==2){
startHeight = 0;
startWidth = num;
}else if(i==3){
startHeight = num;
startWidth = 0;
}
int count = 0;//将tempArr放入arr的计数器
for(int n=startHeight;n<startHeight+num;n++){
for(int m=startWidth;m<startWidth+num;m++){
arr[n][m] = tempArr[count/num][count%num];
count++;
}
}
}
//阶数n=2(2m+1)=6 m=1
int m = (num-1)/2;
cout<<"m:"<<m<<endl;
//AD交换
for(int i=0;i<num;i++){
for(int j=0;j<m;j++){
if(i==num/2){
int temp = arr[i][j+1];
arr[i][j+1] = arr[i+num][j+1];
arr[i+num][j+1] = temp;
}else{
int temp = arr[i][j];
arr[i][j] = arr[i+num][j];
arr[i+num][j] = temp;
}
}
}
//CB交换
for(int i=0;i<num;i++){
for(int j=num;j<num+m-1;j++){
if(i==num/2+num){
int temp = arr[i][j+1];
arr[i][j+j] = arr[i+num][j+1];
arr[i+num][j+1] = temp;
}else{
int temp = arr[i][j];
arr[i][j] = arr[i+num][j];
arr[i+num][j] = temp;
}
}
}
showArrayList(arr,num*2);//输出
}
//双偶数阶幻方
void DoubleDoubleFantasy(int **arr,int num){
int length = num/4;
//-1表示白色,0表示黄色
for(int index=0;index<length;index++){
for(int j=0;j<4;j++){
for(int k=0+index*4;k<4+index*4;k++){
if(index%2==0){
if((j%3==0&&k%2==0)||(j%3!=0&&k%2==1))
arr[j][k] = 0;
else
arr[j][k] = -1;
}else{
if((j%3==0&&k%2==0)||(j%3!=0&&k%2==1))
arr[j][k] = -1;
else
arr[j][k] = 0;
}
}
}
}
//竖的对称复制
for(int index=1;index<length;index++){
for(int j=index*4;j<4+index*4;j++){
for(int k=0;k<num;k++){
arr[j][k] = arr[j-index*4][k];
}
}
}
struct arraylist{
int *data;
int length;
};
//结构体初始化
arraylist tempArray;
tempArray.data = new int[num*num];
tempArray.length = 0;
int count=1;
//顺序将白色填入数值
for(int j=0;j<num;j++){
for(int k=0;k<num;k++){
if(arr[j][k]==0){
tempArray.data[tempArray.length] = count;
tempArray.length++;
count++;
continue;
}else{
arr[j][k] = count;
count++;
}
}
}
//将tempArray的数据从中取出
for(int j=0;j<num;j++){
for(int k=0;k<num;k++){
if(arr[j][k] == 0){
arr[j][k] = tempArray.data[tempArray.length-1];
tempArray.length--;
}
}
}
showArrayList(arr,num);//输出arr
}
void showArrayList(int **arr,int num){
//输出
if(num<10)
for(int k=0;k<num;k++){
for(int n=0;n<num;n++){
if(arr[k][n]<10)
cout<<"0"<<arr[k][n]<<" ";
else
cout<<arr[k][n]<<" ";
}
cout<<endl;
}
else if(num>=10)
for(int k=0;k<num;k++){
for(int n=0;n<num;n++){
if(arr[k][n]<10)
cout<<"00"<<arr[k][n]<<" ";
else if(arr[k][n]<100)
cout<<"0"<<arr[k][n]<<" ";
else
cout<<arr[k][n]<<" ";
}
cout<<endl;
}
}
源文件的主函数:main.cpp
#include "fantasy.h"
void menu(){
cout<<" 幻方制作 "<<endl;
cout<<"======================"<<endl;
cout<<" 1。奇数阶幻方 "<<endl;
cout<<" 2。偶数阶幻方 "<<endl;
}
int main(){
while(true){
menu();
int selection;
cout<<"请输入您的选择:"<<endl;
cin>>selection;
if(selection == 0)
break;
int num;
bool flag = false;
switch(selection){
case 1:
while(!flag){
cout<<"请输入幻方的奇数阶:"<<endl;
cin>>num;
if(num%2!=0&&num>2)
flag = true;
else
cout<<"输入错误,请输入奇数!!"<<endl;
}
getSingleFantasy(num);
break;
case 2:
while(!flag){
cout<<"请输入幻方的偶数阶:"<<endl;
cin>>num;
if(num%2==0&&num>2)
flag = true;
else
cout<<"输入错误,请输入偶数!!"<<endl;
}
getDoubleFantasy(num);
break;
}
}
return 0;
}