数论基础(更新中)
标签: 算法笔记 数论
一、入门知识
本单元难度(le)小学六年级数学。
1.整数除法
除法是四则运算运算之一,作为乘法的逆运算。已知积与其中一个因数求另一因数的运算叫做除法.。
整数除法常有如下表达:
一般地,我们称 a 为被除数,b 为除数,c 为商,d 为余数.
。
亦可简单推出如下逆运算:
2.整除
如果 a 能把 b 除尽,也就是(a div b)余数为0,则我们称 a 整除 b ,也称 b 被 a 整除.
记为:
中间的竖杠表示为整除符号,读作:a 整除 b.
数论之路,皆由“整除”始。
3.整除的性质
-
自反性
对于任意 (n),有(n|n). -
传递性
对于任意 (a|b,b|c),都有(a|c). -
反对称性
对于任意 (a | b, b | a), 都有(a = b).
4.约数与倍数
如果(a|b),那么称 a 是 b 的约数,b 是 a 的倍数。同时称,a 是 b 的因子(因数)。
因此,我们有一个重要推论:
对于任何整数(n ge 2),(n)至少有两个因子:1和 (n)(它本身).
我们将这两个因子称为(n)的平凡因子.
quiz1.如何计算([1, n])中每个数因数的个数?
int p_num[MAXN];
for(int i = 1; i <= N; i ++)
for(int j = i; j <= N; j += i)
p_num[j] = 1;
//O(nlogn)
5.质数
一个整数不存在非平凡因子,我们就称它为质数(亦称为素数).
不是质数的整数我们称它为合数,即合数有大于等于一个非平凡因子.
特殊地,1 不是质数,是合数.
例如:
2 只存在两个平凡因子,即1 和2,不存在非平凡因子.2 是质数.
5 只存在两个平凡因子,即1 和5,不存在非平凡因子.5 是质数.
4 存在非平凡因子 2. 4 不是质数,是合数.
1e9+7 不存在非平凡因子.1e9+7 是质数.
数论都是围绕质数概念所展开,理解质数是走进数论大厦的第一步。
6.判断质数
对于任意整数(n),判断在([2, sqrt{n}]) 是否存在整数(i),使得(i | n).
存在这样的整数(i),(n)是合数.
否则,(n) 即为质数.
证明:
对于任何非平方数 (n),它的因子一定成对出现.
对于平方数 (n),除因子(sqrt{n})外,其余因子亦成对出现.
假设(n)存在一个因子 (i) 落在 ((sqrt{n}, n)) 区间,与 (i) 相对应的 (n) 的另一个因子 (n / i) 一定落在 ([2, sqrt{n}))这个区间。因为因子成对出现,所以只要判断([2, sqrt{n}]) 这个区间是否存在因子就可以。
bool is_prime(int n)
{
if(n == 1) return false; //1是合数.
for(int i = 2; i * i <= n; i ++) //在[2, sqrt(n)]的区间中找因子.
if(!(n % i))
return false;
return true; //没找到因子是质数.
}
//O(sqrt(n))
quiz2. 质数有无限个.
如何证明?
反证法:
假设质数的个数为有限个,分别为:(p_1,p_2,p_3,cdots,p_n).
设(M = p_1 imes p_2 imes p_3 imes cdots imes p_n + 1).
易得,(M mod p_1 = 1, M mod p_2 = 1, M mod p_3 = 1, cdots , M mod p_n = 1).
所以,(M)只存在(1, M)两个因子,即(M)也是质数,但(M
otin {i leq n | q_i }).与假设矛盾.
所以,假设结论的逆命题,质数的个数为无限个成立.
tip 关于质数分布的一点小性质
虽然我们目前没有摸清楚质数的具体分布情况,但是我们能给出近似的:
设(pi (n))为不超过(n) 的质数个数:$$pi(n) sim frac{n}{ln{n}}$$
由此可得推论:
- (n\,) 质数附近的质数密度是(1/ln{n}).
- 第(\,n)个素数(p_n sim nln{n}).
7.如何求质数?
- 朴素法
运用#6 如何判断质数的方法,对区间每个数进行枚举,然而这个方法实在算不上高效,复杂度是(sum_{i=1}^{n}sqrt{i} = O(nsqrt{n}))
int isprime[MAXN], prime[MAXN], cnt = 0;
isprime[1] = 1;
for(int i = 2; i <= N; i ++)
for(int j = 2; j * j <= i; j ++)
if(!(i % j)){
isprime[i] = 1;
break;
}
for(int i = 1; i < N; i ++)
if(!isprime[i])
prime[++ cnt] = i;
//O(n * sqrt(n))
- 埃氏筛法
何为筛法?筛,就是筛选的意思。在一个区间中筛选出不是素数的数来,剩下的数就是素数.
首先找到第一个素数(2),用(2) 筛掉区间中所有(2) 的倍数(这些倍数因为已经有非平凡因子了,就一定是合数.),再用第二个素数(3) 筛掉区间中所有(3) 的倍数.当询问到(4) 的时候,因为(4) 已经被(2) 筛掉了,所以(4) 不是质数.第三个质数是(5),以此类推.筛掉区间的所有合数.
埃氏筛法用到了筛法的思想,时间复杂度为(O(nln{ln{n}})\,)时间复杂度虽然差于线性筛(一个合数可能被多个质数筛过),但也已经非常接近(O(n))了.
int isprime[MAXN], prime[MAXN], cnt = 0;
isprime[1] = 1;
for(int i = 2; i <= N; i ++){
if(!isprime[i]){ //若是此数没有被之前任何一个素数筛掉,那么它就是质数.
prime[++ cnt] = i;
for(int j = 2; i * j <= N; j ++) //接着用这个质数,筛掉它的倍数.
isprime[i * j] = 1;
}
}
//>O(n)
- 欧拉线性筛法
对于小范围质数来说(算法竞赛范围),欧拉筛法是一种很实用的素数求法.还可以用这种筛法筛出欧拉函数.
欧拉筛的思路也是筛掉合数,留下的没被筛掉的数就是质数.欧拉筛优于埃氏筛的地方在于欧拉筛保证每个数都只被其最小的质因数筛掉.因此每个数都只被筛掉一次,所以是线性的.
int isprime[MAXN], prime[MAXN], cnt = 0;
for(int i = 2; i <= N; i ++){
if(!isprime[i])
prime[++ cnt] = i;
for(int j = 1; j <= cnt && i * prime[j] <= N; j ++){
isprime[i * prime[j]] = 1;
if(i % prime[j] == 0) //这个if语句是关键.当i中包含了一个质因数时,说明再往后的乘积
break; //的最小质因数就都是此刻i中包括的那个了质因数了,而不是之后的
} //prime[i]了,这就不满足每个合数只能被它最小的质因数筛掉的性质了.
}
//O(n)
8.质因数分解
对(1)以外的任何正整数都可以拆成质数乘积的方式,这个过程叫做质因数分解.
例如:
(5 = 5 = 5^1)
(15 = 3 imes 5 = 3^1 imes 5^1)
(24 = 2 imes 2 imes 2 imes 3 = 2^3 imes 3)
(60 = 2 imes 2 imes 3 imes 5 = 2^2 imes 3 imes 5)
基本算术定理规定,任何一个大于1的正整数,这样的质因数分解形式是唯一的.
在数学中,对于小整数,我们可以使用短除法来简单求出一个数的质因数的分解.
质因数分解可以在(O(sqrt{n}))中完成.
int factorize(int x, int p[]) // 将x进行质因数分解,并将质因数存放在数组p中,返回质因数个数.
{
int cnt = 0; //cnt记录素因数个数.
for(int i = 2; i * i <= x; i ++){ // 在[2, sqrt(x)]中枚举约数.
while(x % i == 0){ //找到一个质因数就将其不断地从x中除去.
p[++ cnt] = i;
x /= i;
}
}
if(x > 1) p[++ cnt] = i; //如果最后所剩的数大于1,说明还是素数,要放在p中.
return cnt;
}
- 有时间复杂度更优的质因数分解方法 PollardRho-快速分解质因数 .
quiz3.在质因数分解下,如何表示a * b, a / b, a | b?
设$$a = p_1{a_1}p_2{a_2}p_3^{a_3}cdots, $$ $$b = p_1{b_1}p_2{b_2}p_3^{b_3}cdots,$$ $$p_i in { 小于等于max({a, b})的全部质数,共n个. },0 le a_i,b_i. $$
则有
$a imes b = p_1^{a_1 + b_1} ullet p_2^{a_2 + b_2} ullet p_3^{a_3 + b_3} ullet cdots (
)a div b = p_1^{a_1 - b_1} ullet p_2^{a_2 - b_2} ullet p_3^{a_3 - b_3} ullet cdots (
)a | b = a_i le b_i, forall, i le n$
quiz4.在质因数分解下,如何求一个数的约数个数?
设$$a = p_1{a_1}p_2{a_2}p_3^{a_3}cdots,
p_i in { 小于等于max({a, b})的全部质数,共n个.},; 0
le a_i.$$
则有(d(n) = (1 + a_1) ullet (1 + a_2) ullet (1 + a_3) ullet cdots.)
9.最大公约数与最小公倍数
最大公约数,英文为greatest common divisor,简记为gcd.
最小公倍数,英文为least common multiple,简记为lcm.
什么是最大公约数?什么是最小公倍数?
(gcd(a, b))代表(a)和(b)的最大公约数.令(r(a) = {d;|;a\,\,mod\,\,d = 0}, r(b) = {d;|;b\,\,mod\,\,d = 0}),则(gcd(a, b) = max{d in [r(a) cap r(b)]}). 即(gcd(a,b))为能同时整除(a,b)的最大的整数.
(lcm(a, b))代表(a)和(b)的最大公倍数.令(R(a) = {m;|;m \,\,mod\,\, a = 0}, R(b) = {m;|;m \,\,mod\,\, b = 0}),则(lcm(a, b) = min{m in [R(a) cap R(b)]}). 即(lcm(a,b))为能同时被(a, b)整除的最小整数.
例如,(gcd(10,12) = 2,; gcd(36, 48) = 12,; gcd(25, 36) = 1,;lcm(12, 20) = 120,;lcm(7, 11) = 77).
证明(gcd(a, b) imes lcm(a, b) = a imes b).
根据基本算术定理,我们可以将(a)表达为(a = p_1^{a_1}p_2^{a_2}p_3^{a_3}cdots), 将(b)表达为(b = a = p_1^{b_1}p_2^{b_2}p_3^{b_3}cdots).
因为(gcd(a,b))是能同时整除(a, b)的最大整数,所以(gcd(a, b) = p_1^{min{(a_1, b_1)}}p_2^{min{(a_2, b_2)}}p_3^{min{(a_3, b_3)}}cdots).若(p_i)的指数小于(min{(a_i, b_i)}),则不满足最大,如(p_i)的指数大于(min{(a_i, b_i)}),则不满足能整除(a, b).
因为(lcm(a, b))是能同时被(a, b)整除的最小整数,所以(lcm(a, b) = p_1^{max{(a_1, b_1)}}p_2^{max{(a_2, b_2)}}p_3^{max{(a_3, b_3)}}cdots),若(p_i)的指数大于(max{(a_i, b_i)}),则不满足最小,如(p_i)的指数小于(min{(a_i, b_i)}),则不满足同时能被(a, b)整除.
综上,(gcd(a, b) imes lcm(a, b) \ = p_1^{min{(a_1, b_1)}}p_2^{min{(a_2, b_2)}}p_3^{min{(a_3, b_3)}}cdots imes p_1^{max{(a_1, b_1)}}p_2^{max{(a_2, b_2)}}p_3^{max{(a_3, b_3)}}cdots \ = p_1^{max{(a_1, b_1)}+min{(a_1, b_1)}}p_2^{max{(a_2, b_2)} + min{(a_2, b_2)}}p_3^{max{(a_3, b_3)}+min{(a_3,b_3)}}cdots\ = p_1^{(a_1 + b_1)}p_2^{(a_2 + b_2)}p_3^{(a_3+b_3)}cdots \ = a imes b).
如何求最大公约数?
欧几里得辗转相除法
原理:(gcd(a, ;b) = gcd(b,;a\,\,mod\,\,b)).
求两个数的gcd,就等价于求一个数和对另一个数取模的gcd。这样可以不断缩小问题规模,直到发现一个数能被另一个数整除时,则除数就是原问题的最大公约数.
具体地,我们要求(gcd(a,;b)),
Step1:如果(a < b),则交换(a, b)的值.
Step2: 如果(b = 0),说明出现能够整除的情况,则此时的(a)即为所求,为原问题的最大公约数,结束操作.
Step3: 如果(b
ot = 0),我们此时就要求(gcd(b, ;a\,\,mod\,\, b )),循环执行Step1.
时间复杂度:理论上小于(O(log{n})).
int gcd(int a, int b)
{
return b? gcd(b, a % b): a; //运用三元运算符,可使代码变得简洁优美.
}
quiz 5 证明欧几里得辗转相除法的正确性.
令(a > b,\,r(a) = {d;|;a\,\,mod\,\,d = 0}, r(b) = {d;|;b\,\,mod\,\,d = 0}). 易知,(r(x))为(x)的全体约数的集合.
令(d)为(\,a,b)的任意公约数,即(d in [r(a) cap r(b)]).
我们可以将(a, b)分别表示为(a = k_1d, b = k_2d). 即有,(a - b = (k_1 - k_2)d). 因为(k_1 - k_2)也为整数,所以(d ;|; (a - b)).即(d)为(a, \,b)的公约数约数同时也为(b,\,a - b)的公约数.
所以,(r(a,\,b) = r(b,\,a - b) = r(b,\, a - 2 * b) = r(b,\,a - 3 * b) =cdots = r(b,\,a \,\,mod b )).因为它们的公约数集合相等,所以集合中的最大元素也一定相等,即最大公约数也一定相等.得证.
如何求最小公倍数?
我们求出以不大于(O(log{n}))的复杂度求出gcd后,可以利用公式(lcm = a imes b \,/\,gcd\,)简单求出最小公倍数.要注意的是,在编程中要写成lcm = a / gcd * b
,防止超出整型范围.
tip 关于斐波那契数列的最大公约数
求斐波那契数列的相邻两项的最大公约数是辗转相除法的最坏情况.因为斐波那契数列中,每两项都是互质的.
记(F[n])为斐波那契数列的第(n)项。我们有(gcd{(F[a],F[b])}=F[gcd{(a,b)}]).
参考证明:https://www.douban.com/group/topic/33566582/.
二、取模理论
1.模
普遍地,我们可以这样表达除法:$$a = lfloor {frac{a}{p}} floor imes p + a % p$$
其中,(p)是除数,(lfloor {frac{a}{p}} floor)是商,(a \% p) 是余数.
(a \% p)读作(a) 模 (p),即是(a) 在除以(p)下的余数.
- 值域
由于,模运算的本质实取余,所以模运算的结果一定落在([0,; p - 1]).
在一些问题中,若想使模运算的结果落在([1, ;p]), 可以(a - 1) % p + 1
这样计算.
2.随时取模性质
在程序中,因为我们担心一个式子中的某步运算结果会超出整型范围,所以我们想到要对式子进行随时取模.
一个运算中只包含加法和乘法,且如果运算结果只是要对(p)取模,那么我们可以在运算中随时对(p)取模,结果不变.
例如,((a + b imes c + d) \% p = ((a + b imes c \% p) \% p + d) \% p).这样就能尽量保证每步运算结果都不超过(p).
quiz 6 运用随时取模性质,如何证明能被(3)整除的数的充要条件是各个数位上的数加和也一定能被(3)整除.
设(n = overline{abcd} = a imes 1000 + b imes 100 + c imes 10 + d).
(n \% 3 \ = (a imes 1000 + b imes 100 + c imes 10 + d) \% 3\=(a imes 1000 \% 3+ b imes 100 \% 3+ c imes 10 \% 3 + d) \% 3 \ = (a + b + c+ d) \% 3)
证毕.
始编辑于2019/09/16 13:00
最新编辑于2019/11/22 11:56