判断一个数是否为质数(五种)

在某一天起来,脑海中浮现一连串数字分别是2、3、5、7、11、13、17,反应过来好像是一串质数

于是马上写了个基础的质数检测函数

1、试除法

bool is_prime(int num)
{
    if(num <= 1) return false;
    for(int i=2;i<num;i++)
    {
        if(num%i == 0)
            return false;
    }return true;
}

最简单、最基础的质数检测函数,具体实现方式就是让num将2-num-1之间的所有数取余,直到找不到一个能够被模的数,那么num就是质数

但是这种方法一旦需要被检测的数非常大就会发生很艰难的运算,例如以下实例:

让这个方法举例所有200以内的质数,然后再求int类型中最大的质数2147483647,来直观的判断程序运行时间

可以看到,举例200以内的质数用了8ms,而判断int类型中最大的质数居然用了5095ms!这是何等漫长!

于是需要改进算法,优化试除法

2、优化试除法

bool is_prime(int num)
{
    if(num<=1) return false;
    if(num<=3) return true;
    if (num % 2 == 0 || num % 3 == 0) return false;
    for(int i = 5;i*i<=num+1;i+=6)
    {
        if(num%i ==0 || num%(i+2) == 0)
            return false;
    }
    return true;
}

优化试除法是在试除法的基础上进行了优化后的方法,1不是质数,可以直接排除,2、3是质数也可以排除

if (num % 2 == 0 || num % 3 == 0) return false;这里排除了所有2、3的倍数的整数、

为什么 要从5开始呢,因为 2 和 3 的倍数已经筛了,那就从5开始了, i+=6 刚好跳过2 和3 的倍数

要求不变,可以看到举例200以内的质数已经达到了6ms,而判断一个大数是否为质数的速度居然缩短了一大截,只有886ms,比优化前快了5倍!

经过这么一折腾,试除法的效率已经提到了最高,继续优化没有意义了,该换方法了

3、埃拉托斯特尼筛法

埃拉托斯特尼筛法简称埃氏筛,是一种基于排除法的筛选质数方法,原理是将已知质数的倍数筛出,剩下的就是质数了

bool is_prime(int num) {
    vector<bool> prime(num + 1, true);
    prime[0] = prime[1] = false;
    for (int p = 2; p * p <= num; p++) {
        if (prime[p]) {
            for (int i = p * p; i <= num; i += p)
                prime[i] = false;
        }
    }
    return prime[num];
}

代码中定义了一个prime数组,初始默认全是质数,为true,然后通过2、3质数的倍数进行筛出合数,再在数组中更改true为false,剩下筛出来的就是质数了,直接返回 prime[num]即可

可以看到,打印200以内的质数用了8ms(跟试除法有得一比),而且大数判断直接寄了,不过本来埃氏筛就是为了求出一定范围内的质数,而太大的质数意味着prime数组需要非常大的空间,直接溢出了很正常

4、欧氏筛

欧氏筛和埃氏筛目的一样,都是为了筛出一定范围内的质数,而不是用来求一个数是否为质数的方法,代码是

bool is_prime(int num)
{
    if(num<=1) return false;

    vector<bool> status(num+1,true);
    int m = int(sqrt(num));

    status[0] = status[1] = false;
    for(int i = 2;i<=num;i++)
    {
        if(status[i])
        {
            if(i == num) return true;
            for(int j = i*i ; j<=num;j+=i)
                status[j] = false;

        }
    }
    return false;

}

原理同埃氏筛,不过略有不同,是通过筛选质因子来筛除合数,

显而易见的,因为要定义一个数组来存储数字标签,故而在判断大数时一定会寄掉

5、Miller-Rabin质数测试

这是一个很高级的质数检测方法,Miller-Rabin素性测试是一种概率性的素性测试方法,用于判断一个给定的整数是否可能是素数。它基于费马小定理的一个推广,并结合了一些额外的数学原理来提高测试的准确性。

Miller-Rabin测试步骤

  1. 选择随机基:选择一个随机数a∈{2,3,…,n−2}a∈{2,3,…,n−2}。

  2. 计算a^d(%n)a^d(%n):计算a^d(%n)a^d(%n)。如果ad≡1(modn)ad≡1(modn)或ad≡−1(modn)ad≡−1(modn),则nn可能是素数。

  3. 迭代测试:对于r=0,1,…,s−1r=0,1,…,s−1,计算a2rd(modn)a2rd(modn)。如果a2rd≡−1(modn)a2rd≡−1(modn),则停止并认为nn可能是素数。否则,如果a2rd≡1(modn)a2rd≡1(modn)且r<s−1r<s−1,则nn肯定不是素数。

// 快速幂取模函数:计算 (base^exp) % mod
int powMod(int base, int exp, int mod) {
    int result = 1; // 初始结果为 1
    while (exp > 0) { // 当指数 exp 大于 0 时循环
        if (exp % 2 == 1) // 如果 exp 是奇数
            result = (result * base) % mod; // 结果乘以当前 base,并取模
        exp = exp >> 1; // 将 exp 右移一位,相当于 exp // 2
        base = (base * base) % mod; // base 变为 base^2,并取模
    }
    return result; // 返回计算结果
}

// 模乘函数:计算 (a * b) % mod
int mulMod(int a, int b, int mod) {
    return ((long long)a * b) % mod; // 使用 long long 避免溢出
}

// 素性检测函数:使用 Miller-Rabin 算法判断 num 是否为素数
bool is_prime(int num, int k = 5) {
    if (num <= 1 || num == 4) return false; // 1 和 4 不是素数
    if (num <= 3) return true; // 2 和 3 是素数

    int d = num - 1; // 计算 d = num - 1
    // 将 d 变成 2 的幂次方乘以奇数
    while (d % 2 == 0) d /= 2;

    // 初始化随机数生成器
    random_device rd; // 获取随机数种子
    mt19937 gen(rd()); // 使用随机数种子初始化 Mersenne Twister 随机数生成器
    uniform_int_distribution<> dis(2, num - 2); // 生成 2 到 num-2 之间的均匀分布的整数

    for (int i = 0; i < k; i++) { // 进行 k 次测试
        int a = dis(gen); // 随机选取一个 a
        int x = powMod(a, d, num); // 计算 a^d % num
        if (x == 1 || x == num - 1) continue; // 如果 x 是 1 或 num - 1,则继续下一个测试
        while (d != num - 1) { // 将 d 还原到最初的值
            x = mulMod(x, x, num); // 计算 x^2 % num
            d *= 2; // d 加倍
            if (x == 1) return false; // 如果 x 变成 1,则 num 不是素数
            if (x == num - 1) break; // 如果 x 变成 num - 1,则继续下一个测试
        }
        if (x != num - 1) return false; // 如果 x 最终不等于 num - 1,则 num 不是素数
    }
    return true; // 经过 k 次测试均未发现非素数,num 被认为是素数
}

可以看到这个方法对于获取一定范围内的数速度很慢,但是在处理大数质数方面非常快,直接秒了,是个很高级的方法,可惜结果不准确,因为是采用了随机种子使得结果不能完全相信

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值