
本文共 15285 字,大约阅读时间需要 50 分钟。
文章目录
1.质数
1.1 质数的定义
- 规定1不是质数也不是合数,n为质数的前提条件为(n >= 2&& n ∈ N + n∈N+ n∈N+)
- 若n为质数,那么它只有1和它本身两个因子
- 特殊的,质数中只有一个偶数2,其他都是奇数
其他相关知识
- 对于一个整数N,[1,N]的质数大约有 N l n N {N\over lnN} lnNN个,即每 l n N lnN lnN个数中有一个质数
1.2 质数的判定
试除法判定质数
- 1.暴力做法:
- 根据质数定义出发,暴力枚举区间[2,n-1]是否有其他因子,时间复杂度为 O ( n ) O(n) O(n),这里不再赘述
- 2.优化做法:
- 若 一 个 合 数 为 n , 一 个 因 子 d ∣ n , 那 么 一 定 有 n d ∣ n 若一个合数为n,一个因子d|n,那么一定有{n\over d}|n 若一个合数为n,一个因子d∣n,那么一定有dn∣n
- 因此,我们用只需要枚举最小的那个因子d就可以, d < = n d , d 2 < = n , d < = n d <= {n\over d},d^2<=n,d<=\sqrt{n} d<=dn,d2<=n,d<=n
- 枚举区间 [ 2 , n ] [2,\sqrt{n}] [2,n],时间复杂度为 O ( n ) O(\sqrt{n}) O(n)
代码如下:
bool is_prime(int n){ if(n < 2) return false; for(int d = 2; d <= n/d; d ++ ){ //如果d能整除n,那么n为合数 if(n % d == 0) return false; } return true;}
其他相关知识
- 有一些效率更高的随机性算法,例如“Miller-Robbin”等,有较小的概率把合数错误判定为质数,但多次判定合起来的错误趋近于零
2. 筛质数
问题:
给出一个整数N,求1~N之间所有质数
2.1 Eratosthenes 筛法
定义
- 任 意 整 数 x 的 倍 数 2 x , 3 x , … … 都 不 是 质 数 任意整数x的倍数2x,3x,……都不是质数 任意整数x的倍数2x,3x,……都不是质数
- 枚 举 [ 2 , N ] , 从 小 到 大 扫 描 每 一 个 数 , 设 这 个 数 为 x , 则 它 的 倍 数 2 x , 3 x , . . . . . . [ N / x ] ∗ x 都 不 是 质 数 , 标 记 为 合 数 ( 这 些 合 数 就 没 有 用 了 , 遇 到 就 跳 过 ) , 当 扫 描 到 一 个 数 x 的 时 候 , 它 前 面 [ 2 , x − 1 ] 都 已 经 扫 描 过 了 , 如 果 当 前 这 个 数 x 没 有 被 标 记 , 这 个 数 一 定 是 质 数 枚举[2,N],从小到大扫描每一个数,设这个数为x,则它的倍数2x,3x,......[N/x]*x都不是质数,标记为合数(这些合数就没有用了,遇到就跳过),当扫描到一个数x的时候,它前面[2,x-1]都已经扫描过了,如果当前这个数x没有被标记,这个数一定是质数 枚举[2,N],从小到大扫描每一个数,设这个数为x,则它的倍数2x,3x,......[N/x]∗x都不是质数,标记为合数(这些合数就没有用了,遇到就跳过),当扫描到一个数x的时候,它前面[2,x−1]都已经扫描过了,如果当前这个数x没有被标记,这个数一定是质数
- 小 于 x 2 的 x 的 倍 数 在 扫 描 更 小 的 数 已 经 被 标 记 过 ( 例 如 , 前 面 ( x − 1 ) 能 筛 掉 ( x − 1 ) ∗ x ) 小于x^2的x的倍数在扫描更小的数已经被标记过(例如,前面(x-1)能筛掉(x-1)*x) 小于x2的x的倍数在扫描更小的数已经被标记过(例如,前面(x−1)能筛掉(x−1)∗x)
- 因 此 减 小 枚 举 区 间 , 从 x 2 开 始 , 筛 去 x 2 , ( x + 1 ) ∗ x , . . . . . . , ( N / x ) ∗ x 因此减小枚举区间,从x^2开始,筛去x^2,(x+1)*x,......,(N/x)*x 因此减小枚举区间,从x2开始,筛去x2,(x+1)∗x,......,(N/x)∗x
时间复杂度计算
- 首先,引入一个概念: 调 和 级 数 f ( n ) = 1 + 1 2 + 1 3 + 1 4 + … … + 1 n , 当 n 趋 于 ∞ 时 , f ( n ) = l n ( n ) + γ ( γ 为 欧 拉 常 数 ) 调和级数f(n)=1+{1\over 2}+{1\over 3}+{1\over 4}+……+{1\over n},当n趋于∞时,f(n) = ln(n)+γ(γ为欧拉常数) 调和级数f(n)=1+21+31+41+……+n1,当n趋于∞时,f(n)=ln(n)+γ(γ为欧拉常数)
- 筛 素 数 : n / 2 + n / 3 + … … + n / n = n ( f ( n ) − 1 ) = n ( f ( n ) ) − n 筛素数:n/2+n/3+……+n/n = n(f(n) - 1) = n(f(n)) - n 筛素数:n/2+n/3+……+n/n=n(f(n)−1)=n(f(n))−n
- 由 于 , 不 超 过 N 的 质 数 大 约 N / l n N 个 , 所 以 时 间 复 杂 度 为 O ( N l o g N l o g N ) ≈ O ( N ) 由于,不超过N的质数大约N/lnN个,所以时间复杂度为O(NlogNlogN)≈O(N) 由于,不超过N的质数大约N/lnN个,所以时间复杂度为O(NlogNlogN)≈O(N)
代码如下:
int primes[N],cnt;//存储质数bool v[N];//标记合数void get_primes(int n){ for(int i = 2; i <= n; i ++ ){ if(v[i]) continue; primes[++cnt] = i; for(int j = i; j <= n/i; j ++) v[j * i] = true; }}
2.2 线性筛法
思想
- 使 每 一 个 合 数 只 会 被 它 最 小 质 因 子 筛 取 , 时 间 复 杂 度 为 O ( n ) 使每一个合数只会被它最小质因子筛取,时间复杂度为O(n) 使每一个合数只会被它最小质因子筛取,时间复杂度为O(n)
问题引入:
- 通 过 上 面 埃 氏 筛 法 可 以 看 到 , 时 间 复 杂 度 接 近 线 性 , 但 是 即 使 在 优 化 后 ( 从 x 2 开 始 筛 ) , 也 会 重 复 标 记 合 数 通过上面埃氏筛法可以看到,时间复杂度接近线性,但是即使在优化后(从x^2开始筛),也会重复标记合数 通过上面埃氏筛法可以看到,时间复杂度接近线性,但是即使在优化后(从x2开始筛),也会重复标记合数
Solution
- 从 小 到 大 枚 举 已 经 存 储 的 质 数 , 用 一 个 数 组 存 储 每 个 数 的 最 小 质 因 子 从小到大枚举已经存储的质数,用一个数组存储每个数的最小质因子 从小到大枚举已经存储的质数,用一个数组存储每个数的最小质因子
代码如下:
int primes[N],cnt;//存储质数int v[N];//存储每个数的最小质因子void get_primes(int n){ cnt = 0; for(int i = 2; i <= n; i ++ ){ //如果v[i]=0,表示当前数为质数 if(!v[i]) v[i] = i,primes[++cnt] = i; //从小到大枚举所有已经存储过的质数,让i乘上这些质数 for(int j = 1; j <= cnt; j ++){ //如果当前质数大于i的最小质因子或者i*primes[j]>n就终止 if(primes[j] > v[i] || primes[j] > n/i) break; //i*primes[j]的最小质因子就是primes[j] v[i*primes[j]] = primes[j]; } }}
3. 分解质因数
算数基本定理
- 任 何 一 个 大 于 1 的 正 整 数 都 能 唯 一 分 解 为 有 限 个 质 数 的 乘 积 , 可 写 作 : N = p 1 c 1 p 2 c 2 p 3 c 3 … … p k c k , 其 中 p 1 < p 2 < p 3 < . . . < p k , c i 均 为 正 整 数 任何一个大于1的正整数都能唯一分解为有限个质数的乘积,可写作:N =p_1^{c_1}p_2^{c_2}p_3^{c_3}……p_k^{c_k},其中p_1<p_2<p_3<...<p_k,c_i均为正整数 任何一个大于1的正整数都能唯一分解为有限个质数的乘积,可写作:N=p1c1p2c2p3c3……pkck,其中p1<p2<p3<...<pk,ci均为正整数
- 同 样 依 据 上 面 的 试 除 法 , 枚 举 区 间 [ 2 , n ] , 但 如 果 d 为 n 的 质 因 子 , 同样依据上面的试除法,枚举区间[2,\sqrt{n}],但如果d为n的质因子, 同样依据上面的试除法,枚举区间[2,n],但如果d为n的质因子, n d 也 属 于 n 的 质 因 子 , 这 个 区 间 就 不 够 , 而 这 种 情 况 只 会 出 现 一 次 。 {n\over d}也属于n的质因子,这个区间就不够,而这种情况只会出现一次。 dn也属于n的质因子,这个区间就不够,而这种情况只会出现一次。 因 此 , 需 要 特 判 一 下 , n 是 否 有 这 个 大 于 n 的 质 因 子 因此,需要特判一下,n是否有这个大于\sqrt{n}的质因子 因此,需要特判一下,n是否有这个大于n的质因子
- 反 证 法 : 如 果 出 现 两 次 , 就 会 有 两 个 质 因 子 大 于 n , 相 乘 大 于 n 反证法:如果出现两次,就会有两个质因子大于\sqrt{n},相乘大于n 反证法:如果出现两次,就会有两个质因子大于n,相乘大于n
- 区 间 里 面 枚 举 的 n 的 因 子 都 是 n 的 质 因 子 , 因 为 n 的 合 数 因 子 也 是 由 这 些 质 因 子 构 成 的 , 区间里面枚举的n的因子都是n的质因子,因为n的合数因子也是由这些质因子构成的, 区间里面枚举的n的因子都是n的质因子,因为n的合数因子也是由这些质因子构成的, 在 枚 举 到 合 数 因 子 之 前 它 包 含 的 质 因 子 已 经 筛 掉 了 。 在枚举到合数因子之前它包含的质因子已经筛掉了。 在枚举到合数因子之前它包含的质因子已经筛掉了。
代码如下
int primes[N],cnt;//存储质数int c[N];//存储质因子的次数void divide(int n){ cnt = 0; for(int d = 2; d <= n/i; d ++ ){ //这个d就是n的质因子 if(n % d == 0){ primes[++cnt] = d; //筛掉所有的d,并记录d出现的次数 while(n%d == 0) n/=d,c[cnt]++; } } //如果n大于1.那么这个数就是唯一一个大于根号n的质因子 if(n > 1) {primes[++cnt] = n,c[cnt] = 1}}
拓展:PoLLard’s Rho算法
一个比试除法效率更高的质因数分解算法,暂不讨论
4.约数
约数的定义
- 若 一 个 d 能 整 除 n , 那 么 称 d 是 n 的 约 数 , n 是 d 的 倍 数 , 记 为 d ∣ n ( d ∈ Z , n ∈ Z ) 若一个d能整除n,那么称d是n的约数,n是d的倍数,记为d|n(d∈Z,n∈Z) 若一个d能整除n,那么称d是n的约数,n是d的倍数,记为d∣n(d∈Z,n∈Z)
算数基本定理的推论
- N > 1 且 N ∈ Z , N 的 正 约 数 集 合 为 { p 1 b 1 p 2 b 2 p k b k } , 其 中 0 < = b i < = c i N>1 且 N∈Z,N的正约数集合为\{p_1^{b_1}p_2^{b_2}p_k^{b_k}\},其中0<=b_i<=c_i N>1且N∈Z,N的正约数集合为{ p1b1p2b2pkbk},其中0<=bi<=ci
一 、 计 算 N 的 正 约 数 个 数 一、计算N的正约数个数 一、计算N的正约数个数
- 根 据 推 论 可 以 得 出 , N 的 每 一 个 质 因 子 的 次 数 可 以 出 现 0 到 c i 次 , 那 么 N 的 正 约 数 个 数 为 ( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ . . . ∗ ( c k + 1 ) = ∏ i = 1 k ( c i + 1 ) 根据推论可以得出,N的每一个质因子的次数可以出现0到c_i次,那么N的正约数个数为(c_1+1)*(c_2+1)*...*(c_k+1) = \prod_{i=1}^{k}(c_i+1) 根据推论可以得出,N的每一个质因子的次数可以出现0到ci次,那么N的正约数个数为(c1+1)∗(c2+1)∗...∗(ck+1)=∏i=1k(ci+1)
代码如下:
unordered_map<int,int> primes; //first:质因子 second:质因子的次数for(int i = 2; i <= n/i; i ++ ){ while(n % i == 0){ n/=i; primes[i]++; }}if(n > 1) primes[n]++;int res = 1;for(auto prime:primes) { res *= (prime.second + 1); }
二 、 计 算 N 的 所 有 正 约 数 之 和 二、计算N的所有正约数之和 二、计算N的所有正约数之和
- 每 个 正 约 数 都 是 由 质 因 子 组 成 , 所 有 组 合 的 约 数 之 和 可 以 表 示 为 : ( p 1 0 + p 1 1 + p 1 c 1 ) ∗ ( p 2 0 + p 2 1 + p 2 c 2 ) ∗ . . . ∗ ( p k 0 + p k 1 + p k c k ) 每个正约数都是由质因子组成,所有组合的约数之和可以表示为:(p_1^{0}+p_1^{1}+p_1^{c_1})*(p_2^{0}+p_2^{1}+p_2^{c_2})*...*(p_k^{0}+p_k^{1}+p_k^{c_k}) 每个正约数都是由质因子组成,所有组合的约数之和可以表示为:(p10+p11+p1c1)∗(p20+p21+p2c2)∗...∗(pk0+pk1+pkck)
- ★ 这 里 的 公 式 很 特 别 , 用 代 码 写 的 话 , 用 一 个 变 量 t = p i 0 ( 一 次 都 没 出 现 的 情 况 ) , t = ( t ∗ p i + 1 ) , 可 以 写 循 环 , 时 间 复 杂 度 O ( n ) ★这里的公式很特别,用代码写的话,用一个变量t = p_i^0(一次都没出现的情况),t = (t *p_i + 1),可以写循环,时间复杂度O(n) ★这里的公式很特别,用代码写的话,用一个变量t=pi0(一次都没出现的情况),t=(t∗pi+1),可以写循环,时间复杂度O(n)
代码如下:
unordered_map<int,int> primes; //first:质因子 second:质因子的次数for(int i = 2; i <= n/i; i ++ ){ while(n % i == 0){ n/=i; primes[i]++; }}if(n > 1) primes[n]++;int res = 1;for(auto prime:primes) { int p = prime.first,c = prime.second; int t = 1; while(c --) t = t * p + 1; res *= t;}
4.1 试除法求约数
求N的所有正约数
- 若 d 为 n 的 约 数 , 那 么 n d 也 是 n 的 约 数 , 所 以 约 数 总 是 成 对 出 现 , 若d为n的约数,那么{n\over d}也是n的约数,所以约数总是成对出现, 若d为n的约数,那么dn也是n的约数,所以约数总是成对出现,与 求 质 数 一 样 , 只 需 要 枚 举 最 小 的 约 数 d , 1 < = d < = n ( 注 意 这 里 从 1 开 始 ) , 如 果 d = n , 我 们 只 需 要 特 判 一 下 , 只 要 一 个 求质数一样,只需要枚举最小的约数d,1<=d<=\sqrt n(注意这里从1开始),如果d=\sqrt n,我们只需要特判一下,只要一个 求质数一样,只需要枚举最小的约数d,1<=d<=n(注意这里从1开始),如果d=n,我们只需要特判一下,只要一个
- 推 论 : 一 个 整 数 N 的 约 数 个 数 最 多 2 N 个 推论:一个整数N的约数个数最多2\sqrt N个 推论:一个整数N的约数个数最多2N个
代码如下:
int factor[N],cnt;//存储约数void get_factor(int n){ for(int d = 1; d <= n/d; d ++ ){ if(n%d == 0){ factor[++cnt] = d; //如果d*d!=n if(d != n/d) factor[++cnt] = n/d; } }}
4.2 求1~N每个数的约数
- 若 使 用 上 面 的 试 除 法 , 时 间 复 杂 度 为 O ( N N ) 太 高 若使用上面的试除法,时间复杂度为O(N\sqrt N)太高 若使用上面的试除法,时间复杂度为O(NN)太高
- 这 里 采 用 倍 数 法 , 一 个 数 的 所 有 约 数 不 好 算 , 那 么 反 过 来 思 考 , 求 1 N 的 每 个 数 d 的 倍 数 , d , 2 d , 3 d . . . , [ N / d ] ∗ d 它 们 的 约 数 都 是 d 这里采用倍数法,一个数的所有约数不好算,那么反过来思考,求1~N的每个数d的倍数,d,2d,3d...,[N/d]*d它们的约数都是d 这里采用倍数法,一个数的所有约数不好算,那么反过来思考,求1 N的每个数d的倍数,d,2d,3d...,[N/d]∗d它们的约数都是d
- 时 间 复 杂 度 为 O ( N + N / 2 + N / 3 + . . . + N / N ) ≈ O ( N l o g N ) 时间复杂度为O(N+N/2+N/3+...+N/N) ≈ O(NlogN) 时间复杂度为O(N+N/2+N/3+...+N/N)≈O(NlogN)
- 1 到 N 每 个 数 的 约 数 个 数 的 总 和 大 约 为 N l o g N 个 。 1到N每个数的约数个数的总和大约为NlogN个。 1到N每个数的约数个数的总和大约为NlogN个。
代码如下
vector<int> factor[N];for(int i = 1; i <= n; i ++ ){ for(int j = 1; j <= n/i; j ++ ){ factor[i * j].push_back(i); }}for(int i = 1; i <= n; i ++ ){ for(int j = 0; j < factor[i].size(); j ++ ){ printf("%d ",factor[i][j]); } puts("");}
5.最大公约数、最小公倍数
最大公约数的定义
- 若 d ∣ a , d ∣ b , 则 d 是 a 和 b 的 公 约 数 , 在 所 有 公 约 数 中 取 最 大 那 个 , 称 为 a 和 b 的 最 大 公 约 数 , 记 为 g c d ( a , b ) 若d|a,d|b,则d是a和b的公约数,在所有公约数中取最大那个,称为a和b的最大公约数,记为gcd(a,b) 若d∣a,d∣b,则d是a和b的公约数,在所有公约数中取最大那个,称为a和b的最大公约数,记为gcd(a,b)
最小公倍数的定义
- 若 a ∣ m , b ∣ m , 则 m 是 a 和 b 的 公 倍 数 , 在 所 有 公 倍 数 中 取 最 小 那 个 , 称 为 a 和 b 的 最 小 公 倍 数 , 记 为 l c m ( a , b ) 若a|m,b|m,则m是a和b的公倍数,在所有公倍数中取最小那个,称为a和b的最小公倍数,记为lcm(a,b) 若a∣m,b∣m,则m是a和b的公倍数,在所有公倍数中取最小那个,称为a和b的最小公倍数,记为lcm(a,b)
三个数及以上,同理
公式
一、
∀ a , b ∈ N , l c m ( a , b ) = a ∗ b g c d ( a , b ) \forall a,b\in \mathbb{N}, lcm(a,b) = {a*b\over gcd(a,b)} ∀a,b∈N,lcm(a,b)=gcd(a,b)a∗b
- 证明: 设 d = g c d ( a , b ) , a 0 = a / d , b 0 = b / d , 则 g c d ( a 0 , b 0 ) = 1 , 根 据 最 小 公 倍 数 定 义 , l c m ( a 0 , b 0 ) = a 0 ∗ b 0 , l c m ( a , b ) = l c m ( a 0 , b 0 ) ∗ d = a 0 ∗ b 0 / d 设d=gcd(a,b),a_0 = a/d,b_0=b/d,则gcd(a_0,b_0) = 1,根据最小公倍数定义,lcm(a_0,b_0) = a_0*b_0,lcm(a,b) =lcm(a_0,b_0)*d=a_0*b_0/d 设d=gcd(a,b),a0=a/d,b0=b/d,则gcd(a0,b0)=1,根据最小公倍数定义,lcm(a0,b0)=a0∗b0,lcm(a,b)=lcm(a0,b0)∗d=a0∗b0/d
5.1更相减损术
公式
一、
∀ a , b ∈ N , a > = b , 有 g c d ( a , b ) = g c d ( b , a − b ) = g c d ( a , a − b ) \forall a,b \in \mathbb{N},a>=b,有gcd(a,b) = gcd(b,a-b)=gcd(a,a-b) ∀a,b∈N,a>=b,有gcd(a,b)=gcd(b,a−b)=gcd(a,a−b)
二、
∀ a , b ∈ N , 有 g c d ( x a , x b ) = x ∗ g c d ( a , b ) \forall a,b \in \mathbb{N},有gcd(xa,xb) =x*gcd(a,b) ∀a,b∈N,有gcd(xa,xb)=x∗gcd(a,b)
证明
a > = b , 对 于 任 意 公 约 数 d , 因 为 d ∣ a , d ∣ b , a = [ a d ] ∗ d , b = [ b d ] ∗ d , a − b = ( [ a d ] − [ b d ] ) ∗ d , 所 以 d ∣ ( a − b ) . 因 此 , a , b 公 约 数 集 合 与 b , a − b 公 约 数 集 合 相 同 , a , a − b 同 理 a>=b,对于任意公约数d,因为d|a,d|b,a = [{a\over d}]* d,b = [{b\over d}]*d,a-b =([{a\over d}] - [{b\over d}])*d ,所以d|(a-b).因此,a,b公约数集合与b,a-b公约数集合相同,a,a-b同理 a>=b,对于任意公约数d,因为d∣a,d∣b,a=[da]∗d,b=[db]∗d,a−b=([da]−[db])∗d,所以d∣(a−b).因此,a,b公约数集合与b,a−b公约数集合相同,a,a−b同理
5.2欧几里得算法
公式
∀ a , b ∈ N , b ≠ 0 , g c d ( a , b ) = g c d ( b , a m o d b ) \forall a,b \in \mathbb{N},b\neq 0,gcd(a,b) = gcd(b,a\mod b) ∀a,b∈N,b=0,gcd(a,b)=gcd(b,amodb)
证明
这 里 需 要 分 类 讨 论 a 和 b 的 大 小 情 况 这里需要分类讨论a和b的大小情况 这里需要分类讨论a和b的大小情况
- 若 a < b , 那 么 a m o d b = a , g c d ( b , a m o d b ) = g c d ( b , a ) = g c d ( a , b ) 若a<b,那么a\mod b = a,gcd(b,a\mod b) = gcd(b,a) = gcd(a,b) 若a<b,那么amodb=a,gcd(b,amodb)=gcd(b,a)=gcd(a,b)
- 若 a > = b , 这 上 面 更 相 减 损 术 已 经 讨 论 过 了 , g c d ( a , b ) = g c d ( b , a − b ) , 因 为 a − b = a m o d b , g c d ( a , b ) = g c d ( b , a m o d b ) 若a>=b,这上面更相减损术已经讨论过了,gcd(a,b) = gcd(b,a-b),因为a-b=a\mod b,gcd(a,b) = gcd(b,a\mod b) 若a>=b,这上面更相减损术已经讨论过了,gcd(a,b)=gcd(b,a−b),因为a−b=amodb,gcd(a,b)=gcd(b,amodb)
关于两种求最大公约数的方法
- 首 先 , 我 们 清 除 的 知 道 , 求 出 g c d 也 就 能 算 出 l c m 首先,我们清除的知道,求出gcd也就能算出lcm 首先,我们清除的知道,求出gcd也就能算出lcm
- 一 般 情 况 下 , 使 用 欧 几 里 得 算 法 , 时 间 复 杂 度 为 O ( l o g ( a + b ) ) , 若 遇 到 高 精 度 就 用 更 相 减 损 术 ( 暂 时 没 用 过 ) 一般情况下,使用欧几里得算法,时间复杂度为O(log(a+b)),若遇到高精度就用更相减损术(暂时没用过) 一般情况下,使用欧几里得算法,时间复杂度为O(log(a+b)),若遇到高精度就用更相减损术(暂时没用过)
欧几里得算法实现
int gcd(int a,int b){ //当b=0时,gcd(a,0)=a if(!b){ return a; } else{ return gcd(b,a%b); }}
6. 欧拉函数
前置知识:互质
∀ a , b ∈ N , 若 g c d ( a , b ) = 1 , 则 称 为 a 和 b 互 质 , 对 于 三 个 及 以 上 个 数 , g c d ( a , b , c ) = 1 叫 做 a 、 b 和 c 互 质 , g c d ( a , b ) = g c d ( b , c ) = g c d ( a , c ) 叫 a , b , c 两 两 互 质 。 \forall a,b \in \mathbb{N},若gcd(a,b) = 1,则称为a和b互质,对于三个及以上个数,gcd(a,b,c) = 1叫做a、b和c互质,gcd(a,b) = gcd(b,c) = gcd(a,c) 叫a,b,c两两互质。 ∀a,b∈N,若gcd(a,b)=1,则称为a和b互质,对于三个及以上个数,gcd(a,b,c)=1叫做a、b和c互质,gcd(a,b)=gcd(b,c)=gcd(a,c)叫a,b,c两两互质。
欧拉函数的定义
1 ~ N 中 与 N 互 质 数 的 个 数 叫 欧 拉 函 数 , 记 为 ϕ ( N ) 1~N中与N互质数的个数叫欧拉函数,记为\phi (N) 1~N中与N互质数的个数叫欧拉函数,记为ϕ(N)
公式:
依 据 算 术 基 本 定 理 , 若 N = p 1 c 1 p 2 c 2 . . . p k c k , 则 依据算术基本定理,若N=p_1^{c_1}p_2^{c_2}...p_k^{c_k},则 依据算术基本定理,若N=p1c1p2c2...pkck,则
ϕ ( N ) = N ∗ p 1 − 1 p 1 ∗ p 2 − 1 p 2 ∗ . . . ∗ p k − 1 p k = N ∗ ∏ 质 数 p ∣ N ( 1 − 1 p ) \phi(N) = N * {p_1-1 \over {p_1}} * {p_2-1 \over {p_2}} *...*{p_k-1 \over {p_k}} = N *\prod_{质数p|N}(1 - {1\over p}) ϕ(N)=N∗p1p1−1∗p2p2−1∗...∗pkpk−1=N∗∏质数p∣N(1−p1)
证明:
用 到 了 容 斥 原 理 , 参 考 《 算 法 进 阶 指 南 》 上 的 证 明 , 设 p 、 q 是 N 的 质 因 子 , 1 到 N 中 p 的 倍 数 有 [ N / p ] 个 , 用到了容斥原理,参考《算法进阶指南》上的证明,设p、q是N的质因子,1到N中p的倍数有[N/p]个, 用到了容斥原理,参考《算法进阶指南》上的证明,设p、q是N的质因子,1到N中p的倍数有[N/p]个,
同 理 , q 的 倍 数 有 [ N / q ] 个 , 剩 下 的 有 N − [ N / p ] − [ N / q ] 个 , 但 是 这 里 把 p 和 q 的 倍 数 减 去 两 次 ( p 的 倍 数 一 次 , q 的 倍 数 一 次 ) , 需 要 加 上 那 么 结 果 为 N − N p − N q + N p q = N ∗ ( 1 − 1 p ) ∗ ( 1 − 1 q ) , 同 理 得 出 全 部 与 N 互 质 的 个 数 。 同理,q的倍数有[N/q]个,剩下的有N-[N/p]-[N/q]个,但是这里把p和q的倍数减去两次(p的倍数一次,q的倍数一次),需要加上那么结果为N-{N\over p}-{N\over q} + {N\over pq} = N*(1-{1\over p})*(1-{1\over q}),同理得出全部与N互质的个数。 同理,q的倍数有[N/q]个,剩下的有N−[N/p]−[N/q]个,但是这里把p和q的倍数减去两次(p的倍数一次,q的倍数一次),需要加上那么结果为N−pN−qN+pqN=N∗(1−p1)∗(1−q1),同理得出全部与N互质的个数。
代码如下:
int phi(int n){ int ans = n; for(int i = 2; i <= n/i; i ++ ){ if(n % i == 0){ ans = ans/i*(i - 1); while(n % i == 0) n/=i; } } if(n > 1) ans = ans/n*(n - 1); return ans;}
6.1 求2~N中每个数的欧拉函数
- 根 据 欧 拉 函 数 的 公 式 , 我 们 可 以 先 求 出 所 有 质 数 , 前 面 有 两 种 : 埃 氏 筛 法 、 线 性 筛 法 根据欧拉函数的公式,我们可以先求出所有质数,前面有两种:埃氏筛法、线性筛法 根据欧拉函数的公式,我们可以先求出所有质数,前面有两种:埃氏筛法、线性筛法
- 显 然 , 质 数 p 的 欧 拉 函 数 : p − 1 , 合 数 欧 拉 函 数 就 先 找 到 它 的 质 因 子 , 依 次 得 出 显然,质数p的欧拉函数:p-1,合数欧拉函数就先找到它的质因子,依次得出 显然,质数p的欧拉函数:p−1,合数欧拉函数就先找到它的质因子,依次得出
埃氏筛法求欧拉函数
int primes[N],cnt;//存储质数int phi[N];//phi[i]:表示i的欧拉函数void euler(int n){ //1的欧拉函数是1 phi[1] = 1; for(int i = 2; i <= n; i ++ ) phi[i] = i; for(int i = 2; i <= n; i ++ ){ //i是质数 if(phi[i] == i){ //对于j,多个i质因子 for(int j = i; j <= n; j += i){ phi[j] = phi[j]/i*(i - 1); } } }}
线性筛法求欧拉函数
若 i m o d p j = = 0 , ϕ ( i ∗ p j ) = ϕ ( i ) ∗ p j 若i\mod pj == 0,\phi(i*pj) = \phi(i)*pj 若imodpj==0,ϕ(i∗pj)=ϕ(i)∗pj
否 则 , ϕ ( i ∗ p j ) = ϕ ( i ) ∗ p j ∗ ( 1 − 1 p j ) = ϕ ( i ) ∗ ( p j − 1 ) 否则,\phi(i*pj) = \phi(i)*pj*(1-{1\over pj}) = \phi(i)*(pj - 1) 否则,ϕ(i∗pj)=ϕ(i)∗pj∗(1−pj1)=ϕ(i)∗(pj−1)
int primes[N],cnt;//存储质数int st[N];//st[i]:i的最小质因子int phi[N];//phi[i]:表示i的欧拉函数void euler(int n){ for(int i = 2; i <= n; i ++ ){ if(!st[i]) st[i] = i,primes[++cnt] = i,phi[i] = i - 1; for(int j = 1; j <= cnt; j ++ ){ if(primes[j] > st[i] || primes[j] > n/i) break; //i*primes[j]的最小质因子primes[j] st[i*primes[j]] = primes[j]; //如果primes[j]是i的最小质因子,说明i*primes[j]已经有这个质因子 if(i % primes[j] == 0){ phi[i*primes[j]] = phi[i] * primes[j]; }else{ //primes[j]是新成员 phi[i*primes[j]] = phi[i] * (primes[j] - 1); } } }}
后记:以上笔记是本人参考《算法进阶指南》书籍以及之前所学总结而得,如果知识不完善或者有误,请各位指点。如果对您有帮助,点赞,关注共同进步
发表评论
最新留言
关于作者
