数论算法
数论
基本概念
- x 和 y 的最小公约数 gcd(x,y)
- x 和 y 的最大公倍数 lcm(x,y)
一个很有用的定理
$ gcd( x , y ) * lcm ( x , y ) = x * y $
证明
$ x=A_1^{m1} *A_2^{m2} *A_3^{m3} …… *A_k^{mk} $
$ y=A_1^{n1} *A_2^{n2} *A_3^{n3} …… *A_k^{nk} $
得到:
$ x*y=A_1^{m1+n1} *A_2^{m2+n2} …… *A_k^{mk+nk} $
$ gcd(x,y)=A_1^{min(m1,n1)} *A_2^{min(m2,n2)} …… *A_k^{min(mk,nk)} $
$ lcm(x,y)=A_1^{max(m1,n1)} *A_2^{max(m2,n2)} …… *A_k^{max(mk,nk)} $
很显然, $ min(a,b)+max(a,b)=a+b $
$ gcd(x,y)*lcm(x,y)=A_1^{min(m1,n1)+max(m1,n1)} *A_2^{min(m2,n2)+max(m2,n2)} …… *A_k^{min(mk,nk)+max(mk,nk)} =x *y $
求gcd:
欧几里得算法
核心的定理:
( 以下均假设 a 和 b 都为正数 且 a<=b )
$ gcd(a,b)=gcd(a,b \bmod a)=(b \bmod a,a) $
证明
设 $ b=k*a+r $
则 $ b \bmod a=r=b-k*a $
又 $ d \mid b , d \mid a $
有 $ d \mid (b \bmod a) $
可知 d 既是 a 和 b 的最大公因数,又是 b 和 b mod a 的公因数
而 gcd( b , b mod a ) 肯定不会大于 gcd( a , b )
所以 d 同时也是 b 和 b mod a 的最大公因数
现在再来思考怎样求出 gcd( a , b )
从上面这个定理得到启发,我们可以将求 gcd( a , b ) 的问题转化为求 gcd( b mod a,a ) 的问题
然后不断递归下去,直到不能再简化 a 和 b,也就是其中一个数为 0 的情况
int gcd(int a,int b){
if(a>b) return gcd(b,a);
if(b%a==0) return a; //此时已经可以知道gcd=a了,不用再递归
//例: gcd(3,6)=3 gcd(3,5)=gcd(2,3)=gcd(1,2)=1
return gcd(b%a,a);
}
当然,任何数和 0 之间是不能求gcd的,但是写代码时以求方便,可以直接递归到 0 这一层
int gcd(int a,int b){
if(a>b) return gcd(b,a);
if(a==0) return b;
//例: gcd(1,2)=gcd(0,1)=1
return gcd(b%a,a);
}
顺水推舟,我们发现在求gcd的过程中,还可以同时求出许多其他有用的信息
比如,不定方程的一个解
从中看出了一个什么不定方程呢?
设 $ gcd(a,b)=d $
$ ax+by=d $
(以下的 "/" 均表示整除(向下取整))
是否一定存在整数 x , y 使得 ax + by = d 成立呢?
证明
假设 ax + by 的最小正整数值为 s
$ 0 <=a \bmod s < s $
$ a \bmod s=a-(a/s)s=a-(a/s)(ax+by)=(1-(a/s)x)a+(-y(a/s))b $
可知 a mod s 也是 ax + by 的一个可能值
而如果 a mod s > 0,那么 a mod s >= s (由 s 的定义可知)
又 a mod s < s (由取模运算本身的性质可知)
所以矛盾,$ a \bmod s =0 $
s 一定是 a 的因数,同理得到 s 也是 b 的因数,s 是 a 和 b 的公因数,s <= d
而 $ d \mid a,d \mid b $
则 $ d \mid (ax+by),d \mid s ,d<=s $
得到 d = s
这样看来,我们不仅证明了 d 能写成 ax + by 的形式,同时也证明了它是满足这个条件的最小的正整数
这个定理又叫裴蜀定理(提出它的那个数学家就叫裴蜀,这名字真的怪)
现在我们将上式进行变形:(以下的 "/" 均表示整除(向下取整))
已知 $ d=gcd(a,b)=gcd(b \bmod a,a) $
$ d=ax_1+by_1 $
$ d=(b \bmod a)x_2+ay_2=(b-(b/a)a)x_2+ay_2=a(y_2-(b/a)x_2)+bx_2 $
可知 $ y_1=x_2 $
$ x_1=y_2-(b/a)x_2 $
发现没有?只要知道了递归中下一层的解,就可以轻松求出这一层的解
将求 gcd 的函数改改就行了
struct res{
int d,x,y;//作为一个三元组返回结果
}
res work(int a,int b){
if(a=0){ //a=0时,0*x+b*y=gcd(0,b)=b
res r;
r.d=b;r.x=0;r.y=1;
return r;
}
if(a>b) return work(b,a);
res r1,r2;
r2=work(b%a,a);
r1.d=r2.d;r1.y=r2.x;r1.x=r2.y-b/a*r2.x;
return r1;
}
例:
对方程 $ 99x \equiv 9 \pmod{36} $ 的递归求解过程如下:
将原方程转换为:
$ 99x+36y=9 $
然后递归:
a | b | d | x | y |
---|---|---|---|---|
99 | 36 | 9 | -1 | 3 |
36 | 99 | 9 | 3 | -1 |
27 | 36 | 9 | -1 | 1 |
9 | 27 | 9 | 1 | 0 |
0 | 9 | - | 0 | 1 |
结果为:x = -1 , y = 3 (即存在 x = -1 满足这个方程)
对 $ ax+by=d $ 这个方程两边同时乘上一个整数 k
很显然,$ axk+byk=dk $ 表示
另一个方程 $ ax'+by'=dk $ 也有整数解,而且 x' = xk , y' = yk
事实上,对于任意整数 m," $ ax+by=m $ 有整数解 "的充要条件便是 m 是 d 的倍数
证明
$ m=k_1dx+k_2dy=(k_1x+k_2y)d $
可知 m 是 d 的倍数
现在我们的目标就变成了:
已知 a , b , m 是整数,求不定方程 $ ax+by=m $ 的整数解
首先,我们已经知道,如果 m 不是 gcd( a , b )的倍数,那么此方程无整数解,去掉这种情况
它既然是个不定方程,这不定二字就说明了它可不止有一个解
按照上面的那些步骤,我们已经可以求解出它的一个可行解了
那么其余那些解呢?这些解之间又有什么联系呢?
发现: $ a(x+b/d)+b(y-a/d)=ax+by=m $
像这样以此类推,方程的解可以是:$ x'=x+(b/d)k,y'=y-(a/d)k $ ,其中 k 为任意整数
所以我们只需求出其中一组解,就可以推出其余的全部了
void work2(int a,int b,int m){
res r=work(a,b);
if(m%r.d!=0) return;
for(int k=0;k<N;k++){
x[k]=r.x+(b/r.d)*k;
y[k]=r.y-(a/r.d)*k;
}
}
孙子定理
又名中国剩余定理 ,但我更喜欢叫它孙子
先丢一个情景:
夏天,阳光正好,可不幸的是你是一名正在军训的高中牲,而且还是班上的体育委员
教官把数人头的任务扔给了你,然后自己享受阴凉去了
你当然不想在烈日下一个一个一个一个数半天,同学们也不想久站军姿
为了尽快结束任务,聪明的你如韩信附体,想到可以通过变化阵型(即行列数)来算出总共有多少人
例如:排成五人一列时多出一个人,排成七人一列时多出两个人……根据这些算总数
用数学式子表示出来就一目了然了:
\(
\begin{cases}
x \equiv a_1 & \pmod {m_1} \\
x \equiv a_2 & \pmod {m_2} \\
...... \\
x \equiv a_n & \pmod {m_n}
\end{cases}
\)
(m1 , m2 ...... mn 互质)
反正我第一次看到这个式子时完全无从下手……
先来从最简单的情况看起;
$ x \equiv a \pmod{m} $
很显然,存在 x = a 满足此式
再添加一个条件呢?
\(
\begin{cases}
x \equiv a_1 & \pmod {m_1} \\
x \equiv a_2 & \pmod {m_2}
\end{cases}
\)
先尝试一下,让 x 和这两个模数都产生点关系,使得计算其中一个式子时可以把另一个式子中的元素消去
比如 $ x=a_1m_2+a_2m_1 $
好像有点眉目了!
这样可以保证 x 模 m1 时把 a2 消去,又可以在模 m2 时把 a1 消去
但是 a1*m2 (mod m1)可不见得等于 a1 (mod m1) 呀
所以还需要修补一下,给 a1*m2 添加一点东西(设为 k 好了)使得
$ a_1m_2k \equiv a_1 \pmod {m_1} $
很自然地会想到:
$ m_2k \equiv 1 \pmod {m_1} $
k 不就是 m2 在模 m1 情况下的逆元嘛?!
对于 a2*m1 也是一样的思考过程
所以得出 $ x= a_1m_2m_2^{-1} + a_2m_1m_1^{-1} $
受此启发,再来考虑有 n 个条件的情况:
首先要使计算每个条件时不受其他条件的干扰
让 $ x=a_1m_2m_3......m_n+......+a_nm_1m_2......m_{n-1} $
这样比如说对于第一个条件 $ x \equiv a_1 \pmod {m_1} $
模 m1 时就会把 x 这个多项式除了第一项的其余项都约掉,留下 a1m2m3......mn
为了简便,设 $ M_1=m_2m_3......m_n ,M_2=m_1m_3......m_n $ ,以此类推
再对 x 进行同样的修补:
$ x= a_1M_1M_1^{-1} + a_2M_2M_2^{-1} ......+ a_nM_nM_n^{-1} $
记住: $ M_kM_k^{-1} \equiv 1 \pmod {m_k} $
大功告成!!!
举个例子庆祝一下:
\(
\begin{cases}
x \equiv 2 & \pmod {3} \\
x \equiv 1 & \pmod {5} \\
x \equiv 2 & \pmod {7}
\end{cases}
\)
$ M_1= 35 ,M_2= 21 ,M_3= 15 $
$ M_1^{-1}=2, M_2^{-1}=1, M_3^{-1}=1 $
$ x=2M_1M_1^{-1} +M_2M_2^{-1} +2M_3M_3^{-1} =191 $
再验证一下,是不是 perfect ?!
但这只得出了一个可能解啊
再细看一下我们已经得到的式子
可以发现 $ x + km_1m_2m_3......m_n $ 即为答案 ( k 为任意整数 )
我懒得证了,反正能用就行
欧拉定理
欧拉函数: $ \phi (x)= n $
其中 n 为在小于x的正整数中和 n 互质的数的个数
定理内容:无论正整数 a 是多少,都有:
$ a^{\phi(n)} \equiv 1 \pmod n $
特殊地,当 n 是质数时,可以得到一个特殊结论
$ a^{n-1} \equiv 1 \pmod n $
也即 费马小定理
不是那个空间太小写不下的费马定理,那个是费马大定理
对群论的知识已经有所了解了吧?
那么请证明欧拉定理
不不不,我可不想像那些教材一样直接甩一个莫名其妙的结论和注意力惊人的证明过程
总之,虽然小标题叫“欧拉定理”,但我们现在先暂时忘记它
将思路放到群论的深海里
什么?!你还不知道神马是群论?快去看鄙人写的另一篇博客--》群论无痛入门