求解素数

编程珠玑2-性能监视工具 这个章节主要介绍利用性能监测工具来加速计算素数的程序,因为平时用java,因此调研了下java的性能监测工具,了解了下JFR,jmc自带JFR,安装jmc6.0试用了下,jmc6.0和之前的版本有些区别,可以参考https://stackoverflow.com/questions/50800070/java-mission-control-jmc-6-0-does-not-show-hot-methods-when-examining-a-jfr-fl ,不过JFR只能监测到方法级别,本篇暂且先关注求素数这个算法。
程序p1-p5的优化过程书中已经介绍的很清楚了,这里直接写出java版本的p6的程序如下:

    /**
     * 功能:普通方法找出小于n的素数
     * */
    private static int[] findPrimeNumbersBruteForce(int n) {
        int count = 0;
        //已经找到的素数集合
        int[] primeNumbers = new int[n];
        int pos = 0;
        for (int i = 2; i <= n; i++) {
            boolean isPrime = true;
            for (int j=0; j < pos && (long)(primeNumbers[j] * primeNumbers[j]) <= i; j++) {
                //count ++;
                //System.out.println("i  : " + i + ", primeNumbers[j] : " + primeNumbers[j]);
                if (i % primeNumbers[j] == 0) {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime) {
                primeNumbers[pos++] = i;
            }
        }
        //System.out.println("findPrimeNumbersBruteForce count : " + count);
        return primeNumbers;
    }

习题2中提出问题:你能进一步提高写出比性能吗?根据提示,了解埃氏筛法(Sieve of Eratosthenes)。
埃氏筛法是一种由古希腊数学家埃拉托斯特尼(公元前250年)提出的一种简单检定素数的算法。算法思想是从1~n中依次删除2的倍数,3的倍数,.......,从而得到所有的素数。java代码实现如下所示:

    /**
     * 功能:一般埃氏筛法,找出小于等于n的素数
     * */
    private static int[] findPrimeNumbersSieve(int n) {

        boolean[] mark = new boolean[n+1];
        for(int i = 2; i <= n; i++){
            mark[i] = true;
        }

        int i, count = 0;
        int[] primeNumbers = new int[n];
        for (i = 2; i * i <= n; i++) {    //这层可以看作是素数的迭代,因此 i * i <= n                 
            if (mark[i]) {
                primeNumbers[count++] = i;
                for (int j = i * i; j <= n; j += i) {
                    mark[j] = false;
                }
            }
        }

        for (; i <= n; i++) {
            if (mark[i]) {
                primeNumbers[count++] = i;
            }
        }
        return primeNumbers;
    }

埃氏筛法复杂度为O(nlnlnn)(证明过程可从文末参考资料中了解),因此埃氏筛法并不是线性筛法。一个合数会被标记多次,比如30,会被素数2,3,5各标记一次,如果一个合数质因子很多,那么会被标记很多次。对埃氏筛法进行改进,称为线性筛法(也有叫欧拉筛法),java代码实现如下:

/**
     * 功能:线性筛法,找出小于等于n的素数
     * */
    private static int[] findPrimeNumbersLinearSieve( int n ) {
        int count = 0;
        boolean[] mark = new boolean[n+1];
        for (int i = 2; i <= n; i++) {
            mark[i] = true;
        }

        int[] primeNumbers = new int[n];
        int pos = 0;
        for (int i = 2 ; i <= n ; i++) { //这层是1...n的迭代
            if (mark[ i ] )
                primeNumbers[pos++] = i;
            for(int j = 0 ; j < pos && i * primeNumbers[j] <= n; j++){
                mark[ i * primeNumbers[j]] = false;
                //count++;
                //System.out.println("i  : " + i + ", primeNumbers[j] : " + primeNumbers[j]);
                if( i % primeNumbers[j] == 0 )          //保证了一个合数只被最小的素因子标记
                    break;
            }
        }
        //System.out.println("findPrimeNumbersLinearSieve count : " + count);
        return primeNumbers;
    }

线性筛法的复杂度为O(n),线性筛法解决了一个合数被标记多次的问题,思路是一个合数只由最小素因子标记,重点在if( i % primeNumbers[j] == 0 ) break; 这行代码上:
primeNumbers中的素数是自增的,
如果i % primeNumbers[j] == 0,即i = k * primeNumbers[j],那么假设 x= i * primeNumbers[j+1] = k * primeNumbers[j] * primeNumbers[j+1] = k' * primeNumbers[j],因此 x的最小素因子是primeNumbers[j], 而不是primeNumbers[j+1],接下来的素数同理,所以x不用质数primeNumbers[j+1]标记。

有没有觉得和普通筛法(findPrimeNumbersBruteForce(int n))很像???但性能却相差很大,本机测试当n=100000000时,findPrimeNumbersBruteForce(int n)耗时20s+,而线筛的只用1s左右。

数学小知识复习
调和级数:调和级数是各项倒数为等差数列的级数,通常指:
1 + 1/2 + 1/3 + 1/4 + 1/5 + 1/6 + 1/7 + 1/8 +...
而 1 + 1/2 + 1/3 + 1/4 + 1/5 + 1/6 + 1/7 + 1/8 +... = ln(n) + C, C为欧拉常数数值是0.5772……

为什么1不是素数?
根据算术基本定理,每一个比1大的整数,要么本身是一个素数,要么可以写成唯一一组素数的乘积,最小的素数是2。如果1是素数,那么这一系列素数就不唯一了,会造成很多麻烦。

参考文章:
http://www.cnblogs.com/dc93/p/3930362.html, https://blog.csdn.net/OIljt12138/article/details/53861367
http://www.cnblogs.com/zhuohan123/p/3233011.html
Mertens' 2nd theorem

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

  • 什么是素数 素数又称质数,是指大于1的自然数中,除了1和它本身,不能被其它自然数整除的数字。1被定义为非素数。大于...
    程点阅读 7,188评论 1 7
  • 身体一直很硬朗的父亲突然住院了。得知这个消息是哥哥通知的。 他在电话里语速很快:“妹妹,爸爸住院了,脑...
    九猫猫阅读 274评论 0 3
  • 社会性懈怠 马克西米利安·林格尔曼是一位法国工程师。1913年,他对马拉车的效率进行调查。他发现:两匹马一起拉一驾...
    君途漫漫阅读 347评论 1 0
  • 知识点: 1、指导师搜集资料时,应围绕进行的几个问题。2、有效的指导目标应具备的特征。3、心理指导的定义。4、指导...
    孙秋军阅读 251评论 0 0