您的位置:首页 > 其它

质数环问题的两种解法及优化思路

2013-10-19 13:15 483 查看
本学期的计算机系统实验课程中遇到质数环问题,需要编写程序求解并在ARM多核开发系统上开展实验。以下是我通过搜索得到的两种解法,第一种利用C语言编写,只能输出20个数字的质数环;第二种解法利用C++编写,可以输出小于等于20个数字的质数环。

解法一:

//我们可以通过改变n的值来求不同数字范围的质数环,如果超出整型的范围,还需要改变数据类型。
//p[i][j]用来记录数字i和数字j的和是否为质数,f[i]来记录数字i是否使用过,
//T[i]用来记录下一个可以插在数字i后面的与其和为质数的数字在P[i][]中的位置。
//用F[i][j]来存储按数字从小到大的顺序得出的与数字i和为质数的第j个数字,
//例如:F[1][2]存储的是与数字1的和为质数的第二个数字,我们可以通过查询数组P[][]的第一行找出第二个不为0
//值,然后将当前数组单元的列号存储到F[1][2]中,即F[1][2] = 4。
//算法思想是通过查询二维数组F[][],来确定下一个可以插入数组C[]的未使用过的数字,并记录该数字位于数组F[][]的位置,
//以便回溯时寻找下一个符合要求的数字。如果不存在这样的未使用的数字,则需要回溯到上一个已插入C[]的数字,
//寻找下一个可以插在该数字后面的未使用过的数字进行插入,如果所有的数字都已经插入到C[]中,
//那么还要判断首尾数字相加之和是否为质数,如是则打印结果。当所有数字都已插入C[]中,开始
//进行回溯,重复上述操作,寻找其他符合要求的序列。
#include"stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
void main()
{
int  P[21][21], F[21][21], f[21], T[21], C[20];   //20个数
int i, j, k, t, temp, p, flag, n, c;
double duration;
clock_t start, finish;
start = clock();
n = 20;
c = 20000;
for(i = 1; i <= n; i++)
{
k=1;
for(j = 1; j <= n; j++)
{
P[i][j] = 0;                              //数组初始化
F[i][j] = 0;
if(j != i && ((int)abs((i-j) % 2)) != 0)  //i和j一奇一偶
{
temp = i + j;
t = (int) sqrt((double)(temp) + 1);//求i和j和的平方根
for(p = 2; p <= t; p++)
{
P[i][j] = 0;
if(! (temp % p)) break;     //判断temp是否是质数,如果temp是质数,则P[i][j]为1
P[i][j] = 1;
}
}
if(P[i][j])                               //如果i和j的和为质数则
{                                         //记录与数字i和为质数的第k个数字j
F[i][k] = j;
k++;
}
}
}

//for(i = 1; i <= n && c; i++)
for(i=1; i<=n; i++)
{
C[0] = i;
for(p = 1; p <= n; p++)
{
T[p] = 1;                                 //T[i]用来记录下一个可以插在数字i后面的与其和为质数的数字在F[i][]中的位置。
f[p] = 0;                                 //f[i]记录数字i是否使用过,0表示未使用过,1表示使用过
}
f[i] = 1;
k = 1;
while(k)
{
while(F[i][T[i]] && k < n)               //F[i][T[i]]表示存在与i和为质数的数字并且可以插在数字i后面
{
if(! f[F[i][T[i]]])              //f[F[i][T[i]]]判断如果数字F[i][T[i]]没有使用过
{
C[k] = F[i][T[i]];        //记录质数环的数字
f[C[k]] = 1;
T[i]++;
i = C[k];
k++;
}
else;
T[i]++;
}
if(k == n)                               //判断C[0]和C[n-1]的和是否为质数
{
temp = C[0] + C[n-1];
t = (int) sqrt((double)(temp) + 1);
for(p = 2; p <= t; p++)
{
flag = 0;
if(! (temp % p)) break;
flag = 1;                 //如果C[0]和C[n-1]的和为质数,则flag为1
}
if(flag)
{
c--;
for(p = 0; p < n; p++)
{
printf("%d- ",C[p]);
}
printf("\n");
}
}

f[C[--k]] = 0;                       //回溯
T[C[k]] = 1;
i = C[k-1];
if(!c)                                  //如果c为0,则k为0,此时结束while循环,即至多寻找20000个质数环
k=0;
}//end while
}//end for
finish=clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\n", duration );
}//end main


解法二:

/***********************************************************************

分析:

1、每个环都从1开始,先将数组a[0]赋值1.

2、每选定前一个素数,后一个位置就少一个可选择项,由此可用一个数组bUsed[]来标记状态.
3、前一个后一个选定值总和前一个选定值关联,由此可用回溯法(深度优先遍历的方式遍历解答树)。
************************************************************************/
#include "stdafx.h"
#include <iostream>
#include <math.h>
using namespace std;
int n=0;
int a[20];
bool bUsed[20];//对应数组a[20],判断对应节点在当前求得素数环中(解答树)是否有它

/*判断是素数*/
bool isp(int n){
if(n<3)
return false ;
int len=(int)sqrt(n+0.0);
for (int i=2;i<=len;i++){
if(n%i==0)
return false ;
}
return true;
}

/*递归输出所有素数环*/
void AA(int cur){
//在最后一层执行,输出当前求得解答串
if(cur==n&&isp(a[0]+a[n-1])){
for (int i=0;i<n;i++)
cout<<a[i]<<' ';
cout<<endl;
return ;
}
//前n-1层执行,递归选定每一层的整数,使其与前一层的整数之和为素数
else for (int i=2;i<= n;i++){
if(!bUsed[i]&&isp(i+a[cur-1]))
{
//当前值i没被使用,且与前一个选定值之和为素数
a[cur]=i;       //选i为当前项值
bUsed[i]=true;  //状态从没被使用改为被使用
AA(cur+1);      //进入下一层,若cur+1<n则求下一个有效值,否则执行输出语句
//递归后面的语句在从n-1层到第1层回调时执行
bUsed[i]=false; //状态还原,使重新求下一个有效串时不被干扰
}
}
}

int main(){
for (int i=0;i<20;i++)
a[i]=i+1;           //初始化一个数组1,2,3,4...
memset(bUsed,0,sizeof(bUsed));//全部初始化为false表示均没被使用
while (cin>>n,n){
AA(1);              //回溯法遍历解答树,输出所有素数环
}
}


优化思路:可以通过一些规则进行剪枝,能够提高搜索效率。

1、对于输入的n,如果输入n是奇数的话,则没有满足条件的质数环。

if(n & 1) // 奇数无解,直接返回
return ;


2、相邻的两个数奇偶性必然不同。

if(!((a[0] + a[n-1]) & 1)) // 相邻的数奇偶性必然不同
return false ;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: