【数论】费马小定理

文章目录

  • 一、同余
    • [1. 同余的定义](#1. 同余的定义)
    • [2. 同余的性质](#2. 同余的性质)
  • 二、费马小定理
    • [1. 费马小定理的内容](#1. 费马小定理的内容)
    • [2. 费马小定理的证明(趣味版)](#2. 费马小定理的证明(趣味版))
    • [3. 求解乘法逆元](#3. 求解乘法逆元)
      • [(1) 乘法逆元](#(1) 乘法逆元)
      • [(2) 费马小定理 + 快速幂求逆元](#(2) 费马小定理 + 快速幂求逆元)
      • [(3) 应用](#(3) 应用)
  • [三、OJ 练习](#三、OJ 练习)
    • [1. 序列求和 ⭐](#1. 序列求和 ⭐)
      • [(1) 解题思路](#(1) 解题思路)
      • [(2) 代码实现](#(2) 代码实现)
    • [2. 水上舞者索尼娅 ⭐⭐](#2. 水上舞者索尼娅 ⭐⭐)
      • [(1) 解题思路](#(1) 解题思路)
      • [(2) 代码实现](#(2) 代码实现)

一、同余

1. 同余的定义

基本定义 :设 m m m 是正整数,如果 a , b a,b a,b 的差 a − b a-b a−b 被 m m m 整除即 a − b = q m a-b=qm a−b=qm,就称 a , b a, \ b a, b 关于模 m m m 同余,或简称同余。记为
a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm)

简单来说就是如果 a a a 和 b b b 除以 m m m 的余数相同,那么 a , b a, \ b a, b 就关于模 m m m 同余。


2. 同余的性质

  • 若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), c ≡ d ( m o d m ) c\equiv d\pmod m c≡d(modm),则 a ± c ≡ b ± d ( m o d m ) a\pm c\equiv b\pm d\pmod m a±c≡b±d(modm);

  • 若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), c ≡ d ( m o d m ) c\equiv d\pmod m c≡d(modm),则 a c ≡ b d ( m o d m ) ac\equiv bd\pmod m ac≡bd(modm);

  • 若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), n ∈ N n\in N n∈N,则 a n ≡ b n ( m o d m ) a^n\equiv b^n\pmod m an≡bn(modm);

  • 若 a c ≡ b c ( m o d m ) ac\equiv bc\pmod m ac≡bc(modm),且 c c c 与 m m m 互质 ,则 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm);

  • a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm) ⇔ \Leftrightarrow ⇔ a − b = k m a-b=km a−b=km。

可见同余的两侧的数字同时加、减、乘一个数字没有限制,但是同时除以一个数时有一定的限制。


二、费马小定理

1. 费马小定理的内容

费马小定理 :如果 p p p 是一个质数,对于任意整数 a a a,都有
a p ≡ a ( m o d p ) a^p\equiv a\pmod p ap≡a(modp)

此外我们还会见到另一种形式:如果 p p p 是一个质数,a a a 不能被 p p p 整除 ,那么有
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap−1≡1(modp)

这是由于同余的性质,如果两边同时除以 a a a 的话,必须要满足 a a a 和 p p p 互质,由于 p p p 是质数,所以等同余 a a a 不能被 p p p 整除。


2. 费马小定理的证明(趣味版)

证明:定理的证明千篇一律,有趣的思维万里挑一。这里我想用一种有趣的方式进行证明,过程如下:

假设现在有一串珠子形成了一个环,环上珠子的数量是 p p p( p p p是素数),现在我有 a a a 种颜色,我要给这个环上的每一个珠子进行上色。根据小学知识,每个珠子的上色情况都有 a a a 种,因此总共有 a p a^p ap 种上色方式。

假如我要用至少两种颜色进行上色,总共就有 ( a p − a ) (a^p-a) (ap−a) 种上色方式。我们现在要证明的是 a p − a ≡ 0 ( m o d p ) a^p-a\equiv0\pmod p ap−a≡0(modp),也就是证明 a p − a a^p-a ap−a 这个式子能够被 p p p 整除,即证明 a p − a = k p a^p-a=kp ap−a=kp 。

又回到这个给环上色问题上,问题就可以转化为:如果我们可以把至少用两种颜色这一情况分成 k k k 组,每组都刚好有 p p p 种上色方式

那我们就可以证明 a p − a a^p-a ap−a 是 p p p 的 k k k 倍了!

总共 a p a^p ap 种上色方式 { 一种颜色 : a 至少两种颜色: a p − a { p 种 p 种 ⋮ p 种 \begin{cases}一种颜色:a\\\\\\至少两种颜色:a^p-a\begin{cases}p种\\p种\\\vdots \\p种\end{cases}\end{cases} ⎩ ⎨ ⎧一种颜色:a至少两种颜色:ap−a⎩ ⎨ ⎧p种p种⋮p种

为了方便,我们把可以通过旋转得到一模一样的两种上色方式称为"同款",如下:

现在我们把 "同款" 的上色方式放在一组里面,由上图不难发现,当我们关注红色的珠子时,当它绕着旋转一圈后,它把每个位置都经过了一边。也就是说,当我们把这个环进行旋转时,它所出现的不同上色方式恰好就是这个环上的珠子的个数,也就是 p p p。也就说明了同款里不同的上色方式就是 p p p 种 。即我们确实可以把 ( a p − a ) (a^p-a) (ap−a) 种上色方式分成若干个 p p p 种上色方式。

BUT!这好像有个bug啊...万一在旋转过程中出现了两个一模一样的上色咋办?请看VCR:

别急!这个时候, p p p 是素数的条件就很关键了。既然是素数,就说明它只能被1和它本身整除,所以说实际上是不可能出现像上面那样有几个完整周期排列的。只要不是由几个完整的周期依次排列成环的,就不会出现像上面那样转了不到一圈就完全重合的现象。

综上,我们就说明了在 ( a p − a ) (a^p-a) (ap−a) 种上色方式中,我们可以把它分成 k k k 组,每组都恰好有 p p p 种上色方式。即
a p − a = k p ⇕ a p − a ≡ 0 ( m o d p ) ⇕ a p ≡ a ( m o d p ) a^p-a=kp\\ \Updownarrow\\ a^p-a\equiv0\pmod p\\ \Updownarrow\\ a^p\equiv a\pmod p\\ ap−a=kp⇕ap−a≡0(modp)⇕ap≡a(modp)

费马小定理,证毕!


3. 求解乘法逆元

(1) 乘法逆元

  • 定义

对于正整数 a a a 和 p p p,若有
a x ≡ 1 ( m o d p ) ax\equiv1\pmod p ax≡1(modp)

那么把这个同余方程中的 x x x 的解叫做 a a a 模 p p p 的乘法逆元,简称逆元 ,记作 a − 1 a^{-1} a−1。

例如 8 x ≡ 1 ( m o d 5 ) 8x \equiv 1 \pmod 5 8x≡1(mod5) 中, x = 2 , 7 , ⋯ x = 2, 7, \cdots x=2,7,⋯ 。那么解出来的这些 x x x 就是 8 8 8 模 5 5 5 的乘法逆元。

  • a a a 存在乘法逆元的充要条件是 a a a 和 p p p 互质

要想说明 a x ≡ 1 ( m o d p ) ax\equiv1\pmod p ax≡1(modp) 存在这么一个 x x x ,可以进行一个简单的变形。因为是对 a x ax ax 取模,相当于是把 a x ax ax 减掉了 k k k 个 p p p 最终剩下了 1 1 1,相当于可以写成 a x − k p = 1 ax-kp=1 ax−kp=1, k k k 是一个整数,这里等价于 a x + p y = 1 ax+py=1 ax+py=1。而根据裴蜀定理,方程 a x + p y = 1 ax+py=1 ax+py=1 有解的充要条件是 gcd ⁡ ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1。


(2) 费马小定理 + 快速幂求逆元

a a a 存在乘法逆元的充要条件是 a a a 与 p p p 互质,而**当模数 p p p 是质数时,**满足费马小定理的条件,由费马小定理:
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap−1≡1(modp)

提出一个 a a a 出来,可以得到
a ⋅ a p − 2 ≡ 1 ( m o d p ) a\cdot a^{p-2}\equiv1\pmod p a⋅ap−2≡1(modp)

而这个式子就恰好满足了我们希望的形式,因此可以看出, a a a 的逆元就是 a p − 2 a^{p-2} ap−2,而 a p − 2 a^{p - 2} ap−2 则可用快速幂进行求解。

注:当 p p p 不是质数的时候,需要使用其他方法进行求解,例如扩展欧几里得算法。

cpp 复制代码
#include <iostream>

using namespace std;

typedef long long LL;

// 必须要保证 a, p 互质,且 p 为质数
LL qpow(LL a, LL b, LL p)
{
	LL ret = 1;
	while(b)
	{
		if(b & 1) ret = ret * a % p;
		b >>= 1;
		a = a * a % p;
	}
	return ret;
}

int main()
{
	LL x, p; cin >> n >> p;
	cout << qpow(x, p - 2, p) << endl;  // 打印 x 在模 p 意义下的乘法逆元
	return 0;
}

时间复杂度与快速幂一致,为 O ( log ⁡ n ) O(\operatorname{log}n) O(logn)。


(3) 应用

当我们求解 (20 \\div 3)\\bmod 5 这种带模数的运算时,由于 20 ÷ 3 20\div 3 20÷3 是一个小数,我们没法直接求解,这个时候我们可以用逆元把除法变成乘法 。这个问题等价于 ( 20 × 3 − 1 )   m o d   5 (20 \times 3^{-1}) \bmod 5 (20×3−1)mod5,注意这里的 3 − 1 3^{-1} 3−1 是乘法逆元,不是倒数。 3 3 3 在模 5 5 5 下的逆元是 2 2 2,因此原式等价于 ( 20 × 2 )   m o d   5 = 0 (20 \times 2)\bmod 5 = 0 (20×2)mod5=0。


三、OJ 练习

1. 序列求和 ⭐

【题目链接】

序列求和


(1) 解题思路

中学时我们学过, 1 2 + 2 2 + ⋯ + n 2 = n ( n + 1 ) ( 2 n + 1 ) 6 1^2 + 2^2 + \cdots + n^2 = \frac{n(n + 1)(2n + 1)}{6} 12+22+⋯+n2=6n(n+1)(2n+1),所以这道题本质上是在求右边这个分式的值然后对 ( 1 0 9 + 7 ) (10^9 + 7) (109+7) 取模的结果。如果我们直接求解,那么我们需要计算 n ( n + 1 ) ( 2 n + 1 ) n(n + 1)(2n + 1) n(n+1)(2n+1),由于 n n n 非常大,所以我们要边乘边取模,但是模完之后的数可能不是 6 6 6 的倍数了,因此我们不能用除以 6 6 6 来运算最终结果,所以需要用乘法逆元把除法转换为乘法。原式等价于求解
n ( n + 1 ) ( 2 n + 1 ) × 6 − 1   m o d   ( 1 0 9 + 7 ) n(n + 1)(2n + 1) \times 6^{-1} \bmod (10^9 + 7) n(n+1)(2n+1)×6−1mod(109+7)

求解 6 6 6 在模 ( 1 0 9 + 7 ) (10^9 + 7) (109+7) 下的逆元采用费马小定理 + 快速幂即可。


(2) 代码实现

cpp 复制代码
#include<iostream>

using namespace std;

typedef long long LL;

const int p = 1e9 + 7;

LL n;

// 快速幂
LL qpow(LL a, LL n, LL p)
{
    LL ret = 1;
    while(n)
    {
        if(n & 1) ret = ret * a % p;
        n >>= 1;
        a = a * a % p;
    }
    return ret;
}

// 费马小定理求解 x 模 p 下的逆元 
LL rev(LL x, LL p)
{
    return qpow(x, p - 2, p);
}

int main()
{
    while(cin >> n)
    {
        // 由于 n 可能非常大,所以每一步乘法运算后都需要取一次模防止溢出
        LL ans = ((((n % p) * ((n + 1) % p) % p) % p) * ((2 * n + 1) %p)) % p * (rev(6, p)) % p;
        cout << ans << endl;
    }
    
    return 0;
}

2. 水上舞者索尼娅 ⭐⭐

【题目链接】

P11465 水上舞者索尼娅 - 洛谷

【题目背景】

2024 年 12 月 18 日,《炉石传说》31.2.2 补丁上线,水上舞者索尼娅遭到削弱。现在,在这道题目中,你打开时空之门,回到了水索被削弱前的时候。

【题目描述】

你的场上有 k k k 个【水上舞者索尼娅】,你的手牌中有 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 n n n 根)】,你有无尽的法力值,你的手牌数量没有上限。

在你的场上有 k k k 个【水上舞者索尼娅】的情况下:

当你使用 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 x x x 根)】时,你将得到:

  • k k k 张法力值消耗为 0 0 0 的【一串香蕉(还剩 x x x 根)】。
  • 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 x − 1 x-1 x−1 根)】。(若 x = 1 x=1 x=1,则不会得到)

当你使用 1 1 1 张法力值消耗为 0 0 0 的【一串香蕉(还剩 x x x 根)】时,你将得到:

  • 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 x − 1 x-1 x−1 根)】。(若 x = 1 x=1 x=1,则不会得到)

你一共可以使用多少次【一串香蕉】?

由于答案可能很大,你只需求出答案对 1 0 9 + 7 10^9+7 109+7 取模的余数。

【输入格式】

本题有多组数据

第一行一个正整数 T T T,表示数据组数。

对于每组数据:

一行两个正整数 n , k n,k n,k。

【输出格式】

对于每组数据:

输出一行一个整数,表示答案对 1 0 9 + 7 10^9+7 109+7 取模的余数。

【示例一】

输入

复制代码
3
2 2
3 1
12 34

输出

复制代码
12
14
178629506

【说明/提示】

1 ≤ T ≤ 1 0 5 1\le T\le10^5 1≤T≤105, 1 ≤ n , k ≤ 1 0 9 1\le n,k\le10^9 1≤n,k≤109。

样例解释:

对于第 1 1 1 组数据:

场上有 2 2 2 个【水上舞者索尼娅】,初始手牌中有 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 2 2 2 根)】。

使用: 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 2 2 2 根)】。

手牌:

  • 2 2 2 张法力值消耗为 0 0 0 的【一串香蕉(还剩 2 2 2 根)】。
  • 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 1 1 1 根)】。

使用: 2 2 2 张法力值消耗为 0 0 0 的【一串香蕉(还剩 2 2 2 根)】。

手牌:

  • 3 3 3 张法力值消耗为 1 1 1 的【一串香蕉(还剩 1 1 1 根)】。

使用: 3 3 3 张法力值消耗为 1 1 1 的【一串香蕉(还剩 1 1 1 根)】。

手牌:

  • 6 6 6 张法力值消耗为 0 0 0 的【一串香蕉(还剩 1 1 1 根)】。

使用: 6 6 6 张法力值消耗为 0 0 0 的【一串香蕉(还剩 1 1 1 根)】。

手牌:

共使用 1 + 2 + 3 + 6 = 12 1+2+3+6=12 1+2+3+6=12 张【一串香蕉】。


(1) 解题思路

我们把法力值消耗为 1 1 1 的【一串香蕉(还剩 n n n 根)】记为 ( 1 , n ) (1, n) (1,n),以及场上有 k k k 个【水上舞者索尼娅】。此时我们使用一次一串香蕉,根据题意,你会得到 k k k 个 ( 0 , n ) (0, n) (0,n) 和 一个 ( 1 , n − 1 ) (1, n - 1) (1,n−1),此时再把 k k k 个 ( 0 , n ) (0, n) (0,n) 全部使用,那么就会得到 k k k 个 ( 1 , n − 1 ) (1, n - 1) (1,n−1),这个时候,你会得到 k + 1 k + 1 k+1 个 ( 1 , n − 1 ) (1, n - 1) (1,n−1)。

复制代码
(1, n) --使用k+1次--> (k + 1)个(1, n - 1)

那么以此类推,我们可以得出以下规律

复制代码
(1, n) --使用k+1次--> (k + 1)个(1, n - 1) --使用(k+1)^2次--> (k + 1)^2个(1, n - 2)  ... 

所以我们最终就可以使用 ( k + 1 ) + ( k + 1 ) 2 + ⋯ + ( k + 1 ) n = ∑ i = 1 n ( k + 1 ) i (k + 1) + (k + 1)^2 + \cdots + (k + 1)^n = \sum\limits_{i = 1}^{n}(k + 1)^i (k+1)+(k+1)2+⋯+(k+1)n=i=1∑n(k+1)i 张【一根香蕉】。

根据等比数列求和公式再进行整理,最终答案就是 ( k + 1 ) n + 1 − k − 1 k   m o d   ( 1 0 9 + 7 ) \frac{(k+1)^{n + 1} - k - 1}{k} \bmod (10^9 + 7) k(k+1)n+1−k−1mod(109+7)。由于在模运算中出现了除法,所以等价于求解 [ ( k + 1 ) n + 1 − k − 1 ] × k − 1   m o d   ( 1 0 9 + 7 ) [(k+1)^{n + 1} - k - 1]\times k^{-1} \bmod (10^9 + 7) [(k+1)n+1−k−1]×k−1mod(109+7)。


(2) 代码实现

cpp 复制代码
#include<iostream>

using namespace std;

typedef long long LL;

const int p = 1e9 + 7;
LL n, k;

// 快速幂
LL qpow(LL a, LL n, LL p)
{
    LL ret = 1;
    while(n)
    {
        if(n & 1) ret = ret * a % p;
        n >>= 1;
        a = a * a % p;
    }
    return ret;
}

// 求解 x 模 p 下的逆元 
LL inv(LL x, LL p)
{
    return qpow(x, p - 2, p);
}

int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n >> k;
        LL ans = (qpow(k + 1, n + 1, p) - k - 1) * inv(k, p);
        // 注意把答案补成最小非负整数
        cout << ((ans % p) + p) % p << endl;
    }
}

相关推荐
Jamesvalley2 小时前
【uv】 uv安装mysql
uv·1024程序员节
冷崖2 小时前
C++父类与子类进行交互
1024程序员节
杯莫停丶2 小时前
设计模式之:命令模式
设计模式·命令模式·1024程序员节·活动勋章
野犬寒鸦2 小时前
从零起步学习MySQL || 第十章:深入了解B+树及B+树的性能优势(结合底层数据结构与数据库设计深度解析)
java·数据库·后端·mysql·1024程序员节
Never_Satisfied2 小时前
在JavaScript / HTML中,无法通过开发者工具查看DOM元素中input里输入的密码
1024程序员节
程序猿阿伟3 小时前
《打破数据孤岛:3D手游角色表情骨骼协同的实践指南》
1024程序员节
kura_tsuki3 小时前
[Docker 集群] 私有仓库 + compose
1024程序员节
时间的情敌3 小时前
前端实现大文件上传全流程详解
1024程序员节
冬天的雪20083 小时前
SpringBoot知识点总结
1024程序员节