欧几里德
首先,我们知道有一个东西,叫:gcd!(就是最大公约数)
int gcd (int x,int y)
{ return y?gcd(y,x%y):x; }
那么为什么是这样的呢么? 其实作者我一开始也不知道!
介绍:
我们假设
gcd(x,y)=k
g
c
d
(
x
,
y
)
=
k
那么就会有
x=g1∗k,y=g2∗k x = g 1 ∗ k , y = g 2 ∗ k
于是我们试想,如果我们把x 与 y 的相同的部分,比如(x>y)将:
x变成了x-y,即
x=(g1−g2)∗k,y=g2∗k
x
=
(
g
1
−
g
2
)
∗
k
,
y
=
g
2
∗
k
那么我们就是将 k 的信息保留了下来,但是将数据的大小缩小了!这不是一件好事吗?
那么在无数次的相减之后,我们就知道了,最后肯定会减到 一个的系数是 为 0 ,一个的系数是 为 1!因为如果两的数之间还有倍数,那么就是说它就不是最大公约数了,与原先的假设所违背!!
好吧,就是最后只剩下 了 k ,就是我们要的答案!
于是,这玩意就是我们熟悉的更相减损法!
具体操作:我们来拿18 与 48 来举例子!
18 48 -> 18 30 -> 18 12 -> 6 12 -> 6 6 -> 6 0
因为每 2 个不相同的数字,在相减了之后,总会变的相同,不然不就死循环了吗??这样之后,所得到的数不就是gcd的答案吗?
更相减损法
于是代码奉上:
#include <iostream>
#include <stdio.h>
using namespace std;
int gcd(int x,int y)
{
while(x!=y)
{
if(x>y) x-=y;
else y-=x;
}
return x;
}
int main()
{
int a,b;
puts("G_G");
scanf("%d%d",&a,&b);
printf("%d\n",gcd(a,b));
}
辗转相除法
那么我们会发现了一个问题: 每一次只是减一次,会不会太慢?
答案是肯定的!
那么比如 100000 与 1 ,每一次只是减一次,那么总的来说,只需要 mod 一次,不就是好了吗?
于是代码奉上:
int gcd (int x,int y)
{
return y?gcd(y,x%y):x;
}
所以就是这么多啦~(≧▽≦)/~啦啦啦!
拓展欧几里德
介绍:
其实它是用来解方程
ax+by=1
a
x
+
b
y
=
1
的方程的!
那么为什么这么说呢?
我们会发现我们在更相减损的时候,因为x=k1*g,y=k2*g 于是会有k1与k2 的系数在发生值的变化,(土话来说就是)就是你大了就是你减我,我大了就是我减你,直到互质为止,就是(k1,k2)=1!
那么我们会发现因为最后的k1与k2会互质也就是:”1”,那么我们把它们各自相减过对方的次数记为x0与y0,就会有
k1∗(x0)+k2∗(y0)=1 k 1 ∗ ( x 0 ) + k 2 ∗ ( y 0 ) = 1 这是显然的!
那么思考在什么情况之下,这个等式会成立呢?
条件:
其实就是在同时乘上一个gcd(x,y) 就变成了
k1∗g∗x0+k2∗g∗y0=g k 1 ∗ g ∗ x 0 + k 2 ∗ g ∗ y 0 = g
即:A∗x0+B∗y0=g 即 : A ∗ x 0 + B ∗ y 0 = g
所以不难发现,当且仅当 x0,与y0互质的时候就是了 x 0 , 与 y 0 互 质 的 时 候 就 是 了 不难证明吧!
证法:
我们要求:
ax+by=1 a x + b y = 1于是假设我们知道了
b∗x0+amod−by0=1 b ∗ x 0 + a m o d − b y 0 = 1那么,我们来移项一波!
bx0+amod−y0=bx0+(a−a/b∗b)y0=1 b x 0 + a m o d − y 0 = b x 0 + ( a − a / b ∗ b ) y 0 = 1
ay0+b∗(x0−a/b∗y0)=1 a y 0 + b ∗ ( x 0 − a / b ∗ y 0 ) = 1
那么这样的话,我们就知道了 y0就是x的一个解,x0-a/b*y0就是 y0的解
那么就这样递归下去……
就有解了!
代码:
#include <bits/stdc++.h>
using namespace std;
int w,x,y;
void exgcd(int a, int b, int &x, int &y)
{
if (b != 0)
{
exgcd(b, a % b, x, y);
x -= (a / b) * y;
std::swap(x, y);
}
else
x = 1, y = w;
}
int main()
{
srand(time(NULL));
for (w = 1; w <= 20; w++)
{
exgcd(5, 7, x, y);
printf("%d %d\n", x, y);
}
return 0;
}
逆元:
背景:组合数:Cmn=m!((m−n)!∗n!)mod(1e9+7) 背 景 : 组 合 数 : C n m = m ! ( ( m − n ) ! ∗ n ! ) m o d ( 1 e 9 + 7 )
其实信息的我们知道,有时候,组合数会变的十分的大!所以一般会有一个MOD 来取余运算!
但是有时候这个除法来取余十分的容易出差错!
比如
124mod(5)=3
12
4
m
o
d
(
5
)
=
3
但是有时候会变成
12mod(5)3=0.666666
12
m
o
d
(
5
)
3
=
0.666666
于是我们假设,如果有一个数字inv(b),能够使得 b*inv(b)%mod =1
abmod=c a b m o d = c
等式两边同时乘上 b ,会有ab=k∗mod+c a b = k ∗ m o d + c
a=k∗b∗mod+b∗c a = k ∗ b ∗ m o d + b ∗ c
a∗inv(b)=k∗inv(b)∗b∗mod+b∗inv(b)∗c(在mod意义之下,会有) a ∗ i n v ( b ) = k ∗ i n v ( b ) ∗ b ∗ m o d + b ∗ i n v ( b ) ∗ c ( 在 m o d 意 义 之 下 , 会 有 )
a∗inv(b)=k∗mod+c a ∗ i n v ( b ) = k ∗ m o d + c
于是就是 ab a b 在mod意义下 ab=a∗inv(b) a b = a ∗ i n v ( b ) 而且b与mod必须互质!
费马小定理
假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p),即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
解释:
对于1来说 inv[1]=1 i n v [ 1 ] = 1 这是显然!
对于一个数字i来说 , 有 p÷i=k.....r p ÷ i = k . . . . . r
即 p=i∗k+r p = i ∗ k + r
我们要求的是 i 的逆元,就是 i−1 i − 1 是多少,那么在mod p 意义下,等式变为
0=i∗k+r−−−−i∗k=−r 0 = i ∗ k + r − − − − i ∗ k = − r
两边同时除以 i∗r i ∗ r 得到 k∗r−1=i−1 k ∗ r − 1 = i − 1
而 k=p/ir=p k = p / i r = p 于是 r−1=inv[pmodi]=r−1 r − 1 = i n v [ p m o d i ] = r − 1
于是有(注意开 long long):
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6 + 1e5;
int MOD, n;
long long inv[N];
int main()
{
scanf("%d%d", &n, &MOD);
inv[1] = 1;
for (int i = 2; i <= n; i++)
{
inv[i] = (-(long long)(MOD / i * inv[MOD % i]) % MOD + MOD) % MOD;
}
for (int i = 1; i <= n; i++)
printf("%lld\n", inv[i]);
}