判断较小的数知否为质数:
// 时间复杂度O(根号n)
public class Code01_SmallNumberIsPrime {
public static boolean isPrime(long n) {
if (n <= 1) {
return false;
}
// 2 ... 根号n
for (long i = 2; i * i <= n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
时间复杂度为O(n^1/2)
判断较大的数是否为质数:Miller-Rabin测试
// 本文件可以解决10^9范围内数字的质数检查
// 时间复杂度O(s * (logn)的三次方),很快
// 为什么不能搞定所有long类型的数字检查
// 原因在于long类型位数不够,乘法同余的时候会溢出
public static long[] p = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 };
public static boolean millerRabin(long n) {
if (n <= 2) {
return n == 2;
}
if ((n & 1) == 0) {
return false;
}
for (int i = 0; i < p.length && p[i] < n; i++) {
if (witness(p[i], n)) {
return false;
}
}
return true;
}
// 单次测试的函数,不用纠结原理
// 返回n是不是合数
public static boolean witness(long a, long n) {
long u = n - 1;
int t = 0;
while ((u & 1) == 0) {
t++;
u >>= 1;
}
long x1 = power(a, u, n), x2;
for (int i = 1; i <= t; i++) {
x2 = power(x1, 2, n);
if (x2 == 1 && x1 != 1 && x1 != n - 1) {
return true;
}
x1 = x2;
}
if (x1 != 1) {
return true;
}
return false;
}
// 返回 : n的p次方 % mod
// 快速幂,讲解098会重点讲述,此时直接用即可
public static long power(long n, long p, long mod) {
long ans = 1;
while (p > 0) {
if ((p & 1) == 1) {
ans = (ans * n) % mod;
}
n = (n * n) % mod;
p >>= 1;
}
return ans;
}
}
对于c++,因为此题有乘法取余的过程,所以long类型也可能超,所以定义 ll 为128 位
#include <bits/stdc++.h>
using namespace std;
typedef __int128 ll;
typedef pair<int, int> pii;
template<typename T> inline T read() {
T x = 0, f = 1; char ch = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch - '0');
return x * f;
}
template<typename T> inline void write(T x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template<typename T> inline void print(T x, char ed = '\n') {
write(x), putchar(ed);
}
ll t, n;
ll qpow(ll a, ll b, ll mod) {
ll ret = 1;
while(b) {
if(b & 1) ret = (ret * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ret % mod;
}
vector<ll> p = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
bool miller_rabin(ll n) {
if(n < 3 || n % 2 == 0) return n == 2;
ll u = n - 1, t = 0;
while(u % 2 == 0) u /= 2, ++ t;
for(auto a : p) {
if(n == a) return 1;
if(n % a == 0) return 0;
ll v = qpow(a, u, n);
if(v == 1) continue;
ll s = 1;
for(; s <= t; ++ s) {
if(v == n - 1) break;
v = v * v % n;
}
if(s > t) return 0;
}
return 1;
}
int main() {
t = read<ll>();
while(t --) {
n = read<ll>();
if(miller_rabin(n)) puts("Yes");
else puts("No");
}
return 0;
}
分解质因子:
public class Code03_PrimeFactors {
// 展示分解质因子过程
public static void main(String[] args) {
int n = 4012100;
f(n);
}
// 打印所有n的质因子,时间复杂度O(根号n)
public static void f(int n) {
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
System.out.println(i);
while (n % i == 0) {
n /= i;
}
}
}
if (n > 1) {
System.out.println(n);
}
}
952. 按公因数计算最大组件大小 - 力扣(LeetCode)
class Solution {
public:
static const int maxn = 20001;
int father[maxn];
int size[maxn];
void build(int n) {
for (int i = 0; i < n; i++) {
father[i] = i;
size[i] = 1;
}
}
int find(int i) {
if (father[i] != i)
father[i] = find(father[i]);
return father[i];
}
void insert(int a, int b) {
int x = find(a);
int y = find(b);
if (x != y) {
father[x] = y;
size[y] += size[x];
}
return;
}
int largestComponentSize(vector<int>& nums) {
int n = nums.size();
int rem[100001];
memset(rem, -1, sizeof(rem));
build(n);
for (int i = 0; i < n; i++) {
int cur = nums[i];
for (int j = 2; j * j <= cur; j++) {
if (cur % j == 0) {
if (rem[j] == -1)
rem[j] = i;
else {
insert(i, rem[j]);
}
while (cur % j == 0)
cur /= j;
}
}
if (cur > 1) {
if (rem[cur] == -1)
rem[cur] = i;
else {
insert(i, rem[cur]);
}
}
}
int ans = INT_MIN;
for (int i = 0; i < n; i++)
ans = max(ans, size[i]);
return ans;
}
};
求出分解各数的质因子,有相同的质因子就用并查集合并,最后求出最大的并查集即可
952. 按公因数计算最大组件大小 - 力扣(LeetCode)
埃氏筛:
class Solution {
public:
int countPrimes(int m) {
int n = m - 1;
vector<bool> visited(n + 1, false);
for (int i = 2; i * i <= m; i++) {
if (!visited[i])
for (int j = i * i; j <= n; j += i)
visited[j] = true;
}
int ans = 0;
for (int i = 2; i <= n; i++) {
if (!visited[i])
ans++;
}
return ans;
}
};
欧式筛:
class Solution {
public:
int countPrimes(int m) {
int n = m - 1;
vector<bool> visited(m, false);
vector<int> prime(m / 2 + 1, 0);
int ans = 0;
for (int i = 2; i <= n; i++) {
if (!visited[i])
prime[ans++] = i;
for (int j = 0; j < ans; j++) {
if (i * prime[j] > n)
break;
visited[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
}
}
return ans;
}
};
如果只要质数的个数,而不需要哪些是质数,欧式筛可以优化:预估出质数的大体个数,然后去掉其中的合数
public static int ehrlich2(int n) {
if (n <= 1) {
return 0;
}
// visit[i] = true,代表i是合数
// visit[i] = false,代表i是质数
// 初始时认为0~n所有数都是质数
boolean[] visit = new boolean[n + 1];
// 先把所有的偶数去掉,但是算上2
// 估计的质数数量,如果发现更多合数,那么cnt--
int cnt = (n + 1) / 2;
for (int i = 3; i * i <= n; i += 2) {
if (!visit[i]) {
for (int j = i * i; j <= n; j += 2 * i) {
if (!visit[j]) {
visit[j] = true;
cnt--;
}
}
}
}
return cnt;
}