C語言程序設計100例之(12):Eratosthenes篩法求質數

例12   Eratosthenes篩法求質數

問題描述php

Eratosthenes篩法的基本思想是:把某範圍內的天然數從小到大依次排列好。宣佈1不是質數,把它去掉;而後從餘下的數中取出最小的數,宣佈它爲質數,並去掉它的倍數。在第1步以後,獲得質數2,篩中只包含奇數;第2步以後,獲得質數3,一直作下去,當篩中爲空時結束。編程

用Eratosthenes篩法求給定區間內的全部質數。數組

輸入格式框架

兩個整數a和b,其中1≤a≤b≤10000less

輸出格式ide

輸出給定範圍[a,b]間的全部質數,輸出時每一個質數佔6列,每行輸出10個質數。spa

輸入樣例ip

100  200ci

輸出樣例input

101  103  107  109  113  127  131  137  139  149 

151  157  163  167  173  179  181  191  193  197

199

        (1)編程思路。

        下面採用自頂向下逐步求精的方法解決這個問題。

1)先寫出程序的整體框架

初始化,將全部的數都放在篩子中;

k=2;

while(k<=N)

{

     用k將篩子中的數2*k、3*k、4*k …,一一篩去;

      從當前下標k的下一個開始找到下一個仍在篩子中的數,並賦值給k;

}

從2開始,將全部留在篩子中的數(即爲質數)打印出來;

2)篩子的構造

爲了表示一個篩子,並將給定範圍N之內的數放入篩子中,能夠定義一個一維數組

int  prime[N+1];

其中,元素prime[i]==1表示整數i在篩子中;prime[i]==0表示整數i不在篩子中。

所以,初始化數組prime使全部的數都在篩子中,即便prime[2]~ prime[N]的值所有等於1。程序描述爲:

     for (k=2; k<=N;k++)

              prime[k]=1;

3)用k將篩子中的數2*k、3*k、4*k …,一一篩去

n=2;

while(n*k<N)

{

    prime[n*k]=0;

    n++;

 }

4)從當前下標k的下一個開始找到下一個仍在篩子中的數,並賦值給k

k++;

while(prime[k]==0)

     k++;

5)從a開始到b爲止,將仍然在篩子中的數打印出來

for (k=a; k<=b; k++)

{

     if(prime[k])  printf(「%d  「,k);

}

(2)源程序。

#include <stdio.h>

#define N 100000

int main()

{

         int prime[N+1]={0,0},t,k,cnt,a,b;

         for (k=2; k<=N;k++)

                   prime[k]=1;

         k=2;

         while(k<=N)

         {

                   t=2;

                   while(t*k<=N)

                   {

                            prime[t*k]=0;

                            t++;

                   }

                   k++;

                   while(k<=N  && prime[k]==0)

                            k++;

         }

         scanf("%d%d",&a,&b);

         cnt=0;

         for (k=a;k<=b;k++)

        {

            if (prime[k]==1)

           {

                 cnt++;

                printf("%6d",k);

                 if (cnt%10==0)  printf("\n");

          }

     }

    printf("\n");

    return 0;

}

習題12

12-1  質因子分解

        本題選自洛谷題庫 (https://www.luogu.org/problem/P2043)

題目描述

對N!進行質因子分解。

輸入格式

輸入數據僅有一行包含一個正整數N,N<=10000。

輸出格式

輸出數據包含若干行,每行兩個正整數p,a,中間用一個空格隔開。表示N!包含a個質因子p,要求按p的值從小到大輸出。

輸入樣例

10

輸出樣例

2 8

3 4

5 2

7 1

說明/提示

10!=3628800=(2^8)*(3^4)*(5^2)*7

 (1)編程思路。

先看如何求n!中質因子k的個數。以求10!質因子2的個數爲例說明。

設先將1~10共10個數排成一行獲得序列1。且設保存2的因子個數的變量cnt的初值爲0。

    序列1:     1  2  3  4  5  6  7  8  9  10

在序列1中,只有二、四、…、10 共 10/2=5個數中至少含有一個2的因子。故cnt=cnt+n/2=0+5=5。

    將序列1中的每一個數除以2,只保留獲得的整數,可排成序列2。(對應操做爲n=n/2)

    序列2:                 1  2  3  4   5

  對應序列1的數爲:2  4  6   8  10

    在序列2中,有5個數,只有5/2=2個數含有因子2,即原序列中有2個數(4,8)至少含有兩個因子2。  cnt=cnt+n/2=5+5/2=7。

    再將序列2中的每一個數除以2,只保留獲得的整數,可排成序列3。(對應操做爲n=n/2)

    序列3                  1   2    

對應序列1的數爲:4   8

在序列3中,有2個數,只有2/2=1個數含有因子2,即原序列中有1個數(8)至少含有三個因子2。  cnt=cnt+n/2=7+2/2=8。

    再將序列3中的每一個數除以2,只保留獲得的整數,可排成序列4。(對應操做爲n=n/2)

    此時,序列4中再也不有數能被2整除,即原序列中沒有數含有4個2的因子。

       cnt=cnt+n/2=8+1/5=8。  

    至此,求得10!含有質因子2的個數爲8。

按上述過程,將求n!中質因子k的個數寫成一個簡單的循環便可。

        cnt=0;

        while (n!=0)

        {

            cnt+=n/k;

            n/=k;

        }

定義數組int prime[1250]保存全部小於10000的質數,如prime[0]=2,prime[1]=3, prime[2]=5,…。用篩法求出各質數並保存在prime數組中。

定義數組int num[1250],其中num[i]保存n!中質因子prime[i]的個數,num數組的所有元素的初始值置爲0。

程序中按前面介紹的求n!中質因子k的個數的方法,用循環依次求取小於或等於n的質數prime[i]的個數num[i]。

 (2)源程序。

#include <stdio.h>

#define N 10000

int main()

{

    int t,k,i,n,cnt;

         int flag[N+1]={0,0};

         int prime[1250],num[1250]={0};

         for (k=2; k<=N;k++)

                   flag[k]=1;

         k=2; cnt=0;

         while(k<=N)

         {

                   t=2;  prime[cnt++]=k;

                   while(t*k<=N)

                   {

                            flag[t*k]=0;

                            t++;

                   }

                  k++;

                 while(k<=N && flag[k]==0)

                       k++;

         }

         scanf("%d",&n);

         for (i=0;i<cnt;i++)

        {

              if (prime[i]>n) break;

              t=n;

             while (t!=0)

            {

                  num[i]+=t/prime[i];

                  t/=prime[i];

           }

    }

    for (k=0;k<i;k++)

       printf("%d %d\n",prime[k],num[k]);

   return 0;

}

12-2  Prime Gap

        本題選自北大POJ題庫 (http://poj.org/problem?id=3518)

Description

The sequence of n − 1 consecutive composite numbers (positive integers that are not prime and not equal to 1) lying between two successive prime numbers p and p + n is called a prime gap of length n. For example, ‹24, 25, 26, 27, 28› between 23 and 29 is a prime gap of length 6.

Your mission is to write a program to calculate, for a given positive integer k, the length of the prime gap that contains k. For convenience, the length is considered 0 in case no prime gap contains k.

Input

The input is a sequence of lines each of which contains a single positive integer. Each positive integer is greater than 1 and less than or equal to the 100000th prime number, which is 1299709. The end of the input is indicated by a line containing a single zero.

Output

The output should be composed of lines each of which contains a single non-negative integer. It is the length of the prime gap that contains the corresponding positive integer in the input if it is a composite number, or 0 otherwise. No other characters should occur in the output.

Sample Input

10

11

27

2

492170

0

Sample Output

4

0

6

0

114

        (1)編程思路。

        題目的意思是:兩個連續質數a和b之間的區間稱爲非質數區間。求n所在非質數區間的長度。例如,23和29是兩個連續的質數,23和29之間的區間就是一個非質數區間,這個區間的長度爲6,整數27在這個區間中,所以27所在非質數區間的長度爲6。

        定義數組int prime[maxn],元素prime[i]的值爲0表示整數i是質數(在篩子中,沒有被篩掉),prime[i]的值爲1表示整數i是否是質數(不在篩子中,已經被篩掉了)。

        初始時prime的數組元素初值全爲0,表示給定範圍的每一個整數都在篩子中。用篩法將全部的非質數全篩掉。

        爲求取n所在非質數區間的長度。若n自己是一個質數,則其所在非質數區間的長度記爲0。

         若n不是一個質數,可用循環 for (left=n-1; prime[left]==1;  left--);求得比n小的最大質數left;用循環for (right=n+1;prime[right]==1; right++);求得比n大的最小質數right。則n必定在連續質數left和right之間的非質數區間,區間長度爲right-left。

        (2)源程序。

#include <stdio.h>

#define maxn 1399710

int prime[maxn]={0};

int main()

{

    int i,j,n,left,right;   

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

    {

        if(prime[i]==0)

        {

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

              prime[j]=1;

        }

    }

    while (scanf("%d",&n) && n!=0)

    {

        if(prime[n]==0)

        {

            printf("0\n");

            continue;

        }

        else

        {

              for (left=n-1; prime[left]==1;  left--);

              for (right=n+1;prime[right]==1; right++);

              printf("%d\n",right-left);

          }

    }

    return 0;

}

12-3  Largest prime factor

        本題選自杭州電子科技大學OJ題庫 (http://acm.hdu.edu.cn/showproblem.php?pid=2136)

Problem Description

Everybody knows any number can be combined by the prime number.

Now, your task is telling me what position of the largest prime factor.

The position of prime 2 is 1, prime 3 is 2, and prime 5 is 3, etc.

Specially, LPF(1) = 0.

Input

Each line will contain one integer n(0 < n < 1000000).

Output

Output the LPF(n).

Sample Input

1

2

3

4

5

Sample Output

0

1

2

1

3

        (1)編程思路。

本題題意是:求一個整數n的最大質因子在質數表中排第幾。好比,9的最大質因子是3,3在質數表中排第2;5的最大質因子爲5,在質數表中排第3。

定義數組int rank[N],元素prime[i]的值表示整數i的最大質因子在質數表中排第幾。

初始時rank的數組元素初值全爲0,表示還沒有肯定每一個數的最大質因子的排位值。同時,藉助篩法的思想。rank的數組元素初值全爲0,表示給定範圍(1~N)的每一個整數都在篩子中。

在前面介紹的篩法中,咱們只是簡單置數組元素值爲0或爲1,表示在或不在篩子中,本題中rank數組元素值除了表示在或不在篩子中的含義外,非0的元素值還表示最大質因子在質數表中的排位值。爲此,修改的篩法執行過程描述以下:

1)初始時,令i=2,cnt=1,表示最小的質數爲2,其排位值爲1。

2)2<=N,rank[2]=0,cnt=1表示2是排位爲1的質數。同時修改rank[2]、rank[4]、rank[6]、rank[8]、rank[10]……等元素的值爲1(當前cnt=1),這個修改既表示將2的倍數的數從篩子中篩掉,同時表示這些2的倍數的數當前肯定的最大質因子的排位號爲1。 cnt++表示下一個質數的排位值爲2。

3)i++,i=3,此時rank[3]=0表示3在篩子中,3是質數,cnt=2,表示3是排位值爲2的質數。同時修改rank[3]、rank[6]、rank[9]、rank[12]、rank[15]……等元素的值爲2(當前cnt=2),這個修改既表示將3的倍數的數從篩子中篩掉,同時表示這些3的倍數的數當前能肯定的最大質因子的排位號爲2。 cnt++表示下一個質數的排位值爲3。

4)i++,i=4,rank[4]=1不爲0,表示4不在篩子中,4不是質數,能肯定它的最大質因子的排位值爲1。不處理,直接跳過。

5)i++,i=5,此時rank[5]=0表示5在篩子中,5是質數,cnt=3,表示5是排位值爲3的質數。同時修改rank[5]、rank[10]、rank[15]、rank[20]、rank[25]……等元素的值爲3(當前cnt=3),這個修改既表示將5的倍數的數從篩子中篩掉,同時表示這些5的倍數的數當前能肯定的最大質因子的排位號爲3。 cnt++表示下一個質數的排位值爲4。

……

重複上面的過程,直到i>N。此時1~N範圍內全部整數的最大質因子在質數表中的排位值都肯定了,且保存在數組rank的相應元素中。

(2)源程序。

#include <stdio.h>

#define N 1000000

int rank[N+1]={0};

int main()

{

    int i,j,n,cnt=1;

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

    {

        if(rank[i]!=0) continue;

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

           rank[j]=cnt;

        cnt++;

    }

    while (scanf("%d",&n)!=EOF)

         printf("%d\n",rank[n]);

    return 0;

}

相關文章
相關標籤/搜索