zoukankan      html  css  js  c++  java
  • 计算素数的算法(一)

    经常有初学者询问求解N内所有素数(质数)的问题,对此,网上的解答也很多,但很多要么不够专业,要么只有程序没有算法解析,所以三藏大厦对此问题做个小结,探讨一下求解素数的常见算法,同时给出相应的C语言程序及其解析。为了方便初学者理解,本文将从易到难阐述不同算法,高手可以直接看后面的高效算法
    质数的定义
    一个数,如果只有1和它本身两个因数,这样的数叫做质数,又称素数。 

    试除判断法
    算法描述:从上述定义可知,素数不能被1和它本身之外的数整除,所以,判断一个数x是否素数只要看它是否能被2~sqrt(x)间的数整除即可;而求N内所有素数则是循环重复上述过程。
    C语言实现

    复制内容到剪贴板

    代码:

    #include <time.h>
    #include <malloc.h>
    #define N 100000
    //
     简单试除判断法 Ver1
    int s i m p l eDivisionV1(int n)
    {
    int i,j;
    //
     素数数量统计 
    int count = 0;
    //
     分配存放结果的空间 
    int* primes = (int*)malloc( sizeof(int)*n );

    // 2
    是素数谁都知道,不算了 
    primes[count++] = 2;
    //
     循环计算3~n间的数 
    for (i=3; i<=n; i++)
    {
      //
     为什么是sqrt(i),思考一下 
      for (j=2; j<=sqrt(i); j++)
      {
       // i
    j整除,显然不是素数了 
       if (i%j == 0) break;
      }
      // i
    不能被2~sqrt(i)间的数整除,素数也 
      if (j > sqrt(i))
      {
       primes[count++] = i;
      }
    }

    //
     因输出费时,且和算法核心相关不大,故略
      
    //
     释放内存,别忘了传说中的内存泄漏 
    free(primes);

    return count;
    }

    void main()
    {
    int count;
    clock_t start, end;
    // time
    函数不够精确,用clock凑合一下吧 
    start = clock();
    count = s i m p l eDivisionV1(N);

    end = clock();
    printf("[%d]
    以内素数个数:%d, 计算用时:%d毫秒 ", N, count, end-start);
    getch();
    }

    计算结果:
    [100000]
    以内素数个数:9592, 计算用时:468毫秒
    [1000000]
    以内素数个数:78498, 计算用时:10859毫秒
    [5000000]
    以内素数个数:348513, 计算用时:103560毫秒
    噢噢,算算十万还行,百万就10秒多了,而且时间增长很快,这不行,得优化一下!
    优化分析
    仔细研究一下s i m p l eDivisionV1我们可以发现以下几个问题:

    1.     在循环条件中重复调用sqrt(i)显然是比较浪费时间的

    2.     判断素数,真的需要拿2~sqrt(i)间的所有整数去除吗?我们知道,合数都可以分解成若干质数,所以只要2~sqrt(i)间的质数不能整除i即可

    根据上面两点,我们可将s i m p l eDivisionV1升级为s i m p l eDivisionV2,如下

    复制内容到剪贴板

    代码:

    // 简单试除判断法 Ver2
    int s i m p l eDivisionV2(int n)
    {
    int i, j, k, stop;
    //
     素数数量统计 
    int count = 0;
    //
     分配存放结果的空间 
    int* primes = (int*)malloc( sizeof(int)*n );

    // 2
    是素数谁都知道,不算了 
    primes[count++] = 2;
    stop = count;
    //
     循环计算3~n间的数 
    for (i=3; i<=n; i++)
    {
      k = sqrt(i);
      //
     在循环条件中重复调用sqrt是低效做法,故引入k
      while (primes[stop] <= k && stop < count)
       stop++;
      // stop
    干什么用,思考一下
      for (j=0; j<stop; j++)
      {
       if (i%primes[j] == 0) break;
      }
      // i
    不能被2~sqrt(i)间的素数整除,自然也不能被其他数整除,素数也 
      if (j == stop)
      {
       primes[count++] = i;
      }
    }

    //
     因输出费时,且和算法核心相关不大,故略
      
    //
     释放内存,别忘了传说中的内存泄漏 
    free(primes);

    return count;
    }

    然后将main中调用的函数替换为s i m p l eDivisionV2,在看一下执行结果:
    [100000]
    以内素数个数:9592, 计算用时:46毫秒
    [1000000]
    以内素数个数:78498, 计算用时:546毫秒
    [5000000]
    以内素数个数:348513, 计算用时:3515毫秒
    [10000000]
    以内素数个数:664579, 计算用时:8000毫秒
    很开心的看到,经过优化,速度提高了几十倍,尤其是时间增长曲线的坡度变小了,N值越大,V2函数比V1的效率就越高
    对于试除判断这种质数算法来说,三藏认为s i m p l eDivisionV2基本已经接近极限,不大可能有量级上的突破了,有兴趣的朋友可以自己进一步优化。初学者除了参看上述例子外,可以尝试做各种修改及细节优化,也可以将除法变乘法,多加练习是学习编程的好方法。
    虽然,上例中V2已经比V1快了很多了,但随着N的增大,耗时还是不少,那么我们还有更好的方法吗?


  • 相关阅读:
    Spark Scala 读取GBK文件的方法
    Mac OS X 系统下自带的文本文件格式转换工具iconv
    报到
    java 字符串中含有双引号" "与单引号' '问题
    div1嵌套div2,div2居中的解决办法
    ionic4 创建 angular项目 ReactNative下载第三方库出错解决Error: EPERM: operation not permitted, rename
    ionic+cordova 创建项目+打包
    jxl读取excel文件异常:Unable to recognize OLE stream 的解决方法
    学习 javascript (一)javascript 简介
    学习 JavaScript (四)核心概念:操作符
  • 原文地址:https://www.cnblogs.com/BBOOT/p/3812670.html
Copyright © 2011-2022 走看看