素数幻方
1. 案例提出
通常的n阶幻方由1,2,...,n2构成的各行、各列与两对角线之和均相等n行n列方阵。素数幻方全是由素数构成的各行、各列与两对角线之和均相等方阵。
试寻求9个素数,构造一个3阶素数幻方,使得该素数方阵中3行、3列与两对角线上的3个素数之和均等于给定的整数s。
2. 设计要点
(1) 数学建模
设幻方正中间数为n,幻和(即每行,每列与每对角线之和)为s。注意到
(中间一行)+(中间一列)+2*(两对角线)=6s
(上下行)+(左右列)=4s
两式相减即得
6n=2s → n=s/3 (1)
这意味着凡含n的行或列或对角线的三数中,除n之外的另两数与n相差等距。为此,设3阶幻方为:
n-x n+w n-y
n+z n n-z (2)
n+y n-w n+x
同时设方阵的两对角线的三数为大数在下(即x,y>0),下面一行三数为大数在右(即x>y)。这样约定是避免重复统计解。
显见,上述3×3方阵的中间一行,中间一列与两对角线上三数之和均为3n。要使左右两列,上下两行的三数之和也为3n,当且仅当
z=x-y
w=x+y (x>y) (3)
同时易知9个素数中不能有偶素数2,因而x,y,z,w都只能是正偶数。
(2) 穷举设计
对于键盘输入的整数s, 如果存在幻和为s的素数幻方,则s应为中间素数的3倍。若s不是3的整数倍,则对输入的s,输出“无解!”而退出。
设置a数组,数组元素清“0”。通过试商判别,寻找出[3,s]中的所有素数k,并标注a[k]=1,为以后的判断提供依据。
设n=s/3,若a[n]==0,知n不是素数,显然不存在幻和为s的素数幻,显示“无解!”后退出。
设幻方中的素数下界为c,上界为d。显然c=3,注意到d+n+c=s,则d≤2n-3。
注意到 n-y≥3,(n+y)+n+(n-y)=s 得 2n+y=s-(n-y)≤s-3 即 y≤s-2n-3
同理 x≤s-2n-3=n-3
在[2,n-3]中穷举y,在[y+2,n-3]穷举x,并按上述(3)式得z,w:
若出现x=2y,将导致z=y,方阵中出现两对相同的数,显然应予排除。
显然n-w是9个数中最小的,n+w是9个数中最大的。若n-w<c或n+w>d,已超出[c,d]界限,应予以排除。
检测方阵中其他8个数n-x,n+w,n-y,n+z,n-z,n+y,n-w,n+x是否同时为素数,引用变量t1,t2,t1*t2为8个数的标记之积。若t1*t2=0,即8个数中存在非素数,返回。否则,已找到一个三阶素数幻方解,按方阵格式输出并用变量m统计基本解的个数。这样处理,能较快的找出所有解,既无重复,也没有遗漏。
//此题重在数学分析
3. 程序实现
// 指定幻和的三阶素数幻方
#include<stdio.h>
#include<math.h>
void main()
{
int c,d,j,k,t,n,t1,t2,s,w,x,y,z,m;
int a[3000];
printf("请确定素的幻方和s:");
scanf("%d",&s);
if(s%3>0)
{
printf("不存在幻方和为%d的素数比方!\n",s);
return;
}
n=s/3;
m=0;
// 确定幻和为s时素数的最大最小区间
c=3;
d=2*n-3;
for(k=c;k<=d;k++)
a[k]=0;
for(k=c;k<=d;k+=2)
{
for(t=0,j=3;j<=sqrt(k);j+=2)
if(k%j==0)
{
t=1;
break;
}
if(t==0)
a[k]=1; // [c,d]中的奇数k为素数,标注1
}
if(a[n]==0)
{
printf("不存在幻方和为%d的素数方!\n",s);
return;
}
for(y=2;y<=n-3;y+=2)
for(x=y+2;x<=n+3;x+=2)
{
z=x-y;
w=x+y;
if(x==2*y||n-w<c||n+w>d)
continue; // 控制幻方的素数范围
// 控制其余8个均为素数
t1=a[n-w]*a[n+w]*a[n-z]*a[n+z];
t2=a[n-x]*a[n+x]*a[n-y]*a[n+y];
if(t1*t2==0)
continue;
m++;
printf("No %d:\n",m);
printf("%5d%5d%5d\n",n-x,n+w,n-y);
printf("%5d%5d%5d\n",n+z,n,n-z);
printf("%5d%5d%5d\n",n+y,n-w,n+x);
}
printf("\n共有%d个素数幻方.\n",m);
}