大學計算機基礎C語言實驗習題選(1)實驗4-3 循環結構-判素數 四種做法 Miller-Rabin素性測試 孿生素數(6倍數判別法) 樸素做法 樸素改進

實驗4-3 循環結構-判素數

編寫程序sushu.c,輸入一個正整數n(n>2),判斷n是否爲素數。

格式要求 輸入:scanf("%d",&n) 輸出: (1)如果n<=2,則printf(“ERROR”)
(2)如果是素數,則printf("%d是素數", n) 否則printf("%d不是素數", n)

保存,編譯、運行、測試成功後將源程序文件(.c或.cpp)壓縮,提交。

方法一 樸素做法

時間複雜度:O(n)
直接一個一個篩

#include<stdio.h>
int main()
{
    int n;
    scanf("%d",&n);
    if(n<=2)
    {
        printf("ERROR");
    }
    else
    {
        bool is_prime=true;
        for(int i=2;i<n;i++)
        {
            if(n%i==0)
            {
                is_prime=false;
            }
        }
        if(is_prime)
        {
            printf("%d是素數",n);
        }
        else
        {
            printf("%d不是素數",n);
        }
    }
    return 0;
}

方法二:方法一改進

時間複雜度:O(根號n)
如果是偶數,且不是2,肯定不是素數
想象因式分解。3*4=12,不必要從2一直篩到n-1,直接篩到即可
這裏最好不要用sqrt函數,計算機中,平方運算所需cpu週期比根號少得多,故更快些

#include<stdio.h>
int main()
{
    int n;
    scanf("%d",&n);
    if(n<=2)
    {
        printf("ERROR");
    }
    else if(n%2==0&&n!=2)
    {
        printf("%d不是素數",n);
    }
    else
    {
        bool is_prime=true;
        for(int i=2;i*i<=n;i++)
        {
            if(n%i==0)
            {
                is_prime=false;
            }
        }
        if(is_prime)
        {
            printf("%d是素數",n);
        }
        else
        {
            printf("%d不是素數",n);
        }
    }
    return 0;
}

方法三:6倍數判別法

時間複雜度:O(根號n)是鬆的時間複雜度,實際上比上一個快2-4倍
這裏有一個數學定理:大於等於5的質素一定和6的倍數相鄰
詳細解答請轉步知乎
如何證明大於等於5的質素一定和6的倍數相鄰?
其實就是孿生質數,有興趣的讀者可以去網上搜索瞭解

#include<stdio.h>
int main()
{
    int n;
    scanf("%d",&n);
    if(n<=2)
    {
        printf("ERROR");
    }
    else if(n%2==0&&n!=2)
    {
        printf("%d不是素數",n);
    }
    else
    {
        int i,is_prime=1;
        if(n%6!=1&&n%6!=5)
        {
            is_prime=0;
        }
        else
        {
            for(i=5;i*i<=n;i+=6)
            {
                if(n%i==0||n%(i+2)==0)
                {
                    is_prime=0;
                }
            }
        }
        if(is_prime)
        {
            printf("%d是素數",n);
        }
        else
        {
            printf("%d不是素數",n);
        }
    }
    return 0;
}

方法四:Miller-Rabin素性測試

採用了隨機抽樣測試的方法,實際上有可能會判不準,因爲費馬小定理的逆命題實際上是錯的,但是其發生概率實際很低

在測試質數時,抽樣法是一個非常有用的工具。下面給出一種質數判定方法:
對於待判定的整數n。設n-1=d×2s(d是奇數)。對於給定的基底a,若ad≡1 (mod n),或存在0≤r<s使a≡-1 (mod
n),則稱n爲以a爲底的強僞質數。利用二分法,可以在O(logn)的時間內判定n是否爲以a爲底的強僞質數。
對於合數c,以小於c的數爲底,c至多有1/4的機會爲強僞質數。

如果不是隨機抽樣,而是抽樣特殊情況——最小的幾個質數,則: 如果只用2一個數進行測試,最小的強僞質數(反例)是2047,所以一個數顯然不夠;
如果用2和3兩個數進行測試,最小的強僞質數爲1373653,大於106;
如果用2,3,5進行測試,最小的強僞質數爲25326001,大於2×107;
如果用2,3,5,7進行測試,最小的強僞質數爲3215031751,大於3×109,已經比32位帶符號整數的最大值還大了。
可見,通常只要抽取2,3,5,7這幾個固定的數進行測試就能保證測試的正確性了。

適用於大數素性判斷,用到了費馬小定理
感興趣的讀者可以看以下兩篇文章
Miller-Rabin素性測試算法詳解
素數與素性測試(Miller-Rabin測試
這在ACM中有可能會遇到,殺雞焉用牛刀,上面三種交學校的實驗夠用了
這裏用C語言實現

#include<stdio.h>
typedef long long ll;
ll pow_mod(ll a,ll b,ll r)
{
    ll ans=1,buff=a;
    while(b)
    {
        if(b&1)
        {
            ans=(ans*buff)%r;
        }
        buff=(buff*buff)%r;
        b>>=1;
    }
    return ans;
}
ll test(ll n,ll a,ll d)
{
    if(n==2)
    {
        return 1;
    }
    if(n==a)
    {
        return 0;
    }
    if(d%2==0)
    {
        d>>=1;
    }
    int t=pow_mod(a,d,n);
    while(d!=n-1&&t!=n-1&&t!=1)
    {
        t=t*t%n;
        d<<=1;
    }
    return t==n-1||(d&1)==1;
}
ll isprime(ll n)
{
    int a[]={2,3,5,7};
    for(int i=0;i<=3;i++)
    {
        if(n==a[i])
        {
            return 1;
        }
        if(!test(n,a[i],n-1))
        {
            return 0;
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    if(n<=2)
    {
        printf("ERROR");
    }
    else if(n%2==0&&n!=2)
    {
        printf("%d不是素數",n);
    }
    else
    {
        if(isprime(n))
        {
            printf("%d是素數",n);
        }
        else
        {
            printf("%d不是素數",n);
        }
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章