素数判定以及筛法和费马小定理素性测试

- 单个素数最基本的判定方法

bool check(int i)
{
    int k=sqrt(i);
    for(int j=2;j<=k;j++)
        if(i%j==0)return false;
    return true;
}

- 单个素数的判定(6的倍数相邻法)

#include <stdio.h>
#include <math.h>///floor函数的头文件
int isPrime(int n)
{	//返回1表示判断为质数,0为非质数,在此没有进行输入异常检测
	float n_sqrt;
	if(n==2 || n==3) return 1;                      ///与floor函数对应的是ceil 函数,即上取整函数。
	if(n%6!=1 && n%6!=5||n==1) return 0;//不在6的倍数两侧的一定不是质数
	n_sqrt=floor(sqrt((float)n));                   ///floor(x),也写做Floor(x),其功能是“向下取整”,
	                                                ///或者说“向下舍入”,即取不大于x的最大整数(与“四舍
	for(int i=5;i<=n_sqrt;i+=6)//在6的倍数两侧的也可能不是质数
	{                                               ///五入”不同,下取整是直接取按照数轴上最接近要求
	    if(n%(i)==0 | n%(i+2)==0) return 0;         ///值的左边值,即不大于要求值的最大的那个值)。
	}
        return 1;//排除所有,剩余的是质数
}
int main()
{
	int flag,x;
	while(~scanf("%d",&x)){
        flag=isPrime(x);
        printf("%d\n",flag);
	}
	return 0;
}
///floor(3.14) = 3.0
///floor(9.999999) = 9.0
///floor(-3.14) = -4.0
///floor(-9.999999) = -10

- 素数普通筛

///普通筛——埃拉托斯特尼(Eratosthenes)筛法:
#include<stdio.h>
#include<math.h>
#include<stdbool.h>
#include<string.h>
///#include<bits/stdc++.h>  ///或者直接万能头文件
#define N 100
int main(void)
{
    bool number[N+1];
    int i,j;
    memset(number,true,sizeof(number));  ///memset需要#include<string.h>头文件
    for(i=2;i<=sqrt(N);i++)
    {
        if(number[i]==true)///如果i是素数
        {
            for(j=2;j*i<=N;j++)
            {
                number[i*j]=false;///如果i是素数,则i*j不是素数
            }
        }
    }///所有非素数都标记为false,素数都标记为true
    for(i=2;i<N+1;i++)
        if(number[i]==true)
            printf("%d ",i);//输出所有素数
    return 0;
}
///此筛选法的时间复杂度是O(nloglogn)

///算法改进:
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<math.h>
#define N 100
int main(void)
{
    bool number[N+1];
    int i,j;
    memset(number,true,sizeof(number));
    for(i=2;i<=sqrt(N);i++)
    {
        if(number[i]==true)///如果i是素数
        {
            for(j=i*i;j<=N;j+=i)
            {
                number[j]=false;///二次筛选法:i是素数,则下一个起点是i*i,把后面的所有的i*i+2*n*i筛掉
            }
        }
    }///所有非素数都标记为false,素数都标记为true
    for(i=2;i<N+1;i++)
        if(number[i]==true)
            printf("%d ",i);///输出所有素数
    return 0;
}

- 素数普通筛(模板)

#include<stdio.h>
const int maxn = 1e7 + 5;
int pri[maxn];
void getPrime(int n) {
	for (int i = 0; i <= n; ++i) pri[i] = i;
	pri[1] = 0;
	for (int i = 2; i <= n; ++i) {
		if (!pri[i]) continue;
		pri[++pri[0]] = i;
		for (int j = 2; i * j <= n && j < n; ++j) {
			pri[i * j] = 0;
		}
	}
	for(int k=1;k<=n;++k){
        if(pri[k]!=0){
            printf("%d ",pri[k]);
        }
	}
}
int main()
{
    int a;
    while(~scanf("%d",&a))
    {
        getPrime(a);
    }
    return 0;
}

- 素数线性筛

///线性筛——欧拉Euler筛(时间复杂度为O(n))
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#define N 1000
bool number[N+1];
int prime[N+1];
int main(void)
{

    int i,j,count=0;
    memset(number,true,sizeof(number));///memset只能将整形数组初始化值为0或-1,其余值可用fill函数进行填充
    for(i=2;i<=N;i++)
    {
        if(number[i])
            prime[count++]=i;
        for(j=0;j<count&&prime[j]*i<=N;j++)
        {
            number[prime[j]*i]=false;
            if(i%prime[j]==0) ///精华就在于此:它保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)
                break;
        }
    }
    for(i=2;i<N+1;i++)
        if(number[i]==true)
            printf("%d ",i);
    return 0;
}

可能是求质数最高效的算法
C++代码:

/*普通筛*/
#include <iostream>
using namespace std;
int prime(int n);
int main() {
    prime(100);
    return 0;
}
int prime(int n){
    int i;
    bool *prime = new bool[n+1];
    for(i=2;i<n;i++){
        prime[i] = true;
    }
    for(i=2;i<=n;i++){
        if(prime[i]){
            cout<<i<<" ";
            for(int j=i+i;j<n;j+=i){
                prime[j] = false;
            }
        }
    }
    cout<<endl;
    return 0;
}

- 素数筛法预处理模板

(预处理筛出素数、对一个数直接查询是否为素数):

/*筛选2~N范围内的素数,存进prime数组*/
#include<iostream>
using namespace std;
const int N=1e4;
int prime[N],b[N],k=1;///注意k初始化为1
void get_prime()
{
    bool a[N+5];///放大一点防止数组越界
    for(int i=2;i<=N;i++){
        a[i]=true;
    }
    for(int i=2;i<=N;i++){
        if(a[i]){
            prime[k]=i,b[i]=k,k++;///prime数组存素数,b数组记录该素数为第几个素数
            for(int j=i+i;j<=N;j+=i){
                a[j]=false;
            }
        }
    }
}
int main()
{
    get_prime();//预处理,筛出素数
    for(int i=1;i<k;i++){
        cout<<prime[i]<<' '<<b[prime[i]]<<endl;///输出素数prime[i]和该素数为第几个素数b[prime[i]],运行结果如下图所示
    }
    
    ///当判断一个数是否为素数时就可以根据b数组直接查询判断,而不需要对prime数组进行遍历查询,以下为例子
    int x;
    cin>>x;
    if(b[x])
        cout<<"it is prime"<<endl;
    else
        cout<<"it isn't prime"<<endl;
        
    return 0;
}

在这里插入图片描述

预处理出素数,直接查询一个数x是否为素数:

const int N=1e4+5;
bool prime[N];
void get_prime()
{
    for(int i=2;i<=N;i++)
        prime[i]=1;
    for(int i=2;i<=N;i++){
        for(int j=i+i;j<=N;j+=i)
            prime[j]=0;
    }
}
int main()
{
	get_prime();
	cin>>x;
	if(prime[x])cout<<"It is a prime"<<endl;
	else cout<<"It isn't a prime"<<endl;
}

- 利用费马小定理进行素性测试

在这里插入图片描述
若n为素数,则其满足费马小定理:a^(n-1) ≡ 1(mod n)
即如果一个数n是素数,那么 a^(n-1)%n恒等于1 (a为该范围内任意数:1<=a<n)

注意:费马小定理只是素数的必要条件,而不是充分条件,即满足费马小定理的数不一定是素数。所以可通过使用随机数生成a和增加验证费马小定理的次数来提高素性判定的准确率。

题解:费马小定理 + 大数 + 快速幂

import java.math.BigInteger;
import java.util.Random;
import java.util.Scanner;

/**
 * @author Hollay
 * @create 2020-09-09-17:29
 * @description
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            BigInteger n = sc.nextBigInteger();
            BigInteger a = new BigInteger("0");
            BigInteger ans = new BigInteger("1");
            Random rand = new Random();
            boolean flag = false;
            for (int i = 0; i < 30; i++) {
                do {
                    a = new BigInteger(128, rand);
                    a = a.mod(n);
                } while (a.equals(BigInteger.ZERO));
                ans = BigNumberFermat(a, n.subtract(BigInteger.ONE), n);
                if (!ans.equals(BigInteger.ONE)) {
                    System.out.println("no");
                    flag = true;
                    break;
                }
            }
            if (flag == false) {
                System.out.println("yes");
            }
        }
        sc.close();
    }
    static BigInteger BigNumberFermat(BigInteger a, BigInteger n, BigInteger mod) {
        BigInteger ret = new BigInteger("1");
        BigInteger two = new BigInteger("2");
        while (!n.equals(BigInteger.ZERO)) {
            if (n.mod(two) != BigInteger.ZERO) {
                ret = ret.multiply(a);
                ret = ret.mod(mod);
            }
            a = a.multiply(a);
            a = a.mod(mod);
            n = n.divide(two);
        }
        return ret;
    }
}


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值