定理内容
a = b × q + r , 0 ≤ r < b a = b \times q + r, \quad 0 \leq r < b a = b × q + r , 0 ≤ r < b
在模 n n n 的世界里,所有的数字都在 0 0 0 到 n − 1 n-1 n − 1 之间循环。
假设我们有两个整数 a a a 和 b b b ,以及一个正整数 n n n (作为模数)。
a = k 1 ⋅ n + r 1 , 0 ≤ r 1 < n , and r 1 = a ( mod n ) a = k_1 \cdot n + r_1, \quad 0 \leq r_1 < n, \text{ and } r_1 = a \space(\text{mod } n) a = k 1 ⋅ n + r 1 , 0 ≤ r 1 < n , and r 1 = a ( mod n )
b = k 2 ⋅ n + r 2 , 0 ≤ r 2 < n , and r 2 = b ( mod n ) b = k_2 \cdot n + r_2, \quad 0 \leq r_2 < n, \text{ and } r_2 = b \space(\text{mod } n) b = k 2 ⋅ n + r 2 , 0 ≤ r 2 < n , and r 2 = b ( mod n )
定理 :两个数相加后的余数,等于它们各自余数相加后的余数。
( a + b ) ( m o d n ) = ( ( a ( m o d n ) ) + ( b ( m o d n ) ) ) ( m o d n ) (a + b) \pmod n = ((a \pmod n) + (b \pmod n)) \pmod n ( a + b ) ( mod n ) = (( a ( mod n )) + ( b ( mod n ))) ( mod n )
推导过程 :
将我们的初始条件代入 ( a + b ) (a + b) ( a + b ) 中:
a + b = ( k 1 ⋅ n + r 1 ) + ( k 2 ⋅ n + r 2 ) a + b = (k_1 \cdot n + r_1) + (k_2 \cdot n + r_2) a + b = ( k 1 ⋅ n + r 1 ) + ( k 2 ⋅ n + r 2 )
提取公因数 n n n 进行重组:
a + b = ( k 1 + k 2 ) ⋅ n + ( r 1 + r 2 ) a + b = (k_1 + k_2) \cdot n + (r_1 + r_2) a + b = ( k 1 + k 2 ) ⋅ n + ( r 1 + r 2 )
当我们对等式两边同时求模 ( m o d n ) \pmod n ( mod n ) 时,因为 ( k 1 + k 2 ) ⋅ n (k_1 + k_2) \cdot n ( k 1 + k 2 ) ⋅ n 是 n n n 的整数倍,它除以 n n n 的余数必定是 0。所以这部分被完全“抹除”了。
结果只剩下后面的部分:
( a + b ) ( m o d n ) = ( r 1 + r 2 ) ( m o d n ) (a + b) \pmod n = (r_1 + r_2) \pmod n ( a + b ) ( mod n ) = ( r 1 + r 2 ) ( mod n )
因为 r 1 = a ( m o d n ) r_1 = a \pmod n r 1 = a ( mod n ) 且 r 2 = b ( m o d n ) r_2 = b \pmod n r 2 = b ( mod n ) ,代回即可证得加法律。
定理 :两个数相乘后的余数,等于它们各自余数相乘后的余数。
( a × b ) ( m o d n ) = ( ( a ( m o d n ) ) × ( b ( m o d n ) ) ) ( m o d n ) (a \times b) \pmod n = ((a \pmod n) \times (b \pmod n)) \pmod n ( a × b ) ( mod n ) = (( a ( mod n )) × ( b ( mod n ))) ( mod n )
推导过程 :
我们将初始条件代入 ( a × b ) (a \times b) ( a × b ) 并展开多项式:
a × b = ( k 1 ⋅ n + r 1 ) × ( k 2 ⋅ n + r 2 ) a × b = ( k 1 ⋅ k 2 ⋅ n 2 ) + ( k 1 ⋅ n ⋅ r 2 ) + ( k 2 ⋅ n ⋅ r 1 ) + ( r 1 ⋅ r 2 ) a \times b = (k_1 \cdot n + r_1) \times (k_2 \cdot n + r_2) \\ a \times b = (k_1 \cdot k_2 \cdot n^2) + (k_1 \cdot n \cdot r_2) + (k_2 \cdot n \cdot r_1) + (r_1 \cdot r_2) a × b = ( k 1 ⋅ n + r 1 ) × ( k 2 ⋅ n + r 2 ) a × b = ( k 1 ⋅ k 2 ⋅ n 2 ) + ( k 1 ⋅ n ⋅ r 2 ) + ( k 2 ⋅ n ⋅ r 1 ) + ( r 1 ⋅ r 2 )
接下来,我们将前三项提取公因式 n n n :
a × b = n ⋅ ( k 1 ⋅ k 2 ⋅ n + k 1 ⋅ r 2 + k 2 ⋅ r 1 ) + ( r 1 ⋅ r 2 ) a \times b = n \cdot (k_1 \cdot k_2 \cdot n + k_1 \cdot r_2 + k_2 \cdot r_1) + (r_1 \cdot r_2) a × b = n ⋅ ( k 1 ⋅ k 2 ⋅ n + k 1 ⋅ r 2 + k 2 ⋅ r 1 ) + ( r 1 ⋅ r 2 )
当我们对这个表达式求模 ( m o d n ) \pmod n ( mod n ) 时,前面含有 n n n 的巨大项的余数统统是 0,全部被消掉。最终只剩下:
( a × b ) ( m o d n ) = ( r 1 ⋅ r 2 ) ( m o d n ) (a \times b) \pmod n = (r_1 \cdot r_2) \pmod n ( a × b ) ( mod n ) = ( r 1 ⋅ r 2 ) ( mod n )
定理 :
a b ( m o d n ) = ( ( a ( m o d n ) ) b ) ( m o d n ) a^b \pmod n = ((a \pmod n)^b) \pmod n a b ( mod n ) = (( a ( mod n ) ) b ) ( mod n )
推导过程 :
幂运算其实就是连乘运算。根据上面刚刚证明的“乘法律”,既然每一次相乘都可以提前取模,那么连乘 b b b 次自然也可以在每一步都取模。
a b ( m o d n ) = ( a × a × ⋯ × a ⏟ b 次 ) ( m o d n ) a^b \pmod n = (\underbrace{a \times a \times \cdots \times a}_{b \text{ 次}}) \pmod n a b ( mod n ) = ( b 次 a × a × ⋯ × a ) ( mod n )
反复应用乘法律:
a b ( m o d n ) = ( ( a ( m o d n ) ) × ( a ( m o d n ) ) × ⋯ × ( a ( m o d n ) ) ⏟ b 次 ) ( m o d n ) a^b \pmod n = (\underbrace{(a \pmod n) \times (a \pmod n) \times \cdots \times (a \pmod n)}_{b \text{ 次}}) \pmod n a b ( mod n ) = ( b 次 ( a ( mod n )) × ( a ( mod n )) × ⋯ × ( a ( mod n )) ) ( mod n )
证毕。
在模算术里,我们不用“除法”,我们用乘以它的逆元 来代替除法。
如果想计算 a / b ( m o d n ) a / b \pmod n a / b ( mod n ) ,我们其实是去寻找一个特殊的数字 x x x (这就是 b b b 的乘法逆元),使得:
b × x ≡ 1 ( m o d n ) b \times x \equiv 1 \pmod n b × x ≡ 1 ( mod n )
找到之后,除以 b b b 就变成了乘以 x x x :
( a / b ) ( m o d n ) = ( a × x ) ( m o d n ) (a / b) \pmod n = (a \times x) \pmod n ( a / b ) ( mod n ) = ( a × x ) ( mod n )
定理内容 :如果两个正整数 a a a 和 n n n 互质,那么 a a a 的 ϕ ( n ) \phi(n) ϕ ( n ) 次方除以 n n n 的余数,必定等于 1。
用严谨的数学公式表达:
a ϕ ( n ) ≡ 1 ( m o d n ) a^{\phi(n)} \equiv 1 \pmod n a ϕ ( n ) ≡ 1 ( mod n )
定义 :对于任意正整数 n n n ,欧拉函数 ϕ ( n ) \phi(n) ϕ ( n ) 代表的是:在从 1 1 1 到 n n n 的正整数中,有多少个数字与 n n n 互质 (互质即最大公约数为 1)。
让我们看几个直观的例子:
计算 ϕ ( 10 ) \phi(10) ϕ ( 10 ) :
1 到 10 之间,与 10 互质的数有:1, 3, 7, 9。一共 4 个。
所以,ϕ ( 10 ) = 4 \phi(10) = 4 ϕ ( 10 ) = 4 。
计算素数的欧拉函数 :
假设 n n n 是一个素数(比如 p = 7 p=7 p = 7 )。因为 7 是素数,所以比它小的所有正整数 (1, 2, 3, 4, 5, 6) 都和它互质。
规律 1:如果 p p p 是素数,那么 ϕ ( p ) = p − 1 \phi(p) = p - 1 ϕ ( p ) = p − 1 。
计算两个素数乘积的欧拉函数 :
如果 n n n 是由两个不同的素数 p p p 和 q q q 相乘得到的(即 n = p × q n = p \times q n = p × q )。欧拉函数有一个奇妙的乘法性质:ϕ ( p × q ) = ϕ ( p ) × ϕ ( q ) \phi(p \times q) = \phi(p) \times \phi(q) ϕ ( p × q ) = ϕ ( p ) × ϕ ( q ) 。
规律 2:如果 n = p × q n = p \times q n = p × q (p , q p, q p , q 为素数),那么 ϕ ( n ) = ( p − 1 ) ( q − 1 ) \phi(n) = (p - 1)(q - 1) ϕ ( n ) = ( p − 1 ) ( q − 1 ) 。
从左到右的二进制模幂算法(Left-to-Right Binary Exponentiation)
int modexp ( int m , int e_bits [] , int e_bits_len , int n )
{
int R, L;
int k = 0 ;
int s = 1 ;
while (k < e_bits_len)
{
if ( e_bits [k] == 1 )
R = (s * m) % n;
else
R = s;
s = R * R % n;
L = R;
k ++ ;
}
return L;
} 霍纳法则(Horner's Method) ,在中国古代称为“秦九韶算法”,它的核心思想是通过不断提取公因式,将多项式转化为一层层的嵌套,从而消灭高次幂的计算。
假设我们要计算 m 5 ( m o d n ) m^5 \pmod n m 5 ( mod n ) 。
数字 5 5 5 的二进制是 101 2 101_2 10 1 2 。在代码中,数组 e_bits[] = {1, 0, 1}(从高位到低位)。
我们可以把 5 5 5 用多项式展开:
5 = 1 × 2 2 + 0 × 2 1 + 1 × 2 0 5 = 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 5 = 1 × 2 2 + 0 × 2 1 + 1 × 2 0
那么 m 5 m^5 m 5 可以改写为:
m 5 = m 1 × 2 2 + 0 × 2 1 + 1 × 2 0 m^5 = m^{1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0} m 5 = m 1 × 2 2 + 0 × 2 1 + 1 × 2 0
为了方便计算,数学家提取公因式(这称为霍纳法则),将其变为一层层嵌套的形式:
m 5 = ( ( m 1 ) 2 × m 0 ) 2 × m 1 m^5 = ((m^1)^2 \times m^0)^2 \times m^1 m 5 = (( m 1 ) 2 × m 0 ) 2 × m 1
推导
必备工具:指数的两个黄金法则 在开始推导前,我们需要复习初中代数里关于指数的两个核心定律:
法则 1(加法变乘法): m a + b = m a × m b m^{a + b} = m^a \times m^b m a + b = m a × m b 法则 2(乘法变平方): m a × 2 = ( m a ) 2 m^{a \times 2} = (m^a)^2 m a × 2 = ( m a ) 2 第一步:对“指数”使用霍纳法则 我们先不看底数 m m m ,单看指数 5 5 5 的二进制展开:
E = 1 × 2 2 + 0 × 2 1 + 1 × 2 0 E = 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 E = 1 × 2 2 + 0 × 2 1 + 1 × 2 0
如果把它看作一个关于变量 x x x (这里 x = 2 x=2 x = 2 )的多项式:a x 2 + b x + c ax^2 + bx + c a x 2 + b x + c 。
按照传统的计算方法,你需要先算 x 2 x^2 x 2 ,再乘 a a a ,再加后面的项。这需要多次独立的乘法。
我们对指数 E E E 进行提取公因式 2 2 2 的操作:
前两项 ( 1 × 2 2 + 0 × 2 1 ) (1 \times 2^2 + 0 \times 2^1) ( 1 × 2 2 + 0 × 2 1 ) 都有因子 2 2 2 ,我们把 2 2 2 提出来:
E = ( 1 × 2 + 0 ) × 2 1 + 1 × 2 0 E = (1 \times 2 + 0) \times 2^1 + 1 \times 2^0 E = ( 1 × 2 + 0 ) × 2 1 + 1 × 2 0
为了格式统一,我们把 2 0 2^0 2 0 直接写成 1 1 1 (因为它就是末位数字),此时指数变成了:
E = ( 1 × 2 + 0 ) × 2 + 1 E = (1 \times 2 + 0) \times 2 + 1 E = ( 1 × 2 + 0 ) × 2 + 1
第二步:将指数放回底数 m m m 上 现在,我们把经过霍纳法则变形的指数,放回到 m m m 的右上角:
m 5 = m ( 1 × 2 + 0 ) × 2 + 1 m^5 = m^{(1 \times 2 + 0) \times 2 + 1} m 5 = m ( 1 × 2 + 0 ) × 2 + 1
第一层拆解(处理最外面的 + 1):
根据法则 1 (m a + b = m a × m b m^{a + b} = m^a \times m^b m a + b = m a × m b ),把尾巴上的 + 1 拆下来:
m ( 1 × 2 + 0 ) × 2 + 1 = m ( 1 × 2 + 0 ) × 2 × m 1 m^{(1 \times 2 + 0) \times 2 + 1} = m^{(1 \times 2 + 0) \times 2} \times m^1 m ( 1 × 2 + 0 ) × 2 + 1 = m ( 1 × 2 + 0 ) × 2 × m 1
第二层拆解(处理最外面的 x 2):
根据法则 2 (m a × 2 = ( m a ) 2 m^{a \times 2} = (m^a)^2 m a × 2 = ( m a ) 2 ),把乘 2 2 2 变成外面的平方:
m ( 1 × 2 + 0 ) × 2 × m 1 = ( m ( 1 × 2 + 0 ) ) 2 × m 1 m^{(1 \times 2 + 0) \times 2} \times m^1 = (m^{(1 \times 2 + 0)})^2 \times m^1 m ( 1 × 2 + 0 ) × 2 × m 1 = ( m ( 1 × 2 + 0 ) ) 2 × m 1
第三层拆解(处理括号里的 + 0):
再次使用法则 1 :
( m 1 × 2 + 0 ) 2 × m 1 = ( m 1 × 2 × m 0 ) 2 × m 1 (m^{1 \times 2 + 0})^2 \times m^1 = (m^{1 \times 2} \times m^0)^2 \times m^1 ( m 1 × 2 + 0 ) 2 × m 1 = ( m 1 × 2 × m 0 ) 2 × m 1
第四层拆解(处理最内层的 x 2):
再次使用法则 2 :
( ( m 1 ) 2 × m 0 ) 2 × m 1 ((m^1)^2 \times m^0)^2 \times m^1 (( m 1 ) 2 × m 0 ) 2 × m 1
代码逻辑计算(假设 s s s 初始为 1):
第 1 轮 (k = 0 k=0 k = 0 , bit=1):最内层 R = (s * m) % n → \rightarrow → 此时 R = m 1 R = m^1 R = m 1 。s = R * R % n → \rightarrow → 为下一轮做准备,将当前结果平方,s = ( m 1 ) 2 = m 2 s = (m^1)^2 = m^2 s = ( m 1 ) 2 = m 2 。L = R → \rightarrow → L = m 1 L = m^1 L = m 1 。第 2 轮 (k = 1 k=1 k = 1 , bit=0):中间层 由于 bit 为 0,不乘 m m m 。 R = s → \rightarrow → 直接继承上一轮的平方结果,R = m 2 R = m^2 R = m 2 。s = R * R % n → \rightarrow → 继续为下一轮平方,s = ( m 2 ) 2 = m 4 s = (m^2)^2 = m^4 s = ( m 2 ) 2 = m 4 。L = R → \rightarrow → L = m 2 L = m^2 L = m 2 。第 3 轮 (k = 2 k=2 k = 2 , bit=1):最外层 R = (s * m) % n → \rightarrow → R = m 4 × m 1 = m 5 R = m^4 \times m^1 = m^5 R = m 4 × m 1 = m 5 。s = R * R % n → \rightarrow → 算出 m 10 m^{10} m 10 (由于循环结束,这个值被废弃)。L = R → \rightarrow → 最终结果 L = m 5 L = m^5 L = m 5 。 代码中的 s = R * R 负责将当前的指数“左移”(即翻倍),而 if (e_bits[k] == 1) 负责在末尾加上个位数。这使得原本需要乘 e e e 次的运算,变成了只需乘 log 2 ( e ) \log_2(e) log 2 ( e ) 次。
在加密之前,我们需要生成一对钥匙:公钥 (公开给所有人用来加密)和私钥 (自己藏好用来解密)。
第一步:寻找素数
随机选择两个不相等的巨大素数 p p p 和 q q q 。
第二步:计算公共模数 n n n
n = p × q n = p \times q n = p × q
这个 n n n 的长度就是密钥长度(比如2048位)。n n n 是公开的。
第三步:计算欧拉函数 ϕ ( n ) \phi(n) ϕ ( n )
根据欧拉函数性质,因为 p p p 和 q q q 都是素数:
ϕ ( n ) = ( p − 1 ) ( q − 1 ) \phi(n) = (p-1)(q-1) ϕ ( n ) = ( p − 1 ) ( q − 1 )
ϕ ( n ) \phi(n) ϕ ( n ) 代表在小于等于 n n n 的正整数中,与 n n n 互质的数的个数。这个值必须严格保密。
第四步:选择公钥指数 e e e
随机选择一个整数 e e e ,条件是 1 < e < ϕ ( n ) 1 < e < \phi(n) 1 < e < ϕ ( n ) ,且 e e e 与 ϕ ( n ) \phi(n) ϕ ( n ) 互质。通常会选择 65537。
此时,公钥诞生了:( e , n ) (e, n) ( e , n )
第五步:计算私钥指数 d d d
找到一个整数 d d d ,使得 e e e 和 d d d 相乘后对 ϕ ( n ) \phi(n) ϕ ( n ) 取模余数为 1。即求 e e e 在模 ϕ ( n ) \phi(n) ϕ ( n ) 下的乘法逆元:
e × d ≡ 1 ( m o d ϕ ( n ) ) e \times d \equiv 1 \pmod{\phi(n)} e × d ≡ 1 ( mod ϕ ( n ))
这等价于 e × d = k × ϕ ( n ) + 1 e \times d = k \times \phi(n) + 1 e × d = k × ϕ ( n ) + 1 (k k k 为正整数)。
此时,私钥诞生了:( d , n ) (d, n) ( d , n )
假设我们要发送一条机密信息,首先我们将其转换为一个数字 m m m (必须满足 m < n m < n m < n )。
加密过程(使用公钥)
发送方使用接收方的公钥 ( e , n ) (e, n) ( e , n ) ,计算密文 c c c :
c ≡ m e ( m o d n ) c \equiv m^e \pmod{n} c ≡ m e ( mod n )
发送方将计算出的数字 c c c 发送出去。
解密/还原过程(使用私钥)
接收方收到密文 c c c 后,使用自己的私钥 ( d , n ) (d, n) ( d , n ) 进行计算,就能神奇地还原出 m m m :
m ≡ c d ( m o d n ) m \equiv c^d \pmod{n} m ≡ c d ( mod n )
我们将加密公式代入解密公式:
c d ≡ ( m e ) d ≡ m e d ( m o d n ) c^d \equiv (m^e)^d \equiv m^{ed} \pmod{n} c d ≡ ( m e ) d ≡ m e d ( mod n )
我们在生成密钥的第五步知道:e d = k ϕ ( n ) + 1 ed = k\phi(n) + 1 e d = k ϕ ( n ) + 1 。所以:
m e d = m k ϕ ( n ) + 1 = m k ϕ ( n ) × m = ( m ϕ ( n ) ) k × m m^{ed} = m^{k\phi(n) + 1} = m^{k\phi(n)} \times m = (m^{\phi(n)})^k \times m m e d = m k ϕ ( n ) + 1 = m k ϕ ( n ) × m = ( m ϕ ( n ) ) k × m
根据欧拉定理 :如果 m m m 和 n n n 互质,那么 m ϕ ( n ) ≡ 1 ( m o d n ) m^{\phi(n)} \equiv 1 \pmod{n} m ϕ ( n ) ≡ 1 ( mod n ) 。
因此:
( m ϕ ( n ) ) k × m ≡ 1 k × m ≡ m ( m o d n ) (m^{\phi(n)})^k \times m \equiv 1^k \times m \equiv m \pmod{n} ( m ϕ ( n ) ) k × m ≡ 1 k × m ≡ m ( mod n )