数论L1 GCD、素数筛总结

今天学的东西不是特别难,但还是记录一下学的知识 :grin:

1. GCD

  • GCD定义
    GCD即最大公约数,一般记m,n的GCD为 \gcd(m,n)
    数学定义:若 g|m,g|n 且g最大,则称 g\gcd(m,n)
    集合定义:若 M 为数 m 的约数集合, Nn 的约数集合,$G$ 为 \gcd(m,n) 的约数集合,则有 G=M \cap N
    质因数分解定义:设 m=a_1^{k_1}·a_2^{k_2}·...·a_n^{k_n},n=a_1^{c_1}·a_2^{c_2}·...·a_n^{c_n}(k,c\ge 0),则 \gcd(m,n)=a_1^{\min(k_1,c_1)}·a_2^{min(k_2,c_2)}·...·a_n^{\min(k_n,c_n)}
  • GCD求解
    可用欧几里得算法(辗转相除法)求解
    具体来说,就是如果记 r=m-kn (相当于 r = m \mod n , k 为常数),则有 \gcd(m,n)=\gcd(n,r)
    证明:
    如果记 \gcd(m,n)=\gcd(n,r)=g ,则 g|m,g|kn ,因此 g|(m-kn) 。由于 r=m-kn,所以我们可以得到 g|r 。由于 g|n,g|r,所以 gcd(n,r)=g=gcd(m,n),证毕。
    代码:
int gcd(int x,int y) {
	if(y == 0) return x;
	return gcd(y,x % y);
}

时间复杂度 O(\log n) ,空间复杂度 O(1)

2. 质数筛

  • 埃氏筛
    主要思路就是“质数的倍数是合数”。记 np_i 表示 i 是否为合数,每次当 np_i=0 时,将 i 塞入答案数组,同时将 i 的倍数标记为合数。
    代码:
int n;
bool np[N];
vetor<int> prime;
int main() {
    ……
    for(int i = 2;i <= n;i++) {
		if(!np[i]) {
			prime.push_back(i);
			for(int j = 2;j*i <= n;j++) {
				np[j*i] = 1;
			}
		}
	}
    ……
}

时间复杂度 O(n \log\log n) ,空间复杂度 O(n)

  • 线性筛
    埃氏筛虽然已经足够快了 \log\log n 可以当常数) ,但还有一些优化空间。
    我们可以注意到,在埃氏筛中,有亿些合数被重复标记,很浪费时间。我们可以让每个合数被自己最小的质因数筛掉,这样可以把复杂度降到线性。
int n;
bool np[N];
vector<int> prime;
int main() {
    ……
    for(int i = 2;i <= n;i++) {
        if(!np[i]]) {
            prime.push_back(i);
        }
        for(int j : prime) {
            if(j*i>n) break;
            np[j*i] = 1;
            if(i%j==0) break;
            // 由于 prime 里面质数是从小到大的
            //所以 i 乘上其他的质数的结果一定会被
            // j 的倍数筛掉,就不需要在这里先筛一次
           // 所以这里直接 break掉就好了
        }
    }
    ……
}

时间复杂度 O(n) ,空间复杂度 O(n)
(这玩意还能求很多积性函数值,这个以后再说。)

写总结不易,望大佬点赞 :pray:,谢谢!

参考文献:信友队课件,OI-wiki

修改历史:
2024/7/16 写成
2024/7/17 修改线性筛代码,加上参考文献

8 个赞

谢谢大家点赞

2 个赞

写的不错,帮你改到经验分享区

1 个赞

恳请大家在投稿贴里也点个赞,谢谢 :pray: :pray: @所有点赞的人
投稿贴链接: GCD与素数筛总结 - 【活动】信奥笔记分享 - 信友队论坛 (xinyoudui.com)

1 个赞