数论小学习(一)
欧几里得算法
简述
欧几里得算法(也称辗转相除法)是用来求解两个非负整数 \(a\) 和 \(b\) 的最大公约数的经典算法。其核心思想是 \(gcd(a, b) = gcd(b, a \,mod\, b)\),当 \(b=0\) 时,\(gcd(a, 0) = a\)。
证明
1.我们知道 \(a \,mod \, b\) 可以表示为 \(r = a - k·b\),其中 \(k = [a/b]\) 是整数,\(r\) 是余数。
2.如果 \(d | a\) 且 \(d | b\),那么 \(d\) 也能整除 \(a\)和\(b\)的线性组合\(r = a - k·b\)。
3.所以,\(d | r\),即 \(d | (a \,mod\, b)\)。
4. 从 \(3\) 可知,整数 \(a\) 和 \(b\) 的公约数与整数 \(b\) 和 \(a \,mod \,b\) 的最大公约数相同。由于每次迭代 \(b\) 的值都会变成 \(a \,mod\, b\),这个余数严格小于 \(b\) 且非负。所以 \(b\) 的值在每一步都严格减小,最终必然会达到0,此时 $ gcd(a_{end}, 0) = a_{end}$,算法终止。
\(\boxed{gcd(a, b) = gcd(b, a \,mod\, b)}\)
代码
int My_gcd(int a,int b){
return b?My_gcd(b,a%b):a;
}
扩展欧几里得算法
简述
基于贝祖等式 $ \boxed{ax+by=gcd(a,b)}$ ,我们想要找到每一个可能的x和y,可以使用扩展欧几里得算法(\(exgcd\))
步骤
1.先利用欧几里得算法gcd(a,b)=gcd(b,a%b)不断递归到边界值,然后
有\(gcd(a_{end},0)=a_{end}=1·a_{end}+0·0\),此时x=1,y=0;
2.由于恒等式\(d=a·x+b·y=b·x+(a-[a/b]·b)·y=y·a+(x-[a/b]·y)·b\)(欧几里得算法)
3.从最后一个边界点不断会带,令\(y=x-[a/b]·y,x=y\)求出基本的\(x\)和\(y\)
4.这样求出其中一组\(x_{base}\)和\(y_{base}\),还有通解\(\boxed{x=x_{base}+d/b,y=y_{base}+d/a}\)
代码
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b) return x=1,y=0,a;
ll d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
算术基本定理(唯一分解定理)
简述
对于任意大于等于2的数可以分解为若干质因数幂次积的形式
\(n=p_1^{k_1}·p_2^{k_2}·p_3^{k_3}···p_n^{k_n}\)
代码(试除法)
void solve(){
ll n;cin>>n;
vector<pair<int,int>>res;
for(int i=2;i*i<=n;i++){
int cnt=0;
while(n%i==0){
cnt++;
n/=i;
}
if(cnt) res.push_back({i,cnt});
}
if(n>1) res.push_back({n,1});
}
逆元
如果一个线性同余方程 \(ax \equiv 1 \pmod b\),则 x 称为 \(a \bmod b\) 的逆元,记作 \(a^{-1}\)。ps:在模意义下除以一个数等于乘以一个数的逆元
费马小定理
简述
如果 p 是一个质数 ,且 a 是一个不被 p 整除的整数,那么有
\(\boxed{a^{p-1} \equiv 1 \pmod{p}}\)
\(\boxed{a^{p} \equiv a \pmod{p}}\)
证明
1.构造集合\(S=\{1,2,...,n-1\}(\mod n)\)
2.构造集合\(T=\{a·1,a·2,...,a·(n-1)\}(\mod n)\)
3.因为a与n互质,所以所有元素仍然与n互质,显然有,T中元素互不相同且T中元素也包含小于n的所有元素,所有T是S中的一个排列(只是顺序不同).
4.所以T中所有元素之积等于S中所有元素之积\(1⋅2⋅⋯⋅(n−1) \equiv a^{n−1}⋅(1⋅2⋅⋯⋅(n−1))(\mod n)\)
5.约去左右公因数,得到\(\boxed{a^{p-1} \equiv 1 \pmod{p}}\)
欧拉定理
欧拉函数
欧拉函数(Euler's totient function),即 \(\varphi(n)\),表示的是小于等于 n 和 n 互质的数的个数。由唯一分解定理,设
\(n = \prod_{i=1}^{s}p_i^{k_i}\),其中 \(p_i\) 是质数,有 \(\varphi(n) = n \times \prod_{i = 1}^s{\dfrac{p_i - 1}{p_i}}\)。
简述
\(\boxed{a^{\phi(n)} \equiv 1 \pmod{n} \quad (\text{当 } \gcd(a, n) = 1)}\)
证明
考虑集合:\(T=\{a⋅x\,mod\, n∣x∈S\}\)
由于 a 与 n 互质,且 x 与 n 也互质,所以 a·x mod n 仍然与 n 互质。
因此,集合 T 中的所有元素也是与 n 互质的,并且数量也为 \(φ(n)\)
同费马小定理将T和S中乘积建立等式,约去公因数,得证
\(\boxed{a^{\phi(n)} \equiv 1 \pmod{n} \quad (\text{当 } \gcd(a, n) = 1)}\)
注意
当 \(n\) 是质数时,\(φ(n)=n−1\),此时欧拉定理就退化为费马小定理。
埃氏筛
埃氏筛法,时间复杂度是 \(O(n\log\log n)\)。
思路
考虑这样一件事情:对于任意一个大于 1 的正整数 n,那么它的 x 倍就是合数(x > 1)。利用这个结论,我们可以避免很多次不必要的检测。
如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。
代码
vector<int> prime;
bool is_prime[N];
void Eratosthenes(int n) {
is_prime[0] = is_prime[1] = 0;
for (int i = 2; i <= n; ++i) is_prime[i] = 1;
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
prime.push_back(i);
if ((ll)i * i > n) continue;
for (int j = i * i; j <= n; j += i)
is_prime[j] = 0;
}
}
}
欧拉筛
埃氏筛法仍有优化空间,它会将一个合数重复多次标记。有没有什么办法省掉无意义的步骤呢?答案是肯定的。
如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 \(O(n)\) 了。
代码
vector<int>prime;
vector<char>isprime;
void init(const int &n){
isprime.resize(n+1);
isprime[1]=1;
for(int i=2;i<=n;i++){
if(!isprime[i]) prime.push_back(i);
for(auto &p:prime){
if((ll)p*i>n) break;
isprime[i*p]=1;
if(i%p==0) break;
}
}
}
区间筛
简述
给定一个区间[l,r](\(1 \leq l \leq r \leq 2e8\) 且 \(r-l \leq 1e6\) )要筛出里面素数
思路
先用欧拉筛预处理1e6范围内的素数
代码
void init(const int &n){
isprime.resize(n+1);
isprime[1]=1;
for(int i=2;i<=n;i++){
if(!isprime[i]) prime.emplace_back(i);
for(auto &p:prime){
if((ll)p*i>n) break;
isprime[i*p]=1;
if(i%p==0) break;
}
}
}
void solve(){
init(1e6);
int l,r;cin>>l>>r;
vector<unsigned char>st(r-l+2);
if(l==1) st[l]=1;
for(int i=0;i<prime.size();i++){
for(int j=l/prime[i];(ll)j*prime[i]<=1LL*r;j++){
if(j==1) continue;
if((ll)j*prime[i]>=l) st[(ll)j*prime[i]-l]=1;
}
}
}

浙公网安备 33010602011771号