基础数论 I:同余
update:
1. 数学概念
1.1 模意义下的逆元
即
该同余方程有解当且仅当
对于
在
1.2 费马小定理与欧拉定理
1.2.1 费马小定理
无论学什么应该都是最先知道的定理 : )。
费马小定理: 任意
引理:对于一个模
- 证明:反证,假设存在
使得 ,因为 即存在 的逆元 ,既得 ,矛盾。
首先有一组模
1.2.2 欧拉定理
欧拉定理: 任意
证明类似。
引理:对于一个模
- 首先这
个数彼此不同余,可由 中证出,下证 与 互质,因为 ,且 则 也互质。
一个缩系
当
1.3 阶与原根
非常厉害的东西。
1.3.1 阶
阶的定义:使得
性质 1:
性质 2: 任意
- 证明:考虑反证,我们假设存在两个数
,使得 ,则有 ,显然 ,这与阶的最小性矛盾。
性质 3:
- 注意到
,因此 ,得到 。
利用
1.3.2 原根
定义:对于
原根判定定理:对于
- 证明:因为
的所有质因子取遍了 的真因数,所以若存在 ,使得 ,则必然存在 的质因子 使得 ,这说明 ,矛盾。
原根存在定理:一个数
证明详见初等数论(其实没必要)
原根个数: 一个数
- 证明:由原根的性质知:
则若
中国数学家王元证明了最小原根的级别为
所以我们可以直接枚举,复杂度
进而求出所有原根。
2. 扩展欧几里得算法
也叫
即求方程
我们考虑欧几里得法的最后一步,即
假设已知
可得一解
我们令一组特解为
通解即为
int t;
ll a,b,c,gc,x,y;
void exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0){return x = 1,y = 0,void();}
exgcd(b,a%b,y,x);
y -= a / b * x;
}
int main(){
t = read();
while(t--){
a = read(),b = read(),c = read();
gc = __gcd(a,b);
ll dx = b/gc,dy = a/gc;
if(c % gc != 0){//无解
printf("-1\n");
continue;
}
exgcd(a,b,x,y);
x = x * c / gc,y = y * c / gc;//特解
ll l = ceil(double(-x+1)/dx),r = floor((double)(y-1)/dy);// > 0 的区间
if(l > r)printf("%lld %lld\n",x + dx*l,y - dy*r);
else printf("%lld %lld %lld %lld %lld\n",r-l+1,x + dx*l,y - dy*r,x + dx*r,y - dy*l);
}
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
线性同余方程:
可以转化为求解不定方程
求乘法逆元当
3. 扩展中国剩余定理(ExCRT)
不讲中国剩余定理。
求解
首先第一个同余方程的一个特解显然是
我们假设前
当有一次扩欧无解时整个同余方程无解。
会爆
,需要龟速乘。
int n;
ll a[N],b[N];
void exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0){return x = 1,y = 0,void();}
exgcd(b,a%b,y,x);
y -= a / b * x;
}
ll mul(ll x,ll y,ll p){return (x*y-(ll)(x/(long double)p*y+1e-3)*p+p) % p;}
ll lcm(ll x,ll y){return x / __gcd(x,y) * y;}
ll EXCRT(){
ll x = a[1],M = b[1],k = 0,y = 0;
F(i,2,n){
ll gc = __gcd(M,b[i]);
if((a[i]-x) % gc != 0)return -1;//无解
exgcd(M,b[i],k,y);
ll mod = b[i] / gc;
k = (mul(k,(a[i] - x) / gc,mod) + mod) % mod;
x = x + k * M;
M = lcm(M,b[i]);
}//EXCRT
return x;
}
int main(){
n = read();
F(i,1,n)b[i] = read(),a[i] = read();
printf("%lld\n",EXCRT());
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
4. 离散对数
求解同余方程
该问题还未有
4.1 BSGS
当
我们令
则方程变为
我们枚举所有的
然后我们再枚举所有的
复杂度
ll a,b,p;
ll ksm(ll x,ll y,ll mod){
ll ans = 1;
while(y){
if(y & 1)ans = (ans * x) % mod;
x = (x * x) % mod,y >>= 1;
}return ans;
}
ll BSGS(ll a,ll b,ll p){
map<int,int>mp;mp.clear();
b %= p;ll t = sqrt(p)+1;
for(int j = 0;j < t;j++,b = b * a % p)mp[b] = j;
ll aa = ksm(a,t,p);a = 1;
for(int i = 0;i <= t;i++,a = a * aa % p)
if(mp.find(a) != mp.end() && i * t - mp[a] >= 0)return i * t - mp[a];
return -1;
}
int main(){
p = read(),a = read(),b = read();
ll ans = BSGS(a,b,p);
if(ans != -1)printf("%lld\n",ans);
else printf("no solution\n");
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
4.2 ExBSGS
考虑一般情况,我们尽量把问题转化为
可以发现
最后式子为
复杂度
ll a,b,p;
ll ksm(ll x,ll y,ll mod){
ll ans = 1;
while(y){
if(y & 1)ans = (ans * x) % mod;
x = (x * x) % mod,y >>= 1;
}return ans;
}
void exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0)return x = 1,y = 0,void();
exgcd(b,a%b,y,x);
y -= a / b * x;
}
ll inv(ll x,ll p){return exgcd(x,p,x,*new ll),(x % p + p) % p;}
ll BSGS(ll a,ll b,ll p){
map<int,int>mp;mp.clear();
b %= p;ll t = sqrt(p)+1;//x = i * t - j
for(int j = 0;j < t;j++,b = b * a % p)mp[b] = j;
ll aa = ksm(a,t,p);a = 1;
for(int i = 0;i <= t;i++,a = a * aa % p)
if(mp.find(a) != mp.end() && i * t - mp[a] >= 0)return i * t - mp[a];
return -1;
}
ll EXBSGS(ll a,ll b,ll p){
ll d = __gcd(a,p),k = 0;
b %= p,a %= p;
if(b == 1 || p == 1)return k;
while(d > 1){
if(b == 1)return k;
if(b % d)return -1;
p /= d;b = b / d * inv(a / d,p) % p;
d = __gcd(a % p,p),k++;
}
ll ans = BSGS(a,b,p);
return ans == -1 ? -1 : ans + k;
}
int main(){
while(1){
a = read(),p = read(),b = read();
if(!p)break;
ll ans = EXBSGS(a,b,p);
if(ans != -1)printf("%lld\n",ans);
else printf("No Solution\n");
}
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
4.3 习题
第一问快速幂,第二问扩欧,第三问
首先我们求一下通项。
解得
则
我们要求的即是:
这就很板了,
5. 线性方程组
5.1 高斯消元法
求解
主要就是矩阵变换。
只需要知道将系数矩阵中的任意一行乘上一个常数加到另一行整个方程式不变的。
高斯消元就是把系数矩阵通过矩阵变换转化为对角矩阵进行求解。
高斯法:
高斯-约旦法:
int n;
db a[N][N],b[N];
void Gauss(){
for(int i = 1;i <= n;i++){
int k = i;
for(int j = i+1;j <= n;j++)
if(fabs(a[j][i]) > fabs(a[k][i]))k = j;
for(int j = 1;j <= n;j++)swap(a[i][j],a[k][j]);swap(b[i],b[k]);
if(fabs(a[i][i]) < 1e-8)return printf("No Solution\n"),void();
db c = a[i][i];
for(int j = 1;j <= n;j++)a[i][j] /= c;b[i] /= c;
for(int j = 1;j <= n;j++){
if(i == j)continue;
db c = a[j][i];
for(int k = i;k <= n;k++)a[j][k] -= c * a[i][k];
b[j] -= c * b[i];
}
}
for(int i = 1;i <= n;i++)printf("%.2lf\n",b[i]);
}
int main(){
n = read();
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++)a[i][j] = read();
b[i] = read();
}
Gauss() ;
return cerr << "Time : " << clock() << " ms" << endl, 0;
}
高斯消元只是一个解方程的方法。
5.2 习题
我们设每个每个点到球心的距离为
这是一个
我们把相邻两项相减即可消掉二次项与常数项。
是
6. 卢卡斯定理
7. 二次剩余
难
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战