质数
在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数,或者素数。
1.质数的判定——试除法
暴力做法,1到$n$遍历——时间复杂度$O(n)$
优化:n的约数都是成对出现,因此枚举时只枚举其中较小的一个,即2到$\sqrt{n}$,时间复杂度$O(\sqrt{n})$(推荐写成1 <= n / i
,若是i <= sqrt(n)
,比较费时间;若是i * i <= n
,当n接近int
的最大值时,i * i
有溢出风险。)
1 | bool is_prime(int n) |
2.分解质因数——试除法
每个合数都可以写成几个质数相乘的形式,其中每个质数都是这个合数的因数,把一个合数用质因数相乘的形式表示出来,叫做分解质因数。如30=2×3×5 。分解质因数只针对合数。
给定n个正整数$a_i$,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
暴力做法:从小到大枚举所有数,$O(n)$
优化:n中最多只包含一个大于$\sqrt{n}$的质因数,最坏情况$O(\sqrt{n})$,最好情况$O(\log n)$(如当$n-2^k$)
1 | void divide(int n) |
3.筛质数
给定一个正整数n,请你求出1~n中质数的个数。
朴素做法:将所有数从小到大排列,依次把每个数的倍数删掉,剩下的数就是从2到n的质数。(如果p没有被删掉,说明从2到p-1中不存在任何一个p的约数,那p一定是质数。)
1 | int get_primes(int n) |
时间复杂度:$O(n \log n)$
优化:并不需要把每个数的倍数删掉,只需把每个质数的倍数删掉,代码只需很小的改动。——时间复杂度:$O(n \log \log n)$,很接近$O(n)$了。(1到n中有$n / \ln n$个质数)
埃氏筛法
1 | int get_primes(int n) |
优化2:线性筛法,也称为欧拉筛法,思路是:n只会被它的最小质因子筛掉。当$n=10^7$时,线性筛法大概比埃氏筛法快一倍。
1 | int get_primes(int n) //线性筛法 |
如果i % prime[j] == 0
,prime[j]
一定是i
的最小质因数,prime[j]
一定是prime[j] * i
的最小质因数;如果i % prime[j] != 0
,说明prime[j]
一定小于i
的最小质因数,所以prime[j]
也一定是prime[j] * i
的最小质因数。
任何一个合数一定会被筛掉,它一定存在一个最小质因数,设为prime[j]
,当i
枚举到x / prime[j]
时,它就会被筛掉。因为我们是用最小质因子来筛数,而每个数只有一个最小质因子,因此时间复杂度是线性的,即$O(n)$。
约数
1.试除法求约数
给定n个正整数$a_i$,对于每个整数$a_i$,请你按照从小到大的顺序输出它的所有约数。
思路和试除法判断质数相似,从小到大枚举n的约数(只枚举一对中小的那个),时间复杂度为$O(\sqrt{n})$。
1 | vector<int> get_divisors(int n) |
2.约数个数
定理:如果一个数因数分解之后可以写成:$N=p_1^{ \alpha_1} \cdot p_2^{\alpha_2} \cdot \dots p_k^{\alpha_k}$,那么它的约数个数为:$(\alpha_1+1)(\alpha_2+1)\dots (\alpha_3+1)$。
(数N的约数d一定可以写成:$d=p_1^{ \beta_1} \cdot p_2^{\beta_2} \cdot \dots p_k^{\beta_k}$,其中对每一个$\beta_i$,一定有$0 \le \beta_i \le \alpha_i$;N的每一个约数就对应着一组不同的$\beta_i$的取值,因此N的约数的个数就是$\beta_i$的不同取值的组合数,即$(\alpha_1+1)(\alpha_2+1)\dots (\alpha_3+1)$。)
冷知识:
int
范围内的整数,约数个数最多的数,它的约数大约有1500个给定n个正整数$a_i$,请你输出这些数的乘积的约数个数,答案对$10^9+7$取模。
题目是让求数$a_1 \cdot a_2 \dots a_n$的乘积的约数的个数,我们可以分别求出每个数的约数的个数,把所有的约数和指数用一个哈希表存起来,然后套用公式即可。
1 |
|
3.约数之和
定理:如果一个数因数分解之后可以写成:$N=p_1^{ \alpha_1} \cdot p_2^{\alpha_2} \cdot \dots p_k^{\alpha_k}$,那么它的约数之和为:$(p_1^{ 0} + p_1^{1}+ \dots p_1^{\alpha_1})(p_2^{ 0} + p_2^{1}+ \dots p_2^{\alpha_2}) \dots (p_k^{ 0} + p_k^{1}+ \dots p_k^{\alpha_k})$。
(将上式展开,一共有$(\alpha_1+1)(\alpha_2+1)\dots (\alpha_3+1)$项,每一项都是$p_1^{ \beta_1} \cdot p_2^{\beta_2} \cdot \dots p_k^{\beta_k}$,都是N的一个约数,且每个数都不同,则上式就是N的约数之和。)
给定n个正整数$a_i$,请你输出这些数的乘积的约数之和,答案对$10^9+7$取模。
1 |
|
4.求最大公约数
欧几里得算法,也叫辗转相除法。a和b的最大公约数$=$ a模b和b的最大公约数,$(a, b)=(b, a \, mod \,b)$,这样就可以用递归写了,时间复杂度为$O(\log n)$。
(设$a \,mod\, b=a-c \cdot b$,则$(a, b)=(b, a-c \cdot b)$。由d能整除a,d能整除b,则d能整数ax + by,这证上式成立。)
给定n对正整数$a_i,b_i$,请你求出每对数的最大公约数。
1 |
|