zoukankan      html  css  js  c++  java
  • 求素数方法的改进

    素数的定义

      素数一般指质数。质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

    题目:

    求出2-10000之间的素数总个数,并打印出该总数和最后一个素数的值。

    思路1,根据定义来求。

    数字2只有1和2两个因数,因而必定是素数,其他数字x只要判定从2到x-1都无法被它整除,就证明该数字是素数。

     1 /*************************************************************************
     2         > File Name: prime_version1.c
     3         > Author: yudongqun
     4         > Mail: qq2841015@163.com
     5         > Created Time: Tue 03 Nov 2020 04:47:47 PM CST
     6  ************************************************************************/
     7 #include <stdio.h>
     8 #include <time.h>
     9 int is_prime(int n) {
    10     if (n == 0 || n == 1) {
    11         return 0;
    12     }
    13     for (int i = 2; i < n; i++) {
    14         if (n % i == 0) return 0;
    15     }
    16     return 1;
    17 }
    18 
    19 int main(void) {
    20     int t1 = clock();
    21     for (int i = 2; i <= 1000; i++) {
    22         if (is_prime(i)) {
    23             i != 2 && printf(" ");
    24             printf("%d", i);
    25         }
    26     }
    27     printf("
    ");
    28     int t2 = clock();
    29     printf("运行时间: %d ticks
    ", t2 - t1);
    30     return 0;
    31 }

    编译并运行。

    ydqun@VM-0-9-ubuntu prime % gcc prime_version1.c -o version1                                                                                                                                              [0]
    ydqun@VM-0-9-ubuntu prime % ./version1                                                                                                                                                                    [0]
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
    运行时间: 494 ticks
    

    运行结果耗时494个cpu ticks

    这里我们仔细分析,得到如果传入的的参数n是素数,我们要判断n-2次,时间复杂度为O(n),所以当n非常大的时候,效率其实是很低的。我们再仔细分析,其实一个整数,去除以一个比它值一半还大的整数的时候,其实是除不尽的,故我们根本没必要遍历到n-1,只需要遍历到n的一半就可以了。接下来给出改进代码。

    /*************************************************************************
            > File Name: prime_version2.c
            > Author: yudongqun
            > Mail: qq2841015@163.com
            > Created Time: Tue 03 Nov 2020 04:47:47 PM CST
     ************************************************************************/
    #include <stdio.h>
    #include <time.h>
    int is_prime(int n) {
        if (n == 0 || n == 1) {
            return 0;
        }
        for (int i = 2; i <= n >> 1; i++) {
            if (n % i == 0) return 0;
        }
        return 1;
    }
    
    int main(void) {
        int t1 = clock();
        for (int i = 2; i <= 1000; i++) {
            if (is_prime(i)) {
                i != 2 && printf(" ");
                printf("%d", i);
            }
        }
        printf("
    ");
        int t2 = clock();
        printf("运行时间: %d ticks
    ", t2 - t1);
        return 0;
    }

    编译并运行。

    ydqun@VM-0-9-ubuntu prime % gcc  prime_version2.c -o version2                                                                                                                                             [1]
    ydqun@VM-0-9-ubuntu prime % ./version2                                                                                                                                                                    [0]
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
    运行时间: 364 ticks
    

    这时候,优化过后的代码耗时只需要364ticks,这里,我们还可以得出除了2之外的其他偶数,其实都不是素数,所以我们还可以进一步优化。继续给出优化版本。

     1 /*************************************************************************
     2         > File Name: prime_version1.c
     3         > Author: yudongqun
     4         > Mail: qq2841015@163.com
     5         > Created Time: Tue 03 Nov 2020 04:47:47 PM CST
     6  ************************************************************************/
     7 #include <stdio.h>
     8 #include <time.h>
     9 int is_prime(int n) {
    10     if (n == 0 || n == 1 || n % 2 == 0) {
    11         return 0;
    12     }
    13     for (int i = 3; i <= n >> 1; i += 2) {
    14         if (n % i == 0) return 0;
    15     }
    16     return 1;
    17 }
    18 
    19 int main(void) {
    20     int t1 = clock();
    21     for (int i = 2; i <= 1000; i++) {
    22         if (is_prime(i)) {
    23             i != 2 && printf(" ");
    24             printf("%d", i);
    25         }
    26     }
    27     printf("
    ");
    28     int t2 = clock();
    29     printf("运行时间: %d ticks
    ", t2 - t1);
    30     return 0;
    31 }

    编译并运行。

    ydqun@VM-0-9-ubuntu prime % gcc prime_version3.c -o version3                                                                                                                                            [130]
    ydqun@VM-0-9-ubuntu prime % ./version3                                                                                                                                                                    [0]
     3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
    运行时间: 163 ticks
    

    另外其实只需要从2遍历到√n就可以了,因为如果n可以被一个数整除,那么其中一个数一定小于等于n开方,另一个大于等于n的开方。下面给出代码。

     1 /*************************************************************************
     2         > File Name: prime_version4.c
     3         > Author: yudongqun
     4         > Mail: qq2841015@163.com
     5         > Created Time: Tue 03 Nov 2020 04:47:47 PM CST
     6  ************************************************************************/
     7 #include <stdio.h>
     8 #include <time.h>
     9 #include <math.h>
    10 int is_prime(int n) {
    11     if (n == 0 || n == 1 || n % 2 == 0) {
    12         return 0;
    13     }
    14     for (int i = 3; i <= sqrt(n); i += 2) {
    15         if (n % i == 0) return 0;
    16     }
    17     return 1;
    18 }
    19 
    20 int main(void) {
    21     int t1 = clock();
    22     for (int i = 2; i <= 1000; i++) {
    23         if (is_prime(i)) {
    24             i != 2 && printf(" ");
    25             printf("%d", i);
    26         }
    27     }
    28     printf("
    ");
    29     int t2 = clock();
    30     printf("运行时间: %d ticks
    ", t2 - t1);
    31     return 0;
    32 }

    编译并运行。

    ydqun@VM-0-9-ubuntu prime % gcc prime_version4.c  -lm                                                                                                                                                     [0]
    ydqun@VM-0-9-ubuntu prime % ./a.out                                                                                                                                                                       [0]
     3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
    运行时间: 110 ticks
    

    现在,执行所耗的时间仅为110ticks了。也许你认为这已经是最快的求法了,但其实如果我们所求的范围特别大的话,这种求法效率还是欠佳。在数据结构与算法中,有一种思想叫做空间换取时间,就是利用内存花销去换取时间花销。而求素数的方法中,有一种叫做素数筛的方法用的就是这种思想。下面直接给出代码。

     1 /*************************************************************************
     2         > File Name: prime_version5.c
     3         > Author: yudongqun
     4         > Mail: qq2841015@163.com
     5         > Created Time: Wed 04 Nov 2020 08:51:09 AM CST
     6  ************************************************************************/
     7 //prime数组中 下标表示某个数,而该下标对应的元素如果是1表示是非素数,如果是0表示素数
     8 //这里的思想是用素数去标记合数
     9 #include <time.h>
    10 #include <stdio.h>
    11 #define MAX_N 1000
    12 void prime_init(int *prime, int max_n) {
    13     for (int i = 2; i <= max_n; i++) {
    14         if (prime[i]) continue;
    15         for (int j = 2 * i; j <= max_n; j += i) {
    16             prime[j] = 1;
    17         }
    18     }
    19 }
    20 
    21 int main(void) {
    22     int t1 = clock();
    23     int prime[MAX_N + 5] = {0};
    24     prime_init(prime, MAX_N);
    25     for (int i = 2; i < MAX_N; i++) {
    26         if (!prime[i]) {
    27             printf("%d ", i);
    28         }
    29     }
    30     printf("
    ");
    31     int t2 = clock();
    32     printf("运行时间: %d ticks
    ", t2 - t1);
    33     return 0;
    34 }

    编译并运行。

    ydqun@VM-0-9-ubuntu prime % gcc prime_version5.c -o v5                                                                                                                                                    [0]
    ydqun@VM-0-9-ubuntu prime % ./v5                                                                                                                                                                          [0]
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
    运行时间: 86 ticks
    
    素数筛
    证明过程:

    从2开始删去素数本身倍数,向后找到的第一个数字一定是素数。

    证明:简略一说,设已找到第n个素数,删去此数自身倍数后找到剩下的第一个数字L,知L之前有且仅有n个素数,且都无法整除L,即L无法被小于自身的所有素数整除,推出L是素数(L就是第n+1个素数)。照此由第一个素数2往后递推即可

    思路
    1、标记一个范围内的数字是否是合数,没有被标记的则为素数;
    2、算法的空间复杂度为O(N),时间复杂度为O(loglogN * N);
    3、总体思想是用素数去标记掉不是素数的数字,例如我知道了i是素数,那么2 *i、3 * i、4 * i、......就都不是素数。
     做法
    1、用prime[i]来标记i是否是合数
    2、标记为1的数字为合数,否则为素数
    3、第一次知道2是素数,则将2的倍数标记为1
    4、向后找到第一个没有被标记的数字i
    5、将i的倍数全部标记为合数
    6、重复4--6步,知道标记2~1000范围内的所有数字

  • 相关阅读:
    Linux:备份
    在 Cordova/Phonegap for Android 中包含中文文件名的页面
    jQuery插件开发
    为Google Reader守夜。。。
    冒泡排序
    无题六月
    XXX读后感
    KL25的AD采集操作
    工作流--JBPM简介及开发环境搭建
    内存错误:CRT detected that the application wrote to memory after end of heap buffer
  • 原文地址:https://www.cnblogs.com/ydqblogs/p/13921263.html
Copyright © 2011-2022 走看看