素数定义
除了1和该数本身,不能被其他整数整除,1不是素数
素数判定
素数的判定没有统一的公式,可以通过枚举暴力方法判定,也可以基于费马定理和米勒-拉宾定理进行随机测试判定。
暴力判定
针对给定的正整数N,枚举1 to sqrt(N), 时间复杂度为O(sqrt(N)).
bool isPrime(long long N){
if(N == 1){
return false;
}
long long bound = sqrt(N);
for(long long i = 2; i < bound; ++i){
if(N % i == 0){
return false;
}
}
return true;
}
随机测试判定
给定一个素数p,满足费马定理或米勒-拉宾定理:
费马定理
a^p mod p = a
米勒-拉宾定理
a^(p-1) mod p = 1
因为这两个定理都是p为素数的必要不充分条件,但若对于整数p,费马定理和米勒-拉宾定理成立,有很大的概率判断p为素数,因此可以随机产生整数a,多次进行测试,如果不满足则p为合数,如果都满足,则很有可能为素数。下面编程实现a^b mod n:
long long pow_mod(long long a, long long b, long long n){
if(!b){
return 1;
}
long long ans = pow_mod(a, b / 2, n);
ans = ans * ans % n;
if(b % 2 == 1){
ans = ans * a % n;
}
return ans;
}
素数筛选
所有的素数能不能由一个统一的公式产生,这一直是无数先哲试图解决的一个问题,迄今为止,仍然没有找到这样的一个万能公式,因此只能通过暴力筛选素数。
const int maxn = 10001;
const int maxp = 1000;
int vis[maxn]; //vis[i] = 1, 则i是和数; vis[i] = 0,则i是1或者素数
int prime[maxp]; //从小到大保存maxn内所有素数
//生成素数表,放在prime中,返回素数个数
int get_primes(){
//标记素数和合数
int m = sqrt(maxn);
for(int i = 2; i <= m; ++i){
if(!vis[i]){
for(int j = i * i; j <= maxn; j += i){
vis[j] = 1;
}
}
}
//存储素数
int count = 0;
for(int p = 2; p <= maxn; ++p){
if(!vis[p]){
prime[count++] = p;
}
}
return count;
}
分解素因子
分解整数
通常使用试除法,首先构造一个素数表,然后扫描素数表,整除则加入解集。
N!中素因子p的个数
Leetcode中有一道有N!结果里,结尾0的个数的题目:
Factorial Trailing Zeroes,本质就是求素因子5的个数
int Num_p(int N, p){
int num = 0;
while(N){
num += N / p;
N /= p;
}
return num;
}
最大公约数(GCD)
求解两个数的最大公约数和最小公倍数是一个常见的问题,其中最小公倍数需要求最大公约数,因此重要的是求两个数的最大公约数。常见的方法是欧几里得算法和扩展欧几里得算法;
欧几里得算法
定理:gcd(a, b) = gcd(b, a mod b)
证明:设a = kb + r; p = gcd(a, b); b = mp; a = np; 则 a = kmp + r = np, r = (n-km)p, 故 p 也是r的约数,得证。代码如下:
int gcd(int a, int b){
if(!b){
return a;
}
return gcd(b, a % b);
}
扩展欧几里得算法
对于不完全为0的非负整数a, b;必然存在整数对x, y, 使得(a,b) = ax + by,其中(a,b)= gcd(a, b);下面编程实现扩展欧几里得:
int exGcd(int a, int b, int & x, int & y){
if(!b){
x = 1;
y = 0;
return a;
}
int ret = exGcd(b, a % b, y, x);
y = y - a / b * x;
return ret;
}
x,y的迭代更新公式可以容易使用递推归纳证明。这里需要指出的是,这里得到的满足(a,b)= ax + by的整数对x,y并不是唯一的,只是abs(x) + abs(y)取最小值。
求解模线性方程 ax = b(mod n)
解模线性方程相当与求 ax = b + kn => ax + n(-k) = b, 这有点类似用扩展欧几里得算法求(a,b)= ax + by.若(a, n)是 b的约数,即可以求解模线性方程有解。现在我们好奇的是如果已知其中一个解x(可以通过扩展欧几里得算法求得),能否求得其他解。
事实上,如果 ax = b(mod n), 则(x + k * n / (a, n))mod n 也必为解。
贴一道有意思的题目
1/n = 1/a + 1/b; (0<a<=b), 给定整数n,求出所有满足要求的的整数对a,b;若没有,则返回空集。
这道题目稍微化简就很明朗了:1/n = 1/(n+k) + k/n(n+k) => 1/n = 1/(n+k) + 1/(n^2/k + n), 所有k只要满足为n^2的因子就够了。