哥德巴赫猜想穷举验证算法及实现
3.18是哥德巴赫的生日,本来想这一天用穷举的方法验证下他的猜想,无奈因为其它的事情而暂时耽搁,昨晚终于把程序写好了,今天在这里分享一下。
一、哥德巴赫猜想
任一大于2的偶数都可以分解成两个质数的和。
二、穷举验证算法
算法说明:
1、用prime动态数组保存所有质数,并将前两个数初始化为3和1,这样初始化是为动态构造出质数数组prime。
2、如果满足猜想,则输出格式为:偶数x 左质数 右质数
3、用r_index表示prime的最大可索引游标,用l_index表示左质数在质数数组prime中的游标,用cur_index表示每个x开始判断是否为质数前的l_index值。
4、对于每个prime[l_index],如果对应的x-prime[l_index]不是质数,则将l_index减1继续如此判断,当l_inex=0仍不满足时,l_index从cur_index+1继续开始遍历。用这样的算法是因为,通过穷举发现l_index一般在cur_index附近或较小处满足猜想性质,因此先将l_index从cur_index开始向前遍历,后从cur_index向后遍历。
具体算法如下:
/*依次验证每个偶数x*/
for(x=4;;x=x+2)
{
for(l_index=cur_index;l_index>-1;--l_index)
/*y=x-左质数;*/
if(y也是质数)
按格式输出两个质数;
if(l_index==-1)
{
for(l_index=++cur_index;prime[l_index]<x/2+1;++l_index)
if(y也是质数)
{按格式输出两个质数;break;}
if(对应l_index的所有y都不是质数)
输出x不满足猜想性质;
}
}
三、穷举算法实现
以下程序在VC++2008下编译通过能成功运行:
/*
* 2012.03.25 by PC Jiang
*/
#include<stdafx.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<conio.h>
#define P_SIZE 100
/*如果x可以写成两个素数的和,输出左右两个素数,并返回true*/
bool is_Output(int *prime, //prime数组存储素数集合
int x, //任意大于2的偶数
int l_index, //左素数的游标
int &r_index); //右素数的游标
/*判断右素数r_prime是否是素数,如果是,存储到prime数组,并返回true*/
bool is_Prime(int *prime, //prime数组存储素数集合
int r_prime, //右素数
int &r_index); //右素数所在prime数组游标,注意是引用
int main()
{
int *prime=(int *)malloc(sizeof(int)*P_SIZE);//素数集合,1,3,5,7……;
prime[0]=1; //前两个素数是1,3,暂时不考虑2
prime[1]=3;
int l_index=1,r_index=1; //左素数和右素数所在prime数组的游标
int cur_index=1; //遍历prime数组的当前游标
int x; //任意大于2的偶数
bool ret_is_output; //is_output()的返回值
/*从4开始,判断偶数是否可以分成两个素数和,如果是,输出那两个素数*/
for(x=4;;x=x+2)
{
/*从当前游标开始向前遍历prime数组*/
for(l_index=cur_index;l_index>-1;--l_index)
{
/*如果x是素数,输出并开始判断下个偶数x*/
if(is_Output(prime,x,l_index,r_index))
break;
}
/*如果向前遍历完prime数组,不能判断偶数x*/
if(l_index==-1)
{
/*从遍历前的游标开始再向后遍历prime数组*/
for(l_index=++cur_index;prime[l_index]<x/2+1;++l_index)
{
ret_is_output=is_Output(prime,x,l_index,r_index);
/*如果偶数x能写成两个素数的和*/
if(ret_is_output)
{
break;
}
}
if(!ret_is_output)
{
printf("偶数%d不可以写成两个素数的和\n",x);
getch();
}
}
}
getch();
return 0;
}
/*如果x可以写成两个素数的和,输出左右两个素数,并返回true*/
bool is_Output(int *prime, //prime数组存储素数集合
int x, //任意大于2的偶数
int l_index, //左素数的游标
int &r_index) //右素数的游标,注意是引用
{
int r_prime=x-prime[l_index]; //右素数=x-左素数
/*如果x是素数*/
if(is_Prime(prime,r_prime,r_index))
{
printf("%d %d %d\n",x,prime[l_index],r_prime);
return true;
}
else
return false;
}
/*判断右素数r_prime是否是素数,如果是,存储到prime数组,并返回true*/
bool is_Prime(int *prime, //prime数组存储素数集合
int r_prime, //右素数
int &r_index) //右素数所在prime数组游标,注意是引用
{
int sqrt_rprime=sqrt((double)r_prime)+1;//计算r_prime的平方根,加1后作为下面循环边界值
/*r_prime分别除了小于它的所有数,如果没有可以整除的数,则是素数,否则不是*/
for(int i=2;i<sqrt_rprime ;++i)
if(r_prime%i==0)
return false;
/*从游标2开始存储,当r_prime不在prime中时,存储*/
if(r_index>0&&prime[r_index]!=r_prime&&r_prime!=1&&r_prime!=3)
{
r_index++; //如果是素数,游标加1
/*如果prime数组溢出,需要重新分配内存*/
if(r_index%P_SIZE==0)
prime=(int *)realloc(prime,sizeof(int)*P_SIZE);
prime[r_index]=r_prime; //将素数r_index存储在prime中
}
return true;
}
运行结果如下图(按照机器内存大小,适当分配P_SIZE,我将其值设置为100000000,运行了一个晚上):
四、程序复杂度分析
最坏时间复杂度是O(n2.5),当n非常大时,效率是非常低的。
五、问题
1、realloc()在vc++2008下运行第二次就会出现异常,所以如果想让穷举数字更多的话,只能一次开辟更大的内存空间。
2、当x非常大时,普通pc机运行效率非常慢,需要改进算法的时间复杂度。