离散对数 定义 前置知识:阶与原根 。
离散对数的定义方式和对数类似。取有原根的正整数模数 𝑚 m ,设其一个原根为 𝑔 g . 对满足 ( 𝑎 , 𝑚 ) = 1 ( a , m ) = 1 的整数 𝑎 a ,我们知道必存在唯一的整数 0 ≤ 𝑘 < 𝜑 ( 𝑚 ) 0 ≤ k < φ ( m ) 使得
𝑔 𝑘 ≡ 𝑎 ( m o d 𝑚 ) g k ≡ a ( mod m ) 我们称这个 𝑘 k 为以 𝑔 g 为底,模 𝑚 m 的离散对数,记作 𝑘 = i n d 𝑔 𝑎 k = ind g a ,在不引起混淆的情况下可记作 i n d 𝑎 ind a .
显然 i n d 𝑔 1 = 0 ind g 1 = 0 ,i n d 𝑔 𝑔 = 1 ind g g = 1 .
性质 离散对数的性质也和对数有诸多类似之处。
性质 设 𝑔 g 是模 𝑚 m 的原根,( 𝑎 , 𝑚 ) = ( 𝑏 , 𝑚 ) = 1 ( a , m ) = ( b , m ) = 1 ,则:
i n d 𝑔 ( 𝑎 𝑏 ) ≡ i n d 𝑔 𝑎 + i n d 𝑔 𝑏 ( m o d 𝜑 ( 𝑚 ) ) ind g ( a b ) ≡ ind g a + ind g b ( mod φ ( m ) )
进而 ( ∀ 𝑛 ∈ 𝐍 ) , i n d 𝑔 𝑎 𝑛 ≡ 𝑛 i n d 𝑔 𝑎 ( m o d 𝜑 ( 𝑚 ) ) ( ∀ n ∈ N ) , ind g a n ≡ n ind g a ( mod φ ( m ) )
若 𝑔 1 g 1 也是模 𝑚 m 的原根,则 i n d 𝑔 𝑎 ≡ i n d 𝑔 1 𝑎 ⋅ i n d 𝑔 𝑔 1 ( m o d 𝜑 ( 𝑚 ) ) ind g a ≡ ind g 1 a ⋅ ind g g 1 ( mod φ ( m ) )
𝑎 ≡ 𝑏 ( m o d 𝑚 ) ⟺ i n d 𝑔 𝑎 = i n d 𝑔 𝑏 a ≡ b ( mod m ) ⟺ ind g a = ind g b 证明 𝑔 i n d 𝑔 ( 𝑎 𝑏 ) ≡ 𝑎 𝑏 ≡ 𝑔 i n d 𝑔 𝑎 𝑔 i n d 𝑔 𝑏 ≡ 𝑔 i n d 𝑔 𝑎 + i n d 𝑔 𝑏 ( m o d 𝑚 ) g ind g ( a b ) ≡ a b ≡ g ind g a g ind g b ≡ g ind g a + ind g b ( mod m ) 令 𝑥 = i n d 𝑔 1 𝑎 x = ind g 1 a ,则 𝑎 ≡ 𝑔 𝑥 1 ( m o d 𝑚 ) a ≡ g 1 x ( mod m ) . 又令 𝑦 = i n d 𝑔 𝑔 1 y = ind g g 1 ,则 𝑔 1 ≡ 𝑔 𝑦 ( m o d 𝑚 ) g 1 ≡ g y ( mod m ) .
故 𝑎 ≡ 𝑔 𝑥 𝑦 ( m o d 𝑚 ) a ≡ g x y ( mod m ) ,即 i n d 𝑔 𝑎 ≡ 𝑥 𝑦 ≡ i n d 𝑔 1 𝑎 ⋅ i n d 𝑔 𝑔 1 ( m o d 𝜑 ( 𝑚 ) ) ind g a ≡ x y ≡ ind g 1 a ⋅ ind g g 1 ( mod φ ( m ) )
注意到
i n d 𝑔 𝑎 = i n d 𝑔 𝑏 ⟺ i n d 𝑔 𝑎 ≡ i n d 𝑔 𝑏 ( m o d 𝜑 ( 𝑚 ) ) ⟺ 𝑔 i n d 𝑔 𝑎 ≡ 𝑔 i n d 𝑔 𝑏 ( m o d 𝑚 ) ⟺ 𝑎 ≡ 𝑏 ( m o d 𝑚 ) ind g a = ind g b ⟺ ind g a ≡ ind g b ( mod φ ( m ) ) ⟺ g ind g a ≡ g ind g b ( mod m ) ⟺ a ≡ b ( mod m ) 大步小步算法 目前离散对数问题仍不存在多项式时间经典算法(离散对数问题的输入规模是输入数据的位数)。在密码学中,基于这一点人们设计了许多非对称加密算法,如 Ed25519 。
在算法竞赛中,BSGS(baby-step giant-step,大步小步算法)常用于求解离散对数问题。形式化地说,对 𝑎 , 𝑏 , 𝑚 ∈ 𝐙 + a , b , m ∈ Z + ,该算法可以在 𝑂 ( √ 𝑚 ) O ( m ) 的时间内求解
𝑎 𝑥 ≡ 𝑏 ( m o d 𝑚 ) a x ≡ b ( mod m ) 其中 𝑎 ⟂ 𝑚 a ⟂ m 。方程的解 𝑥 x 满足 0 ≤ 𝑥 < 𝑚 0 ≤ x < m .(注意 𝑚 m 不一定是素数)
算法描述 令 𝑥 = 𝐴 ⌈ √ 𝑚 ⌉ − 𝐵 x = A ⌈ m ⌉ − B ,其中 0 ≤ 𝐴 , 𝐵 ≤ ⌈ √ 𝑚 ⌉ 0 ≤ A , B ≤ ⌈ m ⌉ ,则有 𝑎 𝐴 ⌈ √ 𝑚 ⌉ − 𝐵 ≡ 𝑏 ( m o d 𝑚 ) a A ⌈ m ⌉ − B ≡ b ( mod m ) ,稍加变换,则有 𝑎 𝐴 ⌈ √ 𝑚 ⌉ ≡ 𝑏 𝑎 𝐵 ( m o d 𝑚 ) a A ⌈ m ⌉ ≡ b a B ( mod m ) .
我们已知的是 𝑎 , 𝑏 a , b ,所以我们可以先算出等式右边的 𝑏 𝑎 𝐵 b a B 的所有取值,枚举 𝐵 B ,用 hash
/map
存下来,然后逐一计算 𝑎 𝐴 ⌈ √ 𝑚 ⌉ a A ⌈ m ⌉ ,枚举 𝐴 A ,寻找是否有与之相等的 𝑏 𝑎 𝐵 b a B ,从而我们可以得到所有的 𝑥 x ,𝑥 = 𝐴 ⌈ √ 𝑚 ⌉ − 𝐵 x = A ⌈ m ⌉ − B .
注意到 𝐴 , 𝐵 A , B 均小于 ⌈ √ 𝑚 ⌉ ⌈ m ⌉ ,所以时间复杂度为 Θ ( √ 𝑚 ) Θ ( m ) ,用 map
则多一个 l o g log .
为什么要求 𝑎 a 与 𝑚 m 互质 注意到我们求出的是 𝐴 , 𝐵 A , B ,我们需要保证从 𝑎 𝐴 ⌈ √ 𝑚 ⌉ ≡ 𝑏 𝑎 𝐵 ( m o d 𝑚 ) a A ⌈ m ⌉ ≡ b a B ( mod m ) 可以推回 𝑎 𝐴 ⌈ √ 𝑚 ⌉ − 𝐵 ≡ 𝑏 ( m o d 𝑚 ) a A ⌈ m ⌉ − B ≡ b ( mod m ) ,后式是前式左右两边除以 𝑎 𝐵 a B 得到,所以必须有 𝑎 𝐵 ⟂ 𝑚 a B ⟂ m 即 𝑎 ⟂ 𝑚 a ⟂ m .
进阶篇 对 𝑎 , 𝑏 ∈ 𝐙 + a , b ∈ Z + ,𝑝 ∈ 𝐏 p ∈ P ,求解
𝑥 𝑎 ≡ 𝑏 ( m o d 𝑝 ) x a ≡ b ( mod p ) 该问题可以转化为 BSGS 求解的问题。
由于式子中的模数 𝑝 p 是一个质数,那么 𝑝 p 一定存在一个原根 𝑔 g . 因此对于模 𝑝 p 意义下的任意的数 𝑥 ( 1 ≤ 𝑥 < 𝑝 ) x ( 1 ≤ x < p ) 有且仅有一个数 𝑖 ( 0 ≤ 𝑖 < 𝑝 − 1 ) i ( 0 ≤ i < p − 1 ) 满足 𝑥 = 𝑔 𝑖 x = g i .
方法一 我们令 𝑥 = 𝑔 𝑐 x = g c ,𝑔 g 是 𝑝 p 的原根(我们一定可以找到这个 𝑔 g 和 𝑐 c ),问题转化为求解 ( 𝑔 𝑐 ) 𝑎 ≡ 𝑏 ( m o d 𝑝 ) ( g c ) a ≡ b ( mod p ) . 稍加变换,得到
( 𝑔 𝑎 ) 𝑐 ≡ 𝑏 ( m o d 𝑝 ) ( g a ) c ≡ b ( mod p ) 于是就转换成了 BSGS 的基本模型了,可以在 𝑂 ( √ 𝑝 ) O ( p ) 解出 𝑐 c ,这样可以得到原方程的一个特解 𝑥 0 ≡ 𝑔 𝑐 ( m o d 𝑝 ) x 0 ≡ g c ( mod p ) .
方法二 我们仍令 𝑥 = 𝑔 𝑐 x = g c ,并且设 𝑏 = 𝑔 𝑡 b = g t ,于是我们得到
𝑔 𝑎 𝑐 ≡ 𝑔 𝑡 ( m o d 𝑝 ) g a c ≡ g t ( mod p ) 方程两边同时取离散对数得到
𝑎 𝑐 ≡ 𝑡 ( m o d 𝜑 ( 𝑝 ) ) a c ≡ t ( mod φ ( p ) ) 我们可以通过 BSGS 求解 𝑔 𝑡 ≡ 𝑏 ( m o d 𝑝 ) g t ≡ b ( mod p ) 得到 𝑡 t ,于是这就转化成了一个线性同余方程的问题。这样也可以解出 𝑐 c ,求出 𝑥 x 的一个特解 𝑥 0 ≡ 𝑔 𝑐 ( m o d 𝑝 ) x 0 ≡ g c ( mod p ) .
找到所有解 在知道 𝑥 0 ≡ 𝑔 𝑐 ( m o d 𝑝 ) x 0 ≡ g c ( mod p ) 的情况下,我们想得到原问题的所有解。首先我们知道 𝑔 𝜑 ( 𝑝 ) ≡ 1 ( m o d 𝑝 ) g φ ( p ) ≡ 1 ( mod p ) ,于是可以得到
∀ 𝑡 ∈ 𝐙 , 𝑥 𝑎 ≡ 𝑔 𝑐 ⋅ 𝑎 + 𝑡 ⋅ 𝜑 ( 𝑝 ) ≡ 𝑏 ( m o d 𝑝 ) ∀ t ∈ Z , x a ≡ g c ⋅ a + t ⋅ φ ( p ) ≡ b ( mod p ) 于是得到所有解为
∀ 𝑡 ∈ 𝐙 , 𝑎 ∣ 𝑡 ⋅ 𝜑 ( 𝑝 ) , 𝑥 ≡ 𝑔 𝑐 + 𝑡 ⋅ 𝜑 ( 𝑝 ) 𝑎 ( m o d 𝑝 ) ∀ t ∈ Z , a ∣ t ⋅ φ ( p ) , x ≡ g c + t ⋅ φ ( p ) a ( mod p ) 对于上面这个式子,显然有 𝑎 ( 𝑎 , 𝜑 ( 𝑝 ) ) ∣ 𝑡 a ( a , φ ( p ) ) ∣ t . 因此我们设 𝑡 = 𝑎 ( 𝑎 , 𝜑 ( 𝑝 ) ) ⋅ 𝑖 t = a ( a , φ ( p ) ) ⋅ i ,得到
∀ 𝑖 ∈ 𝐙 , 𝑥 ≡ 𝑔 𝑐 + 𝜑 ( 𝑝 ) ( 𝑎 , 𝜑 ( 𝑝 ) ) ⋅ 𝑖 ( m o d 𝑝 ) ∀ i ∈ Z , x ≡ g c + φ ( p ) ( a , φ ( p ) ) ⋅ i ( mod p ) 这就是原问题的所有解。
实现 下面的代码实现的找原根、离散对数解和原问题所有解的过程。
参考代码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 int gcd ( int a , int b ) { return a ? gcd ( b % a , a ) : b ; }
int powmod ( int a , int b , int p ) {
int res = 1 ;
while ( b > 0 ) {
if ( b & 1 ) res = res * a % p ;
a = a * a % p , b >>= 1 ;
}
return res ;
}
// Finds the primitive root modulo p
int generator ( int p ) {
vector < int > fact ;
int phi = p - 1 , n = phi ;
for ( int i = 2 ; i * i <= n ; ++ i ) {
if ( n % i == 0 ) {
fact . push_back ( i );
while ( n % i == 0 ) n /= i ;
}
}
if ( n > 1 ) fact . push_back ( n );
for ( int res = 2 ; res <= p ; ++ res ) {
bool ok = true ;
for ( int factor : fact ) {
if ( powmod ( res , phi / factor , p ) == 1 ) {
ok = false ;
break ;
}
}
if ( ok ) return res ;
}
return -1 ;
}
// This program finds all numbers x such that x^k=a (mod n)
int main () {
int n , k , a ;
scanf ( "%d %d %d" , & n , & k , & a );
if ( a == 0 ) return puts ( "1 \n 0" ), 0 ;
int g = generator ( n );
// Baby-step giant-step discrete logarithm algorithm
int sq = ( int ) sqrt ( n + .0 ) + 1 ;
vector < pair < int , int >> dec ( sq );
for ( int i = 1 ; i <= sq ; ++ i )
dec [ i - 1 ] = { powmod ( g , i * sq * k % ( n - 1 ), n ), i };
sort ( dec . begin (), dec . end ());
int any_ans = -1 ;
for ( int i = 0 ; i < sq ; ++ i ) {
int my = powmod ( g , i * k % ( n - 1 ), n ) * a % n ;
auto it = lower_bound ( dec . begin (), dec . end (), make_pair ( my , 0 ));
if ( it != dec . end () && it -> first == my ) {
any_ans = it -> second * sq - i ;
break ;
}
}
if ( any_ans == -1 ) return puts ( "0" ), 0 ;
// Print all possible answers
int delta = ( n - 1 ) / gcd ( k , n - 1 );
vector < int > ans ;
for ( int cur = any_ans % delta ; cur < n - 1 ; cur += delta )
ans . push_back ( powmod ( g , cur , n ));
sort ( ans . begin (), ans . end ());
printf ( "%zu \n " , ans . size ());
for ( int answer : ans ) printf ( "%d " , answer );
}
扩展篇(扩展 BSGS) 对 𝑎 , 𝑏 , 𝑚 ∈ 𝐙 + a , b , m ∈ Z + ,求解
𝑎 𝑥 ≡ 𝑏 ( m o d 𝑚 ) a x ≡ b ( mod m ) 其中 𝑎 , 𝑚 a , m 不一定互质。
当 ( 𝑎 , 𝑚 ) = 1 ( a , m ) = 1 时,在模 𝑚 m 意义下 𝑎 a 存在逆元,因此可以使用 BSGS 算法求解。于是我们想办法让他们变得互质。
具体地,设 𝑑 1 = ( 𝑎 , 𝑚 ) d 1 = ( a , m ) . 如果 𝑑 1 ∤ 𝑏 d 1 ∤ b ,则原方程无解。否则我们把方程同时除以 𝑑 1 d 1 ,得到
𝑎 𝑑 1 ⋅ 𝑎 𝑥 − 1 ≡ 𝑏 𝑑 1 ( m o d 𝑚 𝑑 1 ) a d 1 ⋅ a x − 1 ≡ b d 1 ( mod m d 1 ) 如果 𝑎 a 和 𝑚 𝑑 1 m d 1 仍不互质就再除,设 𝑑 2 = ( 𝑎 , 𝑚 𝑑 1 ) d 2 = ( a , m d 1 ) . 如果 𝑑 2 ∤ 𝑏 𝑑 1 d 2 ∤ b d 1 ,则方程无解;否则同时除以 𝑑 2 d 2 得到
𝑎 2 𝑑 1 𝑑 2 ⋅ 𝑎 𝑥 − 2 ≡ 𝑏 𝑑 1 𝑑 2 ( m o d 𝑚 𝑑 1 𝑑 2 ) a 2 d 1 d 2 ⋅ a x − 2 ≡ b d 1 d 2 ( mod m d 1 d 2 ) 同理,这样不停的判断下去,直到 𝑎 ⟂ 𝑚 𝑑 1 𝑑 2 ⋯ 𝑑 𝑘 a ⟂ m d 1 d 2 ⋯ d k .
记 𝐷 = ∏ 𝑘 𝑖 = 1 𝑑 𝑖 D = ∏ i = 1 k d i ,于是方程就变成了这样:
𝑎 𝑘 𝐷 ⋅ 𝑎 𝑥 − 𝑘 ≡ 𝑏 𝐷 ( m o d 𝑚 𝐷 ) a k D ⋅ a x − k ≡ b D ( mod m D ) 由于 𝑎 ⟂ 𝑚 𝐷 a ⟂ m D ,于是推出 𝑎 𝑘 𝐷 ⟂ 𝑚 𝐷 a k D ⟂ m D . 这样 𝑎 𝑘 𝐷 a k D 就有逆元了,于是把它丢到方程右边,这就是一个普通的 BSGS 问题了,于是求解 𝑥 − 𝑘 x − k 后再加上 𝑘 k 就是原方程的解啦。
注意,不排除解小于等于 𝑘 k 的情况,所以在消因子之前做一下 Θ ( 𝑘 ) Θ ( k ) 枚举,直接验证 𝑎 𝑖 ≡ 𝑏 ( m o d 𝑚 ) a i ≡ b ( mod m ) ,这样就能避免这种情况。
习题 本页面部分内容以及代码译自博文 Дискретное извлечение корня 与其英文翻译版 Discrete Root 。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。
参考资料 Discrete logarithm - Wikipedia 潘承洞,潘承彪。初等数论。 冯克勤。初等数论及其应用。 本页面最近更新:2024/10/9 22:38:42 ,更新历史 发现错误?想一起完善? 在 GitHub 上编辑此页! 本页面贡献者:Ir1d , StudyingFather , sshwy , Steaunk , Great-designer , H-J-Granger , Enter-tainer , MegaOwIer , countercurrent-time , Henry-ZHR , Konano , ksyx , NachtgeistW , ouuan , stevebraveman , Tiphereth-A , Xeonacid , Alpha1022 , AngelKitty , CCXXXI , Chrogeek , ChungZH , cjsoft , diauweb , Early0v0 , ezoixx130 , FFjet , GavinZhengOI , GekkaSaori , Gesrua , hly1204 , hsfzLZH1 , iamtwz , isdanni , Kelatte , kxccc , Lampese , LovelyBuggies , lychees , Makkiy , Marcythm , mgt , minghu6 , P-Y-Y , Peanut-Tang , PotassiumWings , purple-vine , SamZhangQingChuan , SukkaW , Suyun514 , weiyong1024 , xyf007 , YOYO-UIAT 本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用