判断素数的方法

昨晚,群里面有个人问了个判断素数的题目

题目如下:

链接:筛选法求素数__牛客网
来源:牛客网

用筛选法求n以内的素数。筛选法求解过程为:将2~n之间的正整数放在数组内存储,将数组中2之后的所有能被2整除的数清0,再将3之后的所有能被3整除的数清0 ,以此类推,直到n为止。数组中不为0 的数即为素数。

一开始没看明白题目意思,以为只需要满足不是2或者3的倍数就可以了,然后发现出错了,后来才发现搞错掉了

然后我就查了下判断素数的方法,有如下几个,但不止这几个:1.AKS算法——公认最优的算法(但实际应用中不常见)        2.Miller—Rabbin算法(基于费马小定理的扩展)        3.试除法(优化6倍法)        4.筛选法

1.AKS算法

直说吧,没看懂,太复杂,我也不会,但从GPT那里获得了c++的简易模板

代码如下:

#include 
#include 
using namespace std;

typedef long long ll;

// 求最大公约数的辗转相除法
ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

// 快速幂算法
ll binpow(ll a, ll b, ll m) {
    ll res = 1;
    a %= m;
    while (b > 0) {
        if (b & 1) {
            res = res * a % m;
        }
        a = a * a % m;
        b >>= 1;
    }
    return res;
}

// 判断一个数是否为素数
bool is_prime(ll n) {
    if (n <= 1) {
        return false;
    }
    if (n <= 3) {
        return true;
    }
    if (n % 2 == 0 || n % 3 == 0) {
        return false;
    }
    ll r = sqrt(n);
    for (ll i = 5; i <= r; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }
    return true;
}

// 判断n是否是a的幂
bool is_power(ll n) {
    if (n <= 1) {
        return true;
    }
    ll r = sqrt(n);
    for (ll a = 2; a <= r; ++a) {
        ll p = a;
        while (p <= n) {
            p *= a;
            if (p == n) {
                return true;
            }
        }
    }
    return false;
}

// AKS算法判断n是否为素数
bool AKS(ll n) {
    // 如果n是a的幂,则n不是素数
    if (is_power(n)) {
        return false;
    }
    // 寻找r,满足n^r-1 = (n-1)q,其中q为素数
    ll r = 2;
    while (r <= sqrt(n)) {
        if (n % r == 0) {
            return false;
        }
        ll phi_r = r - 1;
        if (gcd(n, r) == 1) {
            bool is_valid = true;
            for (ll a = 2; a <= sqrt(phi_r) && is_valid; ++a) {
                if (gcd(a, r) == 1) {
                    is_valid = binpow(a, phi_r, n) != 1;
                }
            }
            if (is_valid) {
                ll q = (n - 1) / r;
                if (gcd(q, r) == 1 && binpow(n, q, r) == 1) {
                    return true;
                }
            }
        }
        ++r;
    }
    return false;
}

int main() {
    ll n;
    cin >> n;
    if (AKS(n)) {
        cout << n << " is prime" << endl;
    } else {
        cout << n << " is not prime" << endl;
    }
    return 0;
}

代码不一定正确,经供参考,它太复杂了,所以我直接放弃了,而且实际应用中也不常见

2.Miller—Rabbin算法

话不多说,直接上代码

typedef long long ll;

// 判断一个数是否为素数,返回true表示是素数,返回false表示不是素数
bool is_prime(ll n) {
    // 1. 如果要判断的数n等于2或3,则判定为素数
    if (n == 2 || n == 3) return true;
    // 2. 如果n可以表示为a^b的形式,则判定n为合数
    for (ll a = 2; a * a <= n; ++a) {
        ll b = n;
        while (b > 1) {
            ll m = a, x = 1;
            while (b > 0) {
                if (b & 1) x = x * m % n;
                m = m * m % n;
                b >>= 1;
            }
            if (x == n) return false;
            if (x > 1 && x * x % n == 1) break;
        }
    }
    // 3. 如果n不是奇数,则判定n为合数
    if (n % 2 == 0) return false;
    // 4. 计算r使得r是最小的满足下列不等式之一的整数
    ll r = 1, d = (n - 1) / 2;
    while (d % 2 == 0) {
        r++;
        d /= 2;
    }
    // 如果不存在满足上述条件的r,则n为素数
    if (r == 1) return true;
    // 5. 对所有满足1 < a ≤ r的整数a,判断是否满足条件之一
    for (ll a = 2; a <= r; ++a) {
        // (i) a^n ≡ a mod n
        ll b = n - 1, x = a % n;
        while (b > 0) {
            if (b & 1) x = x * a % n;
            a = a * a % n;
            b >>= 1;
        }
        if (x != a % n) return false;
        // (ii) 对于所有0 < k < n,有(a^k) mod (x^r - 1, n) = 1
        if (r < n - 1) {
            ll y = a;
            for (ll i = 0; i < r; ++i) {
                if (y == 1) break;
                y = y * y % n;
            }
            if (y != 1) return false;
        }
    }
    // 6. 如果满足上述条件,则判定n为素数,否则n为合数
    return true;
}

原理:

  1. 如果要判断的数n等于2或3,则判定为素数。

  2. 如果n可以表示为a^b的形式,则判定n为合数。

  3. 如果n不是奇数,则判定n为合数。

  4. 计算r使得r是最小的满足下列不等式之一的整数。

  5. 对所有满足1 < a ≤ r的整数a,判断是否满足条件之一:

    (i) a^n ≡ a mod n (ii) 对于所有0 < k < n,有(a^k) mod (x^r - 1, n) = 1

  6. 如果满足上述条件,则判定n为素数,否则n为合数。

代码通过循环枚举所有满足条件的a和r,判断n是否为素数。其中,在计算r的过程中,使用了一种称为"二次探测"的方法,用来减少计算量和时间复杂度。

时间复杂度为:O(k * log^3 n),k取任意值,通常为20左右可满足需求

3.试除法(优化6倍法)

还是直接上代码

​
bool is_prime(int n) {
    if (n <= 1) return false;  // 1 不是素数
    if (n <= 3) return true;   // 2 和 3 是素数
    if (n % 2 == 0 || n % 3 == 0) return false;  // 排除掉 2 和 3 的倍数

    int max_divisor = sqrt(n); //求待判断数开方数
    for (int i = 5; i <= max_divisor; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }
    return true;
}

​

试除法(也称质因数分解法)是一种用来判断一个数是否为素数的算法。它的基本原理是将要判断的数 n 依次除以 2 到 sqrt{n} 之间的每个整数,如果都不能整除,则 n 是素数。如果存在能整除 n 的整数,则 n 不是素数。

在优化的试除法中,我们可以先判断 n 是否为 2 或 3,然后只对 6k±1 的形式进行试除,这样能够减少试除的次数,提高算法效率。具体实现中,我们只需要判断 6k±1 ≤ √n  是否成立即可。

需要注意的是,如果 n 为合数,它必定存在小于 sqrt{n} 的质因子,因此只需要试除 2 到 sqrt{n} 之间的数即可,不需要试除所有的数

4.筛选法

 这也是本题要求的算法,代码如下:

#include 
#include 

using namespace std;

vector get_primes(int n) {
    vector primes;
    vector is_prime(n + 1, true); // 初始化所有数为素数

    is_prime[0] = is_prime[1] = false; // 排除0和1不是素数的情况

    // 从2开始筛选,将所有的倍数标记为非素数
    for (int i = 2; i * i <= n; i++) {
        if (is_prime[i]) {
            for (int j = i * i; j <= n; j += i) {
                is_prime[j] = false;
            }
        }
    }

    // 收集所有的素数
    for (int i = 2; i <= n; i++) {
        if (is_prime[i]) {
            primes.push_back(i);
        }
    }

    return primes;
}

int main() {
    int n;
    cin >> n;

    vector primes = get_primes(n);

    for (int prime : primes) {
        cout << prime << " ";
    }
    cout << endl;

    return 0;
}

它的基本思想是先假设所有数都是素数,然后从2开始,将其倍数标记为合数,最终剩下的未被标记的数即为素数。

ok,就到这儿吧,可能存在些许错误,若有发现,请指出,谢谢

你可能感兴趣的:(算法,数据结构,c++)