1. 程式人生 > >c語言:實現對於給定的正整數N,依次打印出小於等於N的所有素數。兩種方法及其優化

c語言:實現對於給定的正整數N,依次打印出小於等於N的所有素數。兩種方法及其優化

編寫一個程式實現對於給定的整數N,依次打印出小於等於N的所有素數。

方法一:試除法

由素數的定義到如下程式

#include<stdio.h>

int print_prime(int num)//prime表示素數

{

int i = 0;

for (i = 2; i <= num; i++)

{

int j = 0;

for (j = 2; j < i; j++)

{

if (i%j == 0)

{

break;

}

}

if (i==j)

{

printf("%d\t",i);

}

}

}

int main()

{

int num;

printf("請輸入一個正整數:");

scanf("%d", &num);

if (num > 1)

{

printf("打印出所有的素數:");

print_prime(num);

}

else

{

printf("不存在小於等於%d的素數", num);

}

printf("\n");

return 0;

}

上面的程式碼中,在判斷素數時一直從2試除到n-1。這裡從n/2之後的數到n-1的試除顯然是多餘的,比如正整7不能被2整除,必然不能被4,6整除。

優化1:

試除的範圍優化到[2,n/2],工作量減少一半,程式碼如下:

int print_prime(int num)//prime表示素數

{

int i = 0;

for (i = 2; i <= num; i++)

{

int j = 0;

for (j = 2; j <= i/2; j++)//修改部分

{

if (i%j == 0)

{

break;

}

}

if (j == (i/2+1))//修改部分

{

printf("%d\t", i);

}

}

}

    既然能將試除範圍優化到[2,n/2],那麼這個範圍是不是還能繼續優化呢?答案是肯定的,在[2,n/2]這個範圍裡(√n,n/2]的試除也是多餘的。因為因數是成對出現的,如16可分解為:1和16 、2和8、4和4、8和2、16和1。這些因數裡必然有一個小於等於4。只需試除小於等於√n的數就可以了。

優化2:

    試除範圍優化為[2,√n],程式碼如下:

#include<stdio.h>

#include<math.h>//修改部分

int print_prime(int num)//prime表示素數

{

int i = 0;

for (i = 2; i <= num; i++)

{

int j = 0;

for (j = 2; j <=sqrt(i); j++)//修改部分

{

if (i%j == 0)

{

break;

}

}

if (j > sqrt(i))//修改部分

{

printf("%d\t", i);

}

}

}

    上面所有的程式碼在找素數的時候是從2到n,在這個範圍內除了2之外的偶數都不是素數,所以可以跳過這些偶數。

    還有試除範圍內除了2之外的偶數也是沒有必要的,因為如果不能被2整除,必然不能被大於2的偶數整除。

優化3:

尋找素數時試除範圍跳過除2之外的偶數。程式碼如下:

int print_prime(int num)//prime表示素數

{

int i = 0;

printf("%d\t", 2);//修改部分

for (i = 3; i <= num; i+=2)//修改部分

{

int j = 2;

for (j = 3; j <= sqrt(i); j+=2)//修改部分

{

if (i%j == 0)

{

break;

}

}

if (j > sqrt(i))

{

printf("%d\t", i);

}

}

}

在上面的程式碼中,試除範圍內的一些數也是不必要的。比如判斷101是否為素數時,要分別試除小於10的2和所有奇數,即2、3、5、7、9,其中對9的試除是不必要的。即對所有的非素數的試除是不必要的,因為非素數必然可分解為比它小的素數的乘積,既然它的質因數不能整除某個數,這個數必然也不能。故試除的範圍可縮小到小於等於√n的所有素數。

優化4:

只試小於√n的素數,那麼問題來了,要試除這些素數時必然要將前面求出的素數儲存起來,開闢多大一塊空間合適呢?因為n的大小未知,所以無法確定開闢多少空間。這裡暫時沒有完全解決的辦法,我的做法是先開闢大小為1000的空間,需要時再修改。

#include<stdio.h>

#include<math.h>

#define MAX 1000  //定義陣列的大小

int print_prime(int num)//prime表示素數

{

int arr[MAX] = { 0 };

int i = 0,j=1;

printf("%d\t", 2);

arr[0] = 2;

for (i = 3; i <= num; i += 2)

{

int k = 0;

while (arr[k]>0&&arr[k]<=sqrt(i))

{

if (i%arr[k] == 0)

{

break;

}

k++;

}

if (!arr[k]||(arr[k] > sqrt(i)))

{

printf("%d\t", i);

arr[j] = i;

j++;

}

}

}

int main()

{

int num;

printf("請輸入一個正整數:");

scanf("%d",&num);

if (num > 1)

{

printf("打印出所有的素數:");

print_prime(num);

}

else

{

printf("不存在小於等於%d的素數",num);

}

printf("\n");

return 0;

}

方法二:篩

這種方法求素數的思想就是,不斷篩去最小的數的倍數。這個最小的數必然是素數。

    比如最小的素數是2,去掉所有2的倍數;接下來最小的數是3,3就是素數,去掉所有的3的倍數;依次類推,直到最小的數小於等於√n為止。為什麼是√n呢? 在上面的試除法中講到只要試除小於等於√n的所有素數即可判斷出小於等於n的所有素數,這裡同樣適用,只要去掉所有的小於等於√n的所有數的倍數,剩下的數就是小於等於n的所有素數。

程式碼如下:

#include<stdio.h>

#include<math.h>

#define MAX 1000  //定義陣列的大小

int print_prime(int num)//prime表示素數

{

int arr[MAX] ;

int i = 0,j=0;

for (i = 0; i < num - 2; i++)//初始化陣列[2,num]

{

arr[i] = i + 2;

}

while (arr[j]<=sqrt(num))//除數的範圍

{

for (i = j + 1; i < num - 1; i++)

{

if (arr[i]%arr[j] ==0)//篩去arr[i]的倍數

{

arr[i] = 0;

}

}

j++;

while(!arr[j])//確定最小數

{

j++;

}

}

for (i = 0; i < num - 1; i++)

{

if (arr[i])

{

printf("%d\t",arr[i]);

}

}

}

int main()

{

int num;

printf("請輸入一個正整數:");

scanf("%d",&num);

if (num > 1)

{

printf("打印出所有的素數:");

print_prime(num);

}

else

{

printf("不存在小於等於%d的素數",num);

}

printf("\n");

return 0;

}

    上面的程式碼有一個很明顯的缺陷就是開闢空間過大,如何來解決這個問題呢?

    上述程式碼所開闢的空間為int型,佔用4個位元組,佔用空間太多,可以構造一個bool型陣列,bool型是一個基本的資料型別,佔1個位元組,以下標來儲存資料,節省了75%的空間。

 優化

構造bool型陣列,以下標來儲存資料,每個數只佔一個位元組。

程式碼如下:

#include<stdio.h>

#include<stdbool.h>

#include<math.h>

#define MAX 1000  //定義陣列的大小

void print_prime(int num)//prime表示素數

{

bool arr[MAX] ;

int i = 0,j=2;

for (i = 0; i <num ; i++)//初始化陣列為真

{

arr[i] = 1;

}

while (arr[j]<=sqrt(num))//除數的範圍

{

for (i = j + 1; i <= num ; i++)

{

if (i%j ==0)//篩去arr[i]的倍數

{

arr[i] = 0;

}

}

j++;

while(!arr[j])//確定最小數

{

j++;

}

}

for (i = 2; i <=num ; i++)//列印素數

{

if (arr[i])

{

printf("%d\t",i);

}

}

}

int main()

{

int num;

printf("請輸入一個正整數:");

scanf("%d",&num);

if (num > 1)

{

printf("打印出所有的素數:");

print_prime(num);

}

else

{

printf("不存在小於等於%d的素數",num);

}

printf("\n");

return 0;

}

結果1:

請輸入一個正整數:9

打印出所有的素數:2     3       5       7

請按任意鍵繼續. . .

結果2:

請輸入一個正整數:1

不存在小於等於1的素數

請按任意鍵繼續. . .