全文 1.6 万余字。推荐学时:4学时 阅读全文可能需要20分钟甚至更长时间。
为什么我们要研究质数?因为几乎所有正整数都可以由若干个质数相乘来生成,可以认为质数是正整数的基本成分。这就是为什么它叫“质数”或者“素数”。
π ( x ) ≈ x ln x \pi\left(x\right)\approx \frac{x}{\ln{x}} π(x)≈lnxx
对于正数 x , x, x, π ( x ) \pi\left(x\right) π(x) 为素数计数函数(Prime-counting function),即不超过 x x x 的素数的数目。当 x x x 较小时( x < 1 0 5 x<10^{5} x<105),这个估计的误差较大。上述估计值总是比实际值小,所以在设定素数表的大小时,我们通常会给估计值乘上 1.2 1.2 1.2 进行修正,防止空间不足。
素数计数函数有更为准确的估计,但上面的是最好算的。
Fibonacci sequence。仅在这一节中,我们约定 F i F_{i} Fi 表示第 i i i 个斐波那契数,则 F 1 = F 2 = 1. F_{1} = F_{2} = 1. F1=F2=1.
( 0 , ) 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , ⋯ (0,)\;1,\,1,\,2,\,3,\,5,\,8,\,13,\,21,\,34,\cdots (0,)1,1,2,3,5,8,13,21,34,⋯
由递推公式及初值,可以得出,当 n > 0 n>0 n>0 时
F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] = φ n 5 − ( 1 − φ ) n 5 . F_{n} = \frac{1}{\sqrt{5}} \left[ \left( \frac{1+\sqrt{5}}{2} \right)^{n} - \left( \frac{1-\sqrt{5}}{2} \right) ^{n}\right]= \frac{\varphi^{n}}{\sqrt{5}} - \frac{\left(1-\varphi\right)^{n}}{\sqrt{5}} . Fn=51[(21+5)n−(21−5)n]=5φn−5(1−φ)n.
F n = O ( φ n ) , log F n = O ( n ) F_n=\Omicron(\varphi^{n}),\;\log{F_n}=\Omicron(n) Fn=O(φn),logFn=O(n)
随着 n n n 的增大, F n + 1 / F n F_{n+1}/F_{n} Fn+1/Fn 越来越接近于黄金比例 ( 5 + 1 ) / 2 (\sqrt{5}+1)/2 (5+1)/2。
三个连续的斐波那契数两两互质,因为
gcd ( F i + 2 , F i + 1 ) = gcd ( F i + 2 − F i + 1 , F i + 1 ) = gcd ( F i , F i + 1 ) \gcd\left(F_{i+2},F_{i+1}\right)=\gcd\left(F_{i+2}-F_{i+1},F_{i+1}\right)=\gcd\left(F_{i},F_{i+1}\right) gcd(Fi+2,Fi+1)=gcd(Fi+2−Fi+1,Fi+1)=gcd(Fi,Fi+1)
且
gcd ( F 2 , F 1 ) = 1. ■ \gcd\left(F_{2},F_{1}\right)=1.\;\; \texttt{■} gcd(F2,F1)=1.■
前 n n n 个斐波那契数的和为 F n + 2 − 1 F_{n+2}-1 Fn+2−1
n ≥ 3 n\ge 3 n≥3 时, F n F_{n} Fn 能整除 F m F_{m} Fm 的充要条件是 n n n 能整除 m m m
gcd ( F m , F n ) = F gcd ( m , n ) \gcd(F_{m},F_{n})=F_{\gcd(m,n)} gcd(Fm,Fn)=Fgcd(m,n)
F n − 1 F n + 1 − F n 2 = ( − 1 ) n F_{n-1}F_{n+1}-F_{n}^{2}=(-1)^{n} Fn−1Fn+1−Fn2=(−1)n
注意到递推式 f ( n ) = a f ( n − 1 ) + b f ( n − 2 ) f(n) = af(n-1)+bf(n-2) f(n)=af(n−1)+bf(n−2) 可以写成矩阵的形式
[ a b 1 0 ] [ f ( n − 1 ) f ( n − 2 ) ] = [ f ( n ) f ( n − 1 ) ] \left[ \begin{array}{cc} a& b \\ 1& 0 \\ \end{array} \right ] \left[\begin{array}{c} f(n-1) \\ f(n-2) \\ \end{array} \right]=\left[\begin{array}{c} f(n) \\ f(n-1)\end{array} \right] [a1b0][f(n−1)f(n−2)]=[f(n)f(n−1)]
对于斐波那契数列,有
[ 1 1 1 0 ] [ F n − 1 F n − 2 ] = [ F n F n − 1 ] \\ \left[ \begin{array}{cc} 1& 1 \\ 1& 0 \\ \end{array} \right ] \left[\begin{array}{c} F_{n-1} \\ F_{n-2} \\ \end{array} \right]=\left[\begin{array}{c} F_{n} \\ F_{n-1}\end{array} \right] [1110][Fn−1Fn−2]=[FnFn−1]
[ 1 1 1 0 ] n − 2 [ F 2 F 1 ] = [ F n F n − 1 ] \\ \left[ \begin{array}{cc} 1& 1 \\ 1& 0 \\ \end{array} \right ]^{n-2} \left[\begin{array}{c} F_{2} \\ F_{1} \\ \end{array} \right]=\left[\begin{array}{c} F_{n} \\ F_{n-1}\end{array} \right] [1110]n−2[F2F1]=[FnFn−1]
只要求出左边的系数矩阵就能求出 F n . F_{n}. Fn. 下面的代码演示如何用矩阵快速幂计算第 50 50 50 个斐波那契数( 125 , 8626 , 9025 125,8626,9025 125,8626,9025)。测试了一下,好像没有多快。
using ull = unsigned long long;
template <ull size>
struct Matrix
{
ull a[size][size];
Matrix()
{
memset(a, 0, sizeof(a));
for (ull i = 0; i < size; ++i)
a[i][i] = 1;
}
Matrix(const Matrix<size> &t) { memcpy(a, t.a, sizeof(a)); }
Matrix<size> &operator=(const Matrix<size> &t)
{
memcpy(a, t.a, sizeof(a));
return *this;
}
Matrix<size> operator*(const Matrix<size> &t)
{
Matrix<size> ret;
for (ull i = 0; i < size; ++i)
{
for (ull j = 0; j < size; ++j)
{
ret.a[i][j] = 0;
for (ull k = 0; k < size; ++k)
ret.a[i][j] += a[i][k] * t.a[k][j];
}
}
return ret;
}
Matrix<size> pow(ull n)
{
if (n < 2) return *this;
Matrix<size> ret, b(*this);
while (n)
{
if (n & 1) ret = ret * b;
b = b * b;
n >>= 1;
}
return ret;
}
};
int main()
{
Matrix<2> m;
ull a[2][2] = {{1, 1}, {1, 0}};
memcpy(m.a, a, sizeof(a));
Matrix<2> t(m);
t = t.pow(50 - 2);
printf("%llu\n", t.a[0][0] + t.a[0][1]);
return 0;
}
是 欧几里得(Euclid) 先提出的,所以又叫欧几里得算法(Euclidean algorithm)。可以求最大公约数。
递归或者循环
int gcd(int a, int b)
{ return b ? gcd(b, a % b) : a; }
int gcd(int a, int b)
{
while(b)
{
int t = a % b;
a = b;
b = t;
}
return a;
}
更相减损术的实现与此类似,但效率极低。
O ( log ( min { a , b } ) ) \Omicron\left(\log{\left(\min{\{a,b}\}\right)}\right) O(log(min{a,b}))
证明如下
以辗转相除法的循环实现为例,设用辗转相除法求 a , b a,b a,b 的最大公约数需要 N N N 次循环,其中 a > b . a > b. a>b. 约定 F i F_{i} Fi 表示第 i i i 个斐波那契数、 φ \varphi φ 表示黄金比例,则 F 1 = F 2 = 1. F_{1} = F_{2} = 1. F1=F2=1.
显然,当 N N N 的值确定时, a , b a,b a,b 的最小值应该分别为第 F N + 2 F_{N+2} FN+2 和 F N + 1 . F_{N+1}. FN+1. 想一想为什么。 即,若该算法需要 N N N 次循环,则有 b ≥ F N + 1 . b \ge F_{N+1}. b≥FN+1.
易得
F N + 1 ≥ φ N − 1 F_{N + 1}\ge \varphi^{N-1} FN+1≥φN−1
所以 b ≥ φ N − 1 b\ge \varphi^{N-1} b≥φN−1 ,即
N ≤ log b log φ + 1 N\le\frac{ \log{b}}{\log{\varphi}} + 1 N≤logφlogb+1
故
O ( N ) = O ( log ( min { a , b } ) ) . ■ \Omicron(N) = \Omicron(\log{\left(\min{\{a,b}\}\right)}).\;\; \texttt{■} O(N)=O(log(min{a,b})).■
证明辗转相除法的时间复杂度是斐波那契数列的第一个实际应用。
求最小公倍数。
int lcm(int a, int b)
{ return a / gcd(a, b) * b; }
已知两正整数 a > b , a>b, a>b, 设 gcd ( a , b ) \gcd\left(a,b\right) gcd(a,b) 为 a , b a,b a,b 的最大公约数:
gcd ( a , b ) = gcd ( b , a ) \gcd\left(a, b\right) = \gcd\left(b, a\right) gcd(a,b)=gcd(b,a) (交换律)
a , b a,b a,b 任意的公约数都是 gcd ( a , b ) \gcd(a, b) gcd(a,b) 的约数
gcd ( a , b ) = gcd ( a ± b , b ) \gcd\left(a, b\right) = \gcd\left(a\pm b, b\right) gcd(a,b)=gcd(a±b,b) (更相减损法 的原理)
gcd ( a , b ) = gcd ( b , a m o d b ) \gcd\left(a, b\right) = \gcd\left(b, a\bmod b\right) gcd(a,b)=gcd(b,amodb) (辗转相除法 的原理)
gcd ( a , gcd ( b , c ) ) = gcd ( gcd ( a , b ) , c ) \gcd\left(a,\gcd\left(b,c\right)\right)=\gcd\left(\gcd\left(a,b\right),c\right) gcd(a,gcd(b,c))=gcd(gcd(a,b),c) (结合律)
一种算法,作用是除去连续整数中的合数、并留下其中的质数。
(图片来源:维基共享资源)
The Sieve of Eratosthenes,最朴素的素数筛。实现简单、效率高。
下面创建一个布尔数组 not_prime
,记录区间 [ 1 , r ] \left [ 1, r\right ] [1,r] 中的数是否为合数。如果是合数,则对应值为 true
。数组长度可以用素数计数函数的估计值来确定。prime_list
保存的就是区间 [ 1 , r ] \left [ 1, r\right ] [1,r] 中的所有素数。
bool not_prime[length];
vector<int> prime_list;
void sieve(int r)
{
for(int i = 2; i <= r; ++i)
{
if(!not_prime[i]) prime_list.push_back(i);
for(int j = i + i; j <= r; j += i)
not_prime[j] = true;
}
}
O [ n log ( log n ) ] \Omicron\left[n \log{\left(\log{n}\right)}\right] O[nlog(logn)]
你问我怎么证明?太难了不会
Multiplicative function
数论中的积性函数是指一个定义域为正整数集、陪域为复数集的函数,且有如下性质:
若一个积性函数有如下性质,则它是完全积性函数(completely multiplicative function):
在数论以外的其他数学领域中研究的积性函数通常是指完全积性函数。如果没有特殊说明,下文中的所有“积性函数”均指不完全积性函数。
Euler’s Totient Function
φ ( x ) = x ∏ ( 1 − 1 p i ) \varphi\left ( x\right )=x\;\prod\left (1-\frac{1}{p_{i}}\right ) φ(x)=x∏(1−pi1)
φ ( x ) \varphi\left ( x\right ) φ(x) 为 [ 1 , x ] [1,x] [1,x] 中与 x x x 互质的数的个数。诸 p i p_{i} pi 为 x x x 的质因数。因为1与1的最大公约数是1,所以1与1互质。 φ ( 1 ) = 1. \varphi\left(1\right)=1. φ(1)=1. 欧拉函数是积性函数。
依其意义,对任意质数 p , p,\; p, 有 φ ( p ) = p − 1 \;\varphi\left(p\right)=p-1 φ(p)=p−1
对任意奇数 n , n,\; n, 有 φ ( 2 n ) = φ ( 2 ) φ ( n ) = φ ( n ) , \;\varphi\left(2 n\right)=\varphi\left(2\right)\varphi\left(n\right)=\varphi\left(n\right), φ(2n)=φ(2)φ(n)=φ(n), 因为 2 与所有奇数互质;
对任意质数 p p p 及任意正整数 k , k,\; k, 有 φ ( p k ) = p k − p k − 1 , \;\begin{aligned}\varphi\left(p^{k}\right)=p^{k}-p^{k-1}\end{aligned}, φ(pk)=pk−pk−1, 因为在 [ 1 , p k ] \left[1,p^{k}\right] [1,pk] 中,不与 p k p^{k} pk 互质的数只有 p p p 的倍数,共 p k − 1 p^{k-1} pk−1 个
当 n > 2 n>2\; n>2 时, φ ( n ) \varphi\left(n\right) φ(n) 为偶数
n = ∑ d ∣ n φ ( d ) n=\sum_{d\:\mid\: n}{\varphi\left(d\right)} n=d∣n∑φ(d)
定义函数
f ( n ) = ∑ d ∣ n φ ( d ) f\left(n\right)=\sum_{d\:\mid\: n}{\varphi\left(d\right)} f(n)=d∣n∑φ(d)
对任意一对互质的正整数 m , n m,n m,n:
f ( m ) f ( n ) = ∑ i ∣ m φ ( i ) ∑ j ∣ n φ ( j ) = ∑ i ∣ m ∑ j ∣ n φ ( i ) φ ( j ) f\left(m\right)f\left(n\right)=\sum_{i\:\mid\: m}{\varphi\left(i\right)}\sum_{j\:\mid\: n}{\varphi\left(j\right)}=\sum_{i\:\mid\: m}{\sum_{j\:\mid\: n}{\varphi\left(i\right)\varphi\left(j\right)}} f(m)f(n)=i∣m∑φ(i)j∣n∑φ(j)=i∣m∑j∣n∑φ(i)φ(j)
= ∑ i ∣ m ∑ j ∣ n φ ( i j ) = ∑ i ∣ m n φ ( i ) = f ( m n ) =\sum_{i\:\mid\: m}{\sum_{j\:\mid\: n}{\varphi\left(i\,j\right)}}=\sum_{i\:\mid\: mn}{\varphi\left(i\right)}=f\left(mn\right) =i∣m∑j∣n∑φ(ij)=i∣mn∑φ(i)=f(mn)
因此, f ( n ) f\left(n\right) f(n) 是积性函数。
对任意质数 p p p 及任意正整数 k k k :
f ( p k ) = ∑ t = 0 k φ ( p t ) = 1 + ( p − 1 ) + ( p 2 − p ) + ⋯ + ( p k − p k − 1 ) = p k f\left(p^{k}\right)=\sum^{k}_{t=0}\varphi\left(p^{t}\right)=1+\left(p-1\right)+\left(p^{2}-p\right)+\cdots +\left(p^{k}-p^{k-1}\right)=p^{k} f(pk)=t=0∑kφ(pt)=1+(p−1)+(p2−p)+⋯+(pk−pk−1)=pk
大于 1 的正整数 n n n 可以分解为若干个质数的积,即
n = ∏ p i a i n=\prod{p_{i}^{a_{i}}} n=∏piai
其中,诸 p i p_i pi 为 n n n 的质因数,诸 a i a_i ai 为对应质因数 p i p_i pi 的出现次数。
f ( n ) f\left(n\right) f(n)为积性函数,故
f ( n ) = f ( ∏ p i a i ) = ∏ f ( p i a i ) = ∏ p i a i = n . ■ f\left(n\right)=f\left(\prod{p_{i}^{a_{i}}}\right)=\prod{f\left(p_{i}^{a_{i}}\right)}=\prod{p_{i}^{a_{i}}}=n.\;\; \texttt{■} f(n)=f(∏piai)=∏f(piai)=∏piai=n.■
The Sieve of Euler,一种素数筛。因为其时间复杂度为线性,所以又叫线性筛。
上文中提到的埃氏筛有一个小问题——很多合数会被重复筛除。比如说 30 。由于 30 有质因数 2, 3, 5,埃氏筛会分别以 2 × 15 2\times15 2×15, 3 × 10 3\times10 3×10, 5 × 6 5\times6 5×6 这三种分解式来筛除 30。30 就被筛除了三次。
理论上, 如果我们能避免重复的筛除,就能提升效率。如何避免重复的筛除呢?
任意一个合数 n n n 都可以分解为 p × m p\times m p×m 的形式,其中 p p p 为 n n n 的最小质因子。我们每次只用合数的最小质因子来筛除这个合数就能避免重复筛除。
bool not_prime[length];
vector<int> prime_list;
void sieve(int r)
{
for (int i = 2; i <= r; ++i)
{
if (!not_prime[i]) prime_list.push_back(i);
for (const auto& p : prime_list)
{
if (i * p > r) break;
not_prime[i * p] = true;
if (i % p == 0) break; // 注
}
}
}
注:这一行保证了每次筛除的合数的最小质因数是 p p p。想一想为什么。
O ( n ) \Omicron(n) O(n)
然而有测试表明,当目标区间分别为 [1, 1e5]、[1, 1e6]、[1, 1e7] 时,埃氏筛都比线性筛快。
线性筛的实质就是把正整数的最小质因数分解出来,筛除合数的时候也是用到了它的实质。
只要稍加修改,上面的线性筛代码就可以算出诸数的质因数个数、诸数的质因数种数及诸数的最小质因数的出现次数。
int num_of_p_factor[length]; // 质因数个数
int variety_of_p_factor[length]; // 质因数种数
int cnt_of_min_p_factor[length]; // 因数中的最小质因数计数器
vector<int> prime_list;
void sieve(int r)
{
int num = 0;
for (int i = 2; i <= r; ++i)
{
if (num_of_p_factor[i] == 0)
{ // i 是质数
prime_list.push_back(i);
num_of_p_factor[i] = 1;
cnt_of_min_p_factor[i] = 1;
variety_of_p_factor[i] = 1;
}
for (const auto& j : prime_list)
{
if(i * j > r) break;
num_of_p_factor[i * j] = num_of_p_factor[i] + 1;
variety_of_p_factor[i * j] = variety_of_p_factor[i] + 1;
if (i % j == 0)
{
--variety_of_p_factor[i * j];
cnt_of_min_p_factor[i * j] = cnt_of_min_p_factor[i] + 1;
break;
}
cnt_of_min_p_factor[i * j] = 1;
}
}
}
因为线性筛能把正整数的最小质因数分解出来,所以线性筛也经常与 积性函数的递推关系式 结合来 生成积性函数的函数值表。下面以欧拉函数为例说明其过程:
对于 φ ( n ) \varphi\left(n\right) φ(n):
All problems in computer science can be solved by another level of indirection.
——大卫·惠勒(David John Wheeler)
抽象一下,通过线性筛生成积性函数的函数值表的格式如下:
对于 f ( n ) f\left(n\right) f(n):
线性筛有着神奇的能力,用它来筛质数就是杀鸡用牛刀,而且在数据很少的时候线性筛还比埃氏筛慢,筛质数就用埃氏筛就行了。
Euler’s Theorem
对任意正整数 a , b a,b a,b,若 a a a 与 b b b 互质,则
a φ ( b ) ≡ 1 ( m o d b ) a^{\varphi\left(b\right)}\equiv1\pmod b aφ(b)≡1(modb)
其中 φ ( n ) \varphi\left(n\right) φ(n) 为上文提到的欧拉函数。
若 b b b 为质数,且 a a a 与 b b b 互质,则 a b − 1 ≡ 1 ( m o d b ) \begin{aligned}a^{b-1}\equiv1\pmod b\end{aligned} ab−1≡1(modb) (费马小定理,Fermat’s Little Theorem)
若 a a a 与 b b b 互质,则 a q ≡ a q m o d φ ( b ) ( m o d b ) \begin{aligned}a^{q}\equiv a^{q\bmod\varphi\left(b\right)}\pmod b\end{aligned} aq≡aqmodφ(b)(modb)
Bézout’s identity,或称裴祖定理。
对于任意整数 a , b , x , y , a,b,x,y, a,b,x,y, 若 g g g 为 gcd ( a , b ) , \gcd \left(a,b\right), gcd(a,b), 则 a x + b y ax+by ax+by 必为 g g g 的倍数,特别地,一定存在整数 x , y , x,y, x,y, 使 a x + b y = g . ax+by=g. ax+by=g.
对于两个正整数 a , b , a,b, a,b, 我们钦定 h h h 是集合 A = { a x + b y ∣ ( x ; y ) ∈ Z 2 } \mathbb{A}=\{ax+by\mid(x;y)\in\mathbb{Z}^{2}\} A={ax+by∣(x;y)∈Z2} 中最小的正数。
假设集合 A \mathbb{A} A 中存在正数 m , m, m, 使得 m m m 不是 h h h 的整数倍。
利用带余除法,假定
m ÷ h = q ⋯ ⋯ r m\div h=q\cdots\cdots r m÷h=q⋯⋯r
则 0 ≤ r < h 0\le r< h 0≤r<h 且 r = m − q h r=m-qh r=m−qh
又因为 m , h ∈ A , m,h\in\mathbb{A}, m,h∈A, 所以 r ∈ A , r\in\mathbb{A}, r∈A, 这使得 h h h 不是 A \mathbb{A} A 中的最小的正数,矛盾。因此, A \mathbb{A} A 中的所有数都是 h h h 的整数倍。
所以, a , b a,b a,b 均为 h h h 的整数倍, h h h 为 a , b a,b a,b 的公因数。钦定 g = gcd ( a , b ) , g=\gcd(a,b), g=gcd(a,b), 故 h ≤ g ⋯ ⋯ ( 1 ) h\le g\cdots\cdots(1) h≤g⋯⋯(1)
设 a = s g , b = t g , a=sg,b=tg, a=sg,b=tg, 则 a x + b y = ( s x + t y ) g , ax+by=(sx+ty)g, ax+by=(sx+ty)g, 所以 A \mathbb{A} A 中的所有数都是 g g g 的整数倍。又 h ∈ A , h\in\mathbb{A}, h∈A, 故 g ≤ h ⋯ ⋯ ( 2 ) g\le h\cdots\cdots(2) g≤h⋯⋯(2)
由 ( 1 ) ( 2 ) (1)(2) (1)(2) 得 h = g . ■ h = g.\;\;\texttt{■} h=g.■
a , b a,b a,b 互质的充要条件是存在整数 x , y , x,y, x,y, 使 a x + b y = 1. ax+by=1. ax+by=1.
Extended Euclidean Algorithm ,又称 扩展欧几里得算法。可以在求得 a , b a,b a,b 的最大公约数的同时,找出整数 x , y x,y x,y ,使它们满足 贝祖等式
a x + b y = gcd ( a , b ) . ax+by=\gcd\left(a,b\right). ax+by=gcd(a,b).
若 a a a 是负数,可以把问题转化为
∣ a ∣ ( − x ) + b y = gcd ( ∣ a ∣ , b ) . \left|a\right| (-x)+by=\gcd\left(\left|a\right|,b\right). ∣a∣(−x)+by=gcd(∣a∣,b).
设 g = gcd ( a , b ) , g=\gcd(a,b), g=gcd(a,b), 并假设辗转相除法中第 i i i 步得出的两个数分别为 a i , b i a_{i},b_{i} ai,bi
则
a 1 x 1 + b 1 y 1 = a 2 x 2 + b 2 y 2 = g a_{1}x_{1}+b_{1}y_{1}=a_{2}x_{2}+b_{2}y_{2}=g a1x1+b1y1=a2x2+b2y2=g
a 2 = b 1 a_{2}=b_{1} a2=b1
b 2 = a 1 − ⌊ a 1 b 1 ⌋ × b 1 b_{2}=a_{1}-\lfloor \frac{a_{1}}{b_{1}}\rfloor\times b_{1} b2=a1−⌊b1a1⌋×b1
得
x 1 = y 2 , y 1 = x 2 − ⌊ a 1 b 1 ⌋ × y 2 x_{1}=y_{2},\;y_{1}=x_{2}-\lfloor \frac{a_{1}}{b_{1}}\rfloor\times y_{2} x1=y2,y1=x2−⌊b1a1⌋×y2
不失一般性地,有
x i = y i + 1 , y i = x i + 1 − ⌊ a i b i ⌋ × y i + 1 x_{i}=y_{i+1},\;y_{i}=x_{i+1}-\lfloor \frac{a_{i}}{b_{i}}\rfloor\times y_{i+1} xi=yi+1,yi=xi+1−⌊biai⌋×yi+1
利用上述递推公式反向递推就能求出 x 1 , y 1 . x_{1},y_{1}. x1,y1.
int xgcd(int a, int b, int &x, int &y)
{ // 递归实现
if(b == 0)
{
x = 1, y = 0;
return a;
}
int g = xgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return g;
}
int xgcd(int a, int b, int &x,int & y)
{ // 循环实现
x = 1, y = 0;
int q, t;
while(b)
{
q = a / b;
t = a;
a = b;
b = t - q * b;
t = x;
x = y;
y = t - q * y;
}
return a;
}
满足贝祖等式的 ( x , y ) (x,y) (x,y) 不止一组,假设我们已经用扩展辗转相除法得出了一组解 ( x 0 , y 0 ) , (x_0,y_0), (x0,y0), 另一组解为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)
则
a x 1 + b y 1 = a x 0 + b y 0 ax_1+by_1=ax_0+by_0 ax1+by1=ax0+by0
得
x 1 − x 0 y 1 − y 0 = − b a \frac{x_1-x_0}{y_1-y_0}=-\frac{b}{a} y1−y0x1−x0=−ab
我们期望上式右侧是既约分数 (想一想为什么),约分之,得
x 1 − x 0 y 1 − y 0 = − b / g a / g \frac{x_1-x_0}{y_1-y_0}=-\frac{b/g}{a/g} y1−y0x1−x0=−a/gb/g
故诸
( x 0 + b g n , y 0 − a g n ) \left(x_0+\frac{b}{g}n,\;y_0-\frac{a}{g}n\right) (x0+gbn,y0−gan)
均满足贝祖等式, n n n 为整数。
x x x 的最小非负数解为
( x 0 m o d b g + b g ) m o d b g . \left(x_0\bmod\frac{b}{g} +\frac{b}{g}\right)\bmod \frac{b}{g}. (x0modgb+gb)modgb.
形如 a x ≡ b ( m o d n ) ax\equiv b\pmod n ax≡b(modn) 的关于 x x x 的方程叫线性同余方程。
证明如下
我们钦定上述线性同余方程有解。
假设 b b b 不是 gcd ( a , n ) \gcd(a,n) gcd(a,n) 的整数倍。
存在整数 y , y, y, 满足
a x = n y + b ax=ny+b ax=ny+b
即
a x − n y = b ax-ny=b ax−ny=b
由贝祖定理, b b b 是 gcd ( a , n ) \gcd(a,n) gcd(a,n) 的整数倍,矛盾。故线性同余方程有解的充要条件是 b b b 为 gcd ( a , n ) \gcd(a,n) gcd(a,n) 的整数倍。 ■ \;\; \texttt{■} ■
线性同余方程可以转化为贝祖等式求解,构造 a x − n y = b = k gcd ( a , n ) ax-ny=b=k\gcd(a,n) ax−ny=b=kgcd(a,n) 即可。
诸 x x x 的周期应为 b / gcd ( a , n ) . b/\gcd(a,n). b/gcd(a,n).
显然,在 a , b , m a,b,m a,b,m 均为正整数时,下列关系成立:
( a + b ) m o d m = ( a m o d m + b m o d m + m ) m o d m (a+b)\bmod m=(a\bmod m+b\bmod m + m)\bmod m (a+b)modm=(amodm+bmodm+m)modm
( a − b ) m o d m = ( a m o d m − b m o d m + m ) m o d m (a-b)\bmod m=(a\bmod m-b\bmod m + m)\bmod m (a−b)modm=(amodm−bmodm+m)modm
( a b ) m o d m = ( ( a m o d m ) ( b m o d m ) ) m o d m (ab)\bmod m=((a\bmod m)(b\bmod m))\bmod m (ab)modm=((amodm)(bmodm))modm
我们通常利用上述关系,不断地对多次计算的中间结果求余,避免最终结果溢出。
但是,下列关系却一般不成立:
因此一旦计算过程中有除法,上面正确的三个式子也不能用了。要是能简单地将求余操作分别移到被除数和除数的位置上,那么很多程序会简单很多。
类比实数域上的除法和乘法的关系,如果存在整数 c , c, c, 满足 b c ≡ 1 ( m o d m ) , bc\equiv 1 \pmod m, bc≡1(modm), 则有
( a ÷ b ) m o d m = ( ( a m o d m ) ( c m o d m ) ) m o d m (a\div b)\bmod m=((a\bmod m)(c\bmod m))\bmod m (a÷b)modm=((amodm)(cmodm))modm
从直觉上来说,就是“在模 m m m 的意义下, b b b 等于 c c c 的倒数”。请读者尝试证明该方法的合理性。
我们把上面的 c c c 叫做 “ b b b 模 m m m 的模逆元(或模倒数)”,显然, b b b 也是 c c c 在模 m m m 意义下的模逆元;如果模逆元存在,它通常不唯一。
もしモジュロが素数じゃない数だったときのことを考えるとふるえて夜も眠れません。
—— AtCoder 某选手
证明如下
我们钦定整数 a a a 对模数 m m m 有模逆元 b . b. b.
假设 a , m a,m a,m 有一相同质因数 p , p, p, 则存在整数 x , y , x,y, x,y, 使得
a = p x , m = p y a=px,m=py a=px,m=py
又由
a b ≡ 1 ( m o d m ) ab\equiv 1\pmod m ab≡1(modm)
可得,存在整数 k , k, k, 满足
a b = m k + 1 ab=mk+1 ab=mk+1
即
p x b = p y k + 1 pxb=pyk+1 pxb=pyk+1
p ( x b − y k ) = 1 p(xb-yk)=1 p(xb−yk)=1
显然 p = 1 , p=1, p=1, 这与上述假设矛盾。因此,整数 a a a 对模数 m m m 有模逆元的充要条件是 a , m a,m a,m 互质。 ■ \texttt{■} ■
欧拉定理:
a , m a,m a,m 互质时,有
a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi\left(m\right)}\equiv 1\pmod m aφ(m)≡1(modm)
所以, a φ ( m ) − 1 a^{\varphi\left(m\right)-1} aφ(m)−1 为 a a a 在模 m m m 意义下的模逆元。其中,欧拉函数的值可以用线性筛得出。一个特别常见的情况是,当 m m m 为质数时, a m − 2 a^{m-2} am−2 为模逆元之一。 请读者尝试证明之。
贝祖定理:
当 a , m a,m a,m 互质、即 gcd ( a , m ) = 1 \gcd\left(a,m\right) = 1 gcd(a,m)=1 时,存在整数 x , y , x,y, x,y, 满足
a x + m y = 1 ax+my=1 ax+my=1
下面证明 x x x 为 a a a 在模 m m m 意义下的模逆元:
a x + m y ≡ a x ≡ 1 ( m o d m ) . ■ ax+my\equiv ax\equiv 1\pmod m.\;\; \texttt{■} ax+my≡ax≡1(modm).■
可用扩展辗转相除法(扩展欧几里得算法)求出 x . x. x.
中国剩余定理,Chinese remainder theorem
将多个线性同余方程联立,就得到了线性同余方程组。
考虑如下形式的线性同余方程组:
x ≡ b i ( m o d m i ) , i ∈ { 1 , 2 , 3 , ⋯ , k } , k ∈ Z + . x\equiv b_i \pmod {m_{i}},\;\;i\in\{1,2,3,\cdots,k\},k\in\mathbb{Z^{+}}. x≡bi(modmi),i∈{1,2,3,⋯,k},k∈Z+.
孙子定理:
若诸 m i m_i mi 两两互质,则上述方程组有解,通解可由下述方式得出:
设 M = ∏ m i , M i = M / m i , M=\prod{m_i},\; M_i=M/m_i, M=∏mi,Mi=M/mi, t i t_i ti 为 M i M_i Mi 模 m i m_i mi 的模逆元,则通解为
x = k M + ∑ a i t i M i x=kM+\sum {a_{i}t_{i}M_{i}} x=kM+∑aitiMi
其中 k k k 为整数。在模 M M M 的意义下,方程组只有一个解:
x = ∑ a i t i M i . x=\sum {a_{i}t_{i}M_{i}}. x=∑aitiMi.
证明:
因为诸 m i m_i mi 互质,所以 gcd ( m i , M i ) = 1 , \gcd(m_i,M_i)=1, gcd(mi,Mi)=1, 所以 M i M_i Mi 有模 m i m_i mi 的模逆元 t i , t_i, ti, 故 a i t i M i ≡ a i ( m o d m i ) ; a_{i}t_{i}M_{i}\equiv a_i \pmod {m_{i}}; aitiMi≡ai(modmi); 对任意 j , j, j, 当 i ≠ j i\neq j i=j 时, a j t j M j ≡ a i ( m o d m i ) ; a_{j}t_{j}M_{j}\equiv a_i \pmod {m_{i}}; ajtjMj≡ai(modmi);
故,对任意 i , i, i, 有
x = a i t i M i + ∑ j ≠ i a j t j M j ≡ a i ( m o d m i ) . x=a_{i}t_{i}M_{i}+\sum_{j\neq i}{a_{j}t_{j}M_{j}}\equiv a_i\pmod{m_{i}}. x=aitiMi+j=i∑ajtjMj≡ai(modmi).
■ \;\;\texttt{■} ■