[数学基础] 6 初等数论四大定理
初等数论四大定理
1. 威尔逊定理
(1) 结论
当且仅当为素数时,。
(2) 证明
充分性:若不为素数,则。
-
当时,显然;
-
当时
- 为完全平方数,则,使得,由于,故,。
- 不是完全平方数,则,使得,则。
必要性:若为素数,则一定有。
时,结论显然成立。
若为奇素数,取集合,构成模乘法的简化剩余系,即,使得。
证明:因为构成模乘法的取值的集合了(除了0),所以这个结论肯定成立。
那么,这个数是不是两两配对的呢?首先,由同余的运算法则,可以得到一定没有的情况,那么它一定是两个一对,或者单独一个的情况。考虑,解得,或者,那么其余则两两配对。因此,得证。
(5)例题
可以看下它的考察方式:UVA1434 YAPTCHA,主要就是欧拉筛+威尔逊定理+前缀和
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n, cnt;
const int N = 3e6 + 20;
bool st[N];
int p[N], pre[N];
void Euler(int n){
for (int i=2;i<=n;++i){
if (!st[i]) p[++cnt] = i;
for (int j=1;p[j]<=n/i;++j){
st[i * p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
void init(){
Euler(N - 1);
for (int i=1;i<N;++i){
int x = 0;
if (i > 7 && (i - 7) % 3 == 0 && !st[i]){
x = 1;
}
pre[i] = pre[i - 1] + x;
}
}
int main(void){
init();
int t;
scanf("%d", &t);
while (t--){
scanf("%d", &n);
printf("%d\n", pre[n * 3 + 7]);
}
return 0;
}
2. 欧拉定理
(1) 结论
若是一个大于1的整数,且满足条件,则我们有
(2) 证明
引理1: 设是一个大于1的整数,是一个整数且满足,如果是模的一个简化剩余系,则也是模的一个简化剩余系。
证明:由于从中取出任何一个正整数,都有。又,可以得到在中任意取出一个整数,都有。那么,现在利用反证法,假设在中存在两个整数,,使得:成立。
又,故,由于是简化剩余系的元素,因此不可能模同余,所以同余等式不可能成立,得证。
下面正式的证明欧拉定理。
考虑模的最小正缩系,即,是不大于且和互质的全体正整数,令是一个整数,满足条件:
令(其中)是一个整数,满足条件:。
则我们有
由于是模的一个简化剩余系,并且,因此也是一个简化剩余系,并且和至少在次序上可能有不同,故得到
因此
即。
(3) 推论
欧拉降幂:
证明:设,则。
,得证。
3. 费马小定理
(1) 结论
事实上,费马小定理就是欧拉定理的一种特殊情况。
如果为质数,,则我们有
(1) 证明
由于为质数,因此,取欧拉定理中,即得到费马小定理。
需要注意的是,,使得,这里不是素数,使得,即费马小定理的逆定理不成立。比如,,,而,不是素数。
(2) 推论
明显的,当为质数,且时,,由此就可以求得在模意义下的乘法逆元。
欧拉定理和费马定理很难单独考察,大部分都是作为解题的一个步骤出现。
4. 中国剩余定理
1) 中国剩余定理(crt)
定理:
有一元线性同余方程组如下。
假设整数两两互质,则对任意的整数,方程组有解,并且通解可以用如下方式构造得到:
设,,设,为在模意义下的的模乘法逆元。
方程组的通解形式为。
在模的意义下,方程组只有一个解:
namo 首先解的每一项,都是可以求出来的,也可以通过扩展欧几里得算法求出。然后再来小小的证明下!
证明:
- 首先证明是方程组的一个解。
对于解的每一项,可以看出,
,
因此,满足
故是方程组的一个解。
- 然后证明在模的意义下,方程组只有一个解。
假设都是方程组的解,那么
而两两互质,这说明,所以方程组的任何两个解之间必然相差的整数倍,所以方程组所有的解的集合就是
所以在模的意义下,方程组只有一个解。
- 代码:
int n;
const int N = 10 + 5;
int A[N], B[N];
ll mod(ll a, ll b){
return (a % b + b) % b;
}
ll exgcd(ll a, ll b, ll &x, ll &y){
if (!b){
x = 1, y = 0; return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main(void){
scanf("%d", &n);
ll M = 1, res = 0, ti, y;
for (int i=1;i<=n;++i){
scanf("%d%d", &A[i], &B[i]);
M = M * A[i];
}
for (int i=1;i<=n;++i){
ll Mi = M / A[i];
exgcd(Mi, A[i], ti, y);
res = mod(res + Mi * ti * B[i], M);
}
printf("%lld\n", res);
return 0;
}
2) 扩展中国剩余定理(excrt)
(rxz的题解写的太好了,受益良多QAQ,他讲的东西真的都是清晰细致又简单 但是因为他用python所以不推mod 岂可修)
https://www.luogu.com.cn/blog/blue/kuo-zhan-zhong-guo-sheng-yu-ding-li
这里自己再重新推导一遍。
修改条件,使得不再两两互质,此时应该如何求方程组的解?
互质条件不满足时,不存在。(回忆逆元的定义,只有当时才存在),那么不能利用公式时,只能尝试去不断的合并方程,直到个方程仅剩下一个,再使用扩展欧几里得算法求解唯一的同余方程。那么,如何将两个方程等价的变换为一个方程呢?考虑如下情况:
此方程组等价于
移项后,得。
令,方程就变成了我们最熟悉的不定方程。那么,记,求是否有,满足。求解的步骤即是:先用裴蜀定理判断是否有解,再用扩展欧几里得算法求解(系)。则,这就是方程的一组特解。为了避免数据溢出,可以让(具体证明见裴蜀定理),那么,,就求出来了两个方程的特解。设这个特解为,那么它的通解就是,那么,就可以合并两个同余方程啦!
这样不断合并,直到只剩下一个方程,解出答案即可。注意,中间非常容易数据溢出!即使是long long!所以最好直接用一个mul函数,一边做乘法一边做模运算。
横线中的这段是rxz的证明,嘛嘛,就相当于方程两边,结果是一样的。
那么,记,,易得。
方程可被写为这样的形式:
由于当时,方程才有解,因此右边的式子为整数。接着按照exgcd的流程走,我们求这个方程的解
,则
将结果代入方程,得。
这个就是方程的一个特解。
那么,进一步的,如何求出整个解系呢?
定理:若有特解,令,则 的通解为。
该定理的证明和CRT的证明完全相同。好累,我不想重抄一遍所以就这样吧
- 代码:
int n;
const int N = 1e5 + 10;
ll a1, m1, a[N], m[N], res;
ll mul(ll a, ll b, ll M){
ll res = 0;
while (b > 0){
if (b & 1) res = (res + a) % M;
a = (a + a) % M;
b >>= 1;
}
return res;
}
ll merge(ll a2, ll m2){
ll x, y, M, c = mod(a2 - a1, m2);
ll d = exgcd(m1, m2, x, y);
if (c % d){
return -1;
}
M = (m1 / d * m2);
ll k1 = mod(mul(x, c / d, m2 / d), m2 / d);
ll r = mod(k1 * m1 + a1, M);
a1 = r, m1 = M;
return r;
}
int main(void){
scanf("%d", &n);
scanf("%lld%lld", &m1, &a1);
if (n == 1){
printf("%lld\n", mod(a1, m1));
return 0;
}
for (int i=1;i<n;++i){
scanf("%lld%lld", &m[i], &a[i]);
}
for (int i=1;i<n;++i){
res = merge(a[i], m[i]);
if (res < 0) break;
}
printf("%lld\n", res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通