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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章