C语言素数幻方计算

#include
#include <string.h>
#include <time.h>
#include <math.h>
#include <algorithm>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>


using namespace std;


/*
先求出全部的四位可逆素数(204个),以矩阵的行为单位,在四位可逆素数的范围内进行穷举,
然后将穷举的四位整数分解为数字后,再进行列和对角线方向的条件判断,改进的算法与最初
的算法相比,大大地减少了穷举的次数。


考虑矩阵的第一行和最后一行数字,它们分别是列方向四位数的第一个数字和最后一个数字,
由于这些四位数也必须是可逆素数,所以矩阵的每一行和最后一行中的各个数字都不能为偶数或5。
这样穷举矩阵的第一行和最后一行时,它们的取值范围是:所有位的数字均不是偶数或5的四位可
逆数。由于符合这一条件的四位可逆素数很少,所以这一范围限制又一次减少了穷举的次数。


对算法的进一步研究会发现:当设定了第一和第二行的值后,就已经可以判断出当前的这种组合
是否一定是错误的(尚不能肯定该组合一定是正确的)。若按列方向上的四个两位数与四位可逆数
的前两位矛盾(不是其中的一种组合),则第一、二行的取值一定是错误的。同理在设定了前三行
数据后,可以立刻判断出当前的这种组合是否一定是错误的,若判断出矛盾情况,则可以立刻设
置新的一组数据。这样就可以避免将四个数据全部设定好以后再进行判断所造成的低效。
*/


bool isPrime(int n);//判断是否为素数
int advert(int n);//将一个正数反过来输出
void change(int a[4][4], int n, int i); //将整形转换成数组
bool contact1(int a[4][4]);//第一行和第二行的联系
bool contact2(int a[4][4]);//前三行的联系
bool contact3(int a[4][4]);//前四行的联系
bool judge(int a[4][4], int n);//判断是否各位都位奇数且不等于5
void print(int a[4][4]);//打印数组
bool isTrue(int a[4][4]);//如果正反对角线也是素数...


int b[1000], bLen = 0, digit[1000], dLen = 0;
//digit中存放互逆的素数...以减小计算量
//b中存放在digit互逆素数中各位都位奇数且不等于5的数


void main()
{
int start = GetTickCount();//计时开始


int i, j, k, m, a[4][4] = {0};

int count = 0;//计算个数


for(i = 1001; i < 10000; i++)//将四位的互逆素数存放在digit数组中
{
int t = advert(i);


if(isPrime(i) && isPrime(t))
digit[dLen++] = i;
}


sort(digit, digit + dLen);//将这些数从小到大排列,,当然你也可以不排列


for(i = 0; i < dLen; i++)
{
change(a, digit[i], 0);//将digit中的数分解


if(judge(a, 0))//如果各位都位奇数且不等于5
{
b[bLen++] = digit[i];

// cout<<digit[i]<<endl;
}
}


for(i = 0; i < bLen; i++)
{
change(a, b[i], 0);//填充a数组的第1行


for(j = 0; j < dLen; j++)
{
change(a, digit[j], 1);


if(!contact1(a))//填充a数组的第2行
continue;


for(k = 0; k < dLen; k++)
{
change(a, digit[k], 2);//填充a数组的第3行


if(!contact2(a))
continue;


for(m = 0; m < bLen; m++)
{
change(a, b[m], 3);//填充a数组的第4行


if(!contact3(a))
continue;


if(isTrue(a))
{
count++;


print(a);


cout<<endl;
}
}
}
}
}


int end = GetTickCount();//计时结束


cout<<count<<"个"<<endl;


cout<<"花费了"<<end - start<<"毫秒!"<<endl;
}


bool isPrime(int n)
{
int i;


for(i = 2; i < n; i++)//素数的定义...
{
if(n % i == 0)
return false;
}


return true;
}


int advert(int n)//将整形反向输出
{
int m = 0;


while(n)//从末尾开始求10的余数...
{
m += n % 10;
n /= 10;
m *= 10;
}


return m / 10;
}


void change(int a[4][4], int n, int i)//将n转换成a的第i行
{
a[i][0] = n / 1000;
a[i][1] = n / 100 % 10;
a[i][2] = n / 10 % 10;
a[i][3] = n % 10;
}


bool contact1(int a[4][4])//如果第一行跟第二行的数满足那互逆素数的时候
{
int i, j;


for(i = 0; i < 4; i++)
{
int t = a[0][i] * 10 + a[1][i];//第一行 * 10 + 第二行 = 前两行的值


for(j = 0; j < dLen; j++)
{
if(digit[j] / 100 == t)//如果它不在digit中,则不是互逆的
break;
}


if(j == dLen)
return false;
}


return true;
}


bool contact2(int a[4][4])//同上
{
int i, j;


for(i = 0; i < 4; i++)
{
int t = a[0][i] * 100 + a[1][i] * 10 + a[2][i];


for(j = 0; j < dLen; j++)
{
if(digit[j] / 10 == t)
break;
}


if(j == dLen)
return false;
}


return true;
}


bool contact3(int a[4][4])//同上
{
int i, j;


for(i = 0; i < 4; i++)
{
int t = a[0][i] * 1000 + a[1][i] * 100 + a[2][i] * 10 + a[3][i];


for(j = 0; j < dLen; j++)
{
if(digit[j] == t)
break;
}


if(j == dLen)
return false;
}


return true;
}


bool judge(int a[4][4], int n)
{
int i;


for(i = 0; i < 4; i++)
{
if(a[n][i] % 2 == 0 || a[n][i] == 5)//四位数满足是奇数且不等于5,因为如果末尾是5的话可以被5整除
break;
}


if(i == 4)
return true;


return false;
}


void print(int a[4][4])//打印
{
int i, j;


for(i = 0; i < 4; i++)
{
for(j = 0; j < 4; j++)
{
cout<<a[i][j]<<" ";
}
cout<<endl;
}
}


bool isTrue(int a[4][4])
{
int sum1 = a[0][0] * 1000 + a[1][1] * 100 + a[2][2] * 10 + a[3][3];//正对角线的值
int sum2 = a[3][0] * 1000 + a[2][1] * 100 + a[1][2] * 10 + a[0][3];//反对角线的值
int flag1 = 1, flag2 = 1;//两个标记


int i;

for(i = 0; i < dLen; i++)
{
if(digit[i] == sum1)
{
flag1 = 0;


if(flag2 == 0)//如果flag2先等于0了就可以跳出循环了
break;
}


if(digit[i] == sum2)
{
flag2 = 0;


if(flag1 == 0)//同上
break;
}
}


if(flag1 == 0 && flag2 == 0)//当两个都在互逆素数中时则成立,当然你也可以用i == dLen
return true;


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