zoukankan      html  css  js  c++  java
  • 哥德巴赫猜想穷举验证算法及实现

      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机运行效率非常慢,需要改进算法的时间复杂度。

  • 相关阅读:
    装饰器
    内置函数
    文件操作
    函数
    数据结构[总结笔记]
    汉诺塔解题思路
    springboot事物
    mysql5.7.29 zip包安装教程
    mysql常用语句【转载】
    springboot+mysql+jpa+sharding-jdbc+druid读写分离
  • 原文地址:https://www.cnblogs.com/jpcflyer/p/2417478.html
Copyright © 2011-2022 走看看