《编程珠玑》看书笔记

临睡前翻看了下《编程珠玑(续)》这本书,看到第一章就被吸引了,性能监视工具这节从计算素数入手。题目是:
打印所有小于1000的素数
简单直白的方法就是,针对每个小于1000的数字n,从2开始到n-1,如果能被任意一个数整除,那它就不是素数。代码如下:

    int prime(int n)
    {
        int i;
        for(i = 2; i < n; i++)
        {
            if(n % i == 0)
                return 0;
        }
        return 1;
    }
    int main()
    {
        int i, n ;
        n = 1000;
        for(i = 2; n <= n; i++)
        {
            if(prime(i))
                printf("%d\n", i);
        }
    }

这里存在可优化的地方,优化的地方就是根本不需要计算到n-1这个大小。因为根据数论的理论,只要求得至多不大于N的整型数即可得出这个数是否为素数:
如下为理论证明:
第一,对于一个自然数N,只要能被一个非1非自身的数整除,它就肯定不是素数,所以不
必再用其他的数去除。
第二,对于N来说,只需用小于N的素数去除就可以了。例如,如果N能被15整除,实际
上就能被3和5整除,如果N不能被3和5整除,那么N也决不会被15整除。
第三,对于N来说,不必用从2到N一1的所有素数去除,只需用小于等于√N(根号N)的所有素数去除就可以了。这一点可以用反证法来证明:
如果N是合数,则一定存在大于1小于N的整数d1和d2,使得N=d1×d2。
如果d1和d2均大于√N,则有:N=d1×d2>√N×√N=N。
而这是不可能的,所以,d1和d2中必有一个小于或等于√N。
因此可以只求得N之内的数即可判断该数是不是素数:

    int root(int n)
    { return (int) sqrt((float) n);}
    int prime(int n)
    {
        int i, bound;
        bound = root(n);
        for(i = 2; i <= bound; i++)
        {
            if(n % i == 0)
                return 0;
        }
        return 1;
    }
    int main()
    {
        int i, n ;
        n = 1000;
        for(i = 2; n <= n; i++)
        {
            if(prime(i))
                printf("%d\n", i);
        }
    }

可是这个方法还不是特别好,因为求平方根的操作比较耗费时间,因此如果能把这个时间省略就更好了,因此想到了如下办法:

    int root(int n)
    { return (int) sqrt((float) n);}
    int prime(int n)
    {
        int i, bound;
        bound = root(n);
        for(i = 2; i * i <= n; i++)
        {
            if(n % i == 0)
                return 0;
        }
        return 1;
    }
    int main()
    {
        int i, n ;
        n = 1000;
        for(i = 2; n <= n; i++)
        {
            if(prime(i))
                printf("%d\n", i);
        }
    }

是不是很惊喜啊,这样运算速度又快了一步,不过还不是最优的,试想将一个数先判断是否能被2,3,5整除,那么这个数肯定不是素数,这样一下子剔除了好多数,然后只考虑奇数的因子,这样时间上又优化了很多


    int prime(int n)
    {
        int i;
        if(n % 2 == 0)
            return (n==2); //一定要排除2本身这个素数,下同
        if(n % 3 == 0)
            return (n == 3);
        if (n % 5 == 0 )
            return (n == 5);
        for(i = 7; i * i<= n; i += 2)
        {
            if(n % i == 0)
                return 0;
        }
        return 1;
    }
    int main()
    {
        int i, n ;
        n = 1000;
        for(i = 2; n <= n; i++)
        {
            if(prime(i))
                printf("%d\n", i);
        }
    }

这样又优化了很多啊,这本书讲的真实厉害!后面还有的优化就是厄拉多塞筛法
简单介绍一下厄拉多塞筛法。厄拉多塞是一位古希腊数学家,他在寻找素数时,采用了一种与众不同的方法:先将2-N的各数放入表中,然后在2的上面画一个圆圈,然后划去2的其他倍数;第一个既未画圈又没有被划去的数是3,将它画圈,再划去3的其他倍数;现在既未画圈又没有被划去的第一个数 是5,将它画圈,并划去5的其他倍数……依次类推,一直到所有小于或等于N的各数都画了圈或划去为止。这时,表中画了圈的以及未划去的那些数正好就是小于 N的素数。
可以用一个数组保存每个元素,数组下标对应元素,此时对这个数组进行标记,如果为某个数的倍数,就将其标记为0,之后,没有被标记的元素就是素数。


    int num[N]; 
    for (int i = 0; i < N; i++)  
        {  
            num[i] = i;  
        }  

       for (int j = 2; j < N; j++)  
       {  
           if(0 != num[j])  
           {  
               for (int k = 2; k*j < N; k++)  
               {  
                   num[k*j] = 0;  
               }  
           }  
       }  

       for (int n = 0; n < N; n++)  
       {  
           if(0 != num[n])  
           {  
               cout<<" "<<num[n];  
           }  
       }  

其实这还可以继续优化,因为用数组表示每个数,当数组范围较大时,占用内存较多,可以采用位图法降低空间消耗。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值