很久以前就想学这个算法了,但当时迫于知识水平没法理解,就一直咕到现在了,最近重新深刻学习了一下。
本文的大部分内容来自文章 [3] 和 Chee Keng Yap 在 1999 年出版的书 [4]。
我们解决了什么
我们得到了如下问题的 O ( n log 2 n ) O ( n log 2 n ) 做法:
计算两个多项式的 gcd
给出 f ( x ) , h ( x ) f ( x ) , h ( x ) ,找到 g ( x ) g ( x ) 满足 f ( x ) g ( x ) ≡ 1 ( mod h ( x ) ) f ( x ) g ( x ) ≡ 1 ( mod h ( x ) )
给出 P ( x ) , Q ( x ) P ( x ) , Q ( x ) ,找出两个多项式 A ( x ) , B ( x ) A ( x ) , B ( x ) 满足 A ( x ) P ( x ) + B ( x ) Q ( x ) = gcd ( P ( x ) , Q ( x ) ) A ( x ) P ( x ) + B ( x ) Q ( x ) = gcd ( P ( x ) , Q ( x ) )
换句话说,我们得到了大部分需要用到多项式 gcd 的问题的优秀算法。
通过修改这个算法可以得到最小线性递推式问题的 O ( n log 2 n ) O ( n log 2 n ) 算法,可以参见 [6]。
算法原理
这里以多项式 gcd 为例阐述做法。考虑一下常见的多项式 gcd 的实现:
计算 R ( x ) = P ( x ) mod Q ( x ) R ( x ) = P ( x ) mod Q ( x )
将问题递归到 gcd ( Q ( x ) , R ( x ) ) gcd ( Q ( x ) , R ( x ) ) 。
由于每次递归子问题的时候最劣情况多项式的度数只会减少 1 1 ,这导致我们的复杂度寄了。实际上,不需要精心构造,大部分情况下随机数据都会卡满这个上界。
考虑一下我们的复杂度都浪费在哪里了:计算取模时我们先计算了除法,而计算除法可以只使用最高的若干次项得到答案 ,这部分的复杂度是对的,而真正破坏复杂度分析的部分是后面的计算取模。纵使除法的答案可能非常小,我们还是使用了 O ( max ( deg P , deg Q ) log ) O ( max ( deg P , deg Q ) log ) 的代价计算了取模。
一个简单的想法是,如果能规避掉取模,而是想办法直接得到除法的结果并计算答案就能让复杂度对起来。
什么是 Half-GCD
重新考虑一下多项式 gcd 的过程,实际上是构造了一个多项式序列 r − 2 , r − 1 , r 0 , r 1 , ⋯ r k r − 2 , r − 1 , r 0 , r 1 , ⋯ r k ,其中:
r − 2 = A ( x ) r − 2 = A ( x )
r − 1 = B ( x ) r − 1 = B ( x )
r i = r i − 2 − q i r i − 1 r i = r i − 2 − q i r i − 1 ,其中 q i = r i − 2 / r i − 1 q i = r i − 2 / r i − 1 。
r k = 0 r k = 0
我们可以不用考虑取模,而是把它写成一个线性变换:
[ 0 1 1 − q i ] [ r i − 2 r i − 1 ] = [ r i − 1 r i ] [ 0 1 1 − q i ] [ r i − 2 r i − 1 ] = [ r i − 1 r i ]
我们引入记号
⟨ Q ⟩ = [ Q 1 1 0 ] ⟹ ⟨ Q ⟩ − 1 = [ 0 1 1 − Q ] ⟨ Q ⟩ = [ Q 1 1 0 ] ⟹ ⟨ Q ⟩ − 1 = [ 0 1 1 − Q ]
于是无非是
⟨ q k ⟩ − 1 ⋯ ⟨ q 1 ⟩ − 1 ⟨ q 0 ⟩ − 1 [ A ( x ) B ( x ) ] = [ gcd ( A ( x ) , B ( x ) 0 ] ⟨ q k ⟩ − 1 ⋯ ⟨ q 1 ⟩ − 1 ⟨ q 0 ⟩ − 1 [ A ( x ) B ( x ) ] = [ gcd ( A ( x ) , B ( x ) 0 ]
于是我们只需要算出前面那堆东西乘起来的结果 M − 1 M − 1 ,然后把它作用到我们的初始值上就可以大力算出答案,这样就规避了中间的取模过程,这就是 Half-GCD 算法的基本思想。
具体地,Half-GCD 没有直接算最后的矩阵,它算出的是这样一个矩阵 M M :假设 deg A > deg B deg A > deg B 且 M − 1 [ A B ] = [ C D ] M − 1 [ A B ] = [ C D ] ,那么必须成立:
deg C ≥ ⌈ deg A 2 ⌉ > deg D deg C ≥ ⌈ deg A 2 ⌉ > deg D
相信算出了这个矩阵之后该怎么做大家都会:再算一步欧几里得 E = C mod D E = C mod D ,于是必然有 ⌈ deg A 2 ⌉ > deg D > deg E ⌈ deg A 2 ⌉ > deg D > deg E ,于是我们就把问题递归到了一个规模减半的问题,递归调用就可以算出答案。
现在的问题就是如何实现 Half-GCD。
Outline. 这里给出一个 Half-GCD 的形象理解:
假设我们现在要计算 h g c d ( A , B ) h g c d ( A , B ) ,我们设定某个阈值 m m ,然后把多项式拆开:
A = x m A 0 + A 1 A = x m A 0 + A 1
然后计算 M = h g c d ( A 0 , B 0 ) M = h g c d ( A 0 , B 0 ) ,并直接计算 [ A ′ B ′ ] = M − 1 [ A B ] [ A ′ B ′ ] = M − 1 [ A B ] 。由于前半部分的叙述「除法的结果只和最高次项有关」,我们可以大胆猜测这个变换应用于整体多项式的时候,低次项的影响可以不用考虑,即 deg A ′ deg A ′ 只和 deg A ′ 0 deg A 0 ′ 有关,而和 A 1 A 1 无关。更进一步地,在大部分情况下都应该有
deg A ′ = deg A ′ 0 + m deg A ′ = deg A 0 ′ + m
如果真的有这个性质,那么接下来的思路很简单了:假设原本 deg A = 2 m deg A = 2 m ,那么也就是说我们只需要按照 m m 做一次这种分治就可以得到 deg A ′ = 3 2 m + o ( m ) deg A ′ = 3 2 m + o ( m ) ,然后我们再按照 1 2 m 1 2 m 做一次这个分治,就可以得到 deg A ′′ = m + o ( m ) deg A ″ = m + o ( m ) ,也就是达成了我们的目标,而我们付出的代价仅有两次规模减半的调用,于是复杂度就对了!
下面叙述证明的细节。
Half-GCD 原理
首先我们介绍一个概念:regular 矩阵。
Definition. 称矩阵 M M 是 regular 的,当且仅当下面三者中至少一个成立:
换句话说,M M 必须是 ⟨ q 1 ⟩ ⟨ q 2 ⟩ ⋯ ⟨ q 1 ⟩ ⟨ q 2 ⟩ ⋯ 的形式。
然后有个引理
Lemma 1(ordering property). 设 M = [ p q r s ] M = [ p q r s ] 是一个 regular 的矩阵,则 M M 要么是单位矩阵,要么满足:
deg p ≥ max ( deg q , deg r ) ≥ min ( deg q , deg r ) ≥ deg s deg p > deg s deg p ≥ max ( deg q , deg r ) ≥ min ( deg q , deg r ) ≥ deg s deg p > deg s
Proof. 直接归纳即可。■ ◼
Definition. 我们称一个 regular 矩阵 M M 可以将 [ A B ] [ A B ] reduce 成 [ C D ] [ C D ] ,当且仅当
M − 1 [ A B ] = [ C D ] M − 1 [ A B ] = [ C D ]
并记做
[ A B ] M ⟶ [ C D ] [ A B ] ⟶ M [ C D ]
于是欧几里得算法的实质就是我们从 [ A B ] [ A B ] 开始,不断用 ⟨ q i ⟩ ⟨ q i ⟩ reduce 它直到变成 [ gcd ( A , B ) 0 ] [ gcd ( A , B ) 0 ] 。
然后我们直接给出 Half-GCD 算法的伪代码:
Algorithm Polynomial hgcd ( A , B ) : Input : A , B ∈ F p [ x ] , deg ( A ) > deg ( B ) ≥ 0 . Output : a regular matrix M which reduces ( A , B ) to ( C , D ) where deg C ≥ ⌈ deg A 2 ⌉ > deg D . 1 m ← ⌈ deg ( A ) 2 ⌉ ; 2 if deg ( B ) < m then return ( [ 1 0 0 1 ] ) ; 3 R ← hgcd ( A div x m , B div x m ) ; 4 [ C D ] ← R − 1 [ A B ] ; 5 if deg ( D ) < m then return ( R ) ; 6 [ Q E ] ← [ C div D C mod D ] ; 7 if deg ( E ) < m then return ( R ⟨ Q ⟩ ) ; 8 k ← 2 m − deg ( D ) ; 9 S ← hgcd ( D div x k , E div x k ) ; 10 return ( R ⟨ Q ⟩ S ) ; Algorithm Polynomial hgcd ( A , B ) : Input : A , B ∈ F p [ x ] , deg ( A ) > deg ( B ) ≥ 0 . Output : a regular matrix M which reduces ( A , B ) to ( C , D ) where deg C ≥ ⌈ deg A 2 ⌉ > deg D . 1 m ← ⌈ deg ( A ) 2 ⌉ ; 2 if deg ( B ) < m then return ( [ 1 0 0 1 ] ) ; 3 R ← hgcd ( A div x m , B div x m ) ; 4 [ C D ] ← R − 1 [ A B ] ; 5 if deg ( D ) < m then return ( R ) ; 6 [ Q E ] ← [ C div D C mod D ] ; 7 if deg ( E ) < m then return ( R ⟨ Q ⟩ ) ; 8 k ← 2 m − deg ( D ) ; 9 S ← hgcd ( D div x k , E div x k ) ; 10 return ( R ⟨ Q ⟩ S ) ;
然后分析它的正确性:
Lemma 2(Correctness Criteria). 设 A , B A , B 为两个多项式且 deg A > deg B deg A > deg B ,然后设 m m 是某个阈值,M M 是一个 regular 矩阵,然后定义如下几个东西:
A = x m A 0 + A 1 B = x m B 0 + B 1 M − 1 [ A 0 B 0 ] = [ A ′ 0 B ′ 0 ] M − 1 [ A 1 B 1 ] = [ A ′ 1 B ′ 1 ] A ′ = x m A ′ 0 + A ′ 1 B ′ = x m B ′ 0 + B ′ 1 A = x m A 0 + A 1 B = x m B 0 + B 1 M − 1 [ A 0 B 0 ] = [ A 0 ′ B 0 ′ ] M − 1 [ A 1 B 1 ] = [ A 1 ′ B 1 ′ ] A ′ = x m A 0 ′ + A 1 ′ B ′ = x m B 0 ′ + B 1 ′
那么如果满足
deg A ′ 0 > deg B ′ 0 deg A 0 ≤ 2 deg A ′ 0 deg A 0 ′ > deg B 0 ′ deg A 0 ≤ 2 deg A 0 ′
就一定有如下两个柿子成立:
deg A ′ = m + deg A ′ 0 deg B ′ ≤ m + max ( deg B ′ 0 , deg A 0 − deg A ′ 0 − 1 ) deg A ′ = m + deg A 0 ′ deg B ′ ≤ m + max ( deg B 0 ′ , deg A 0 − deg A 0 ′ − 1 )
特别地,上面两个柿子蕴含了
deg A ′ > deg B ′ deg A ′ > deg B ′
Proof.
建议先重新读一遍整个引理。
前半部分的一堆定义无非是我们把 A , B A , B 截取出来,然后对它们分别做变换再合并回去。中间的条件部分可以理解为 M M 实际上是 h g c d ( A 0 , B 0 ) h g c d ( A 0 , B 0 ) 的产物,然后后面的结论是关于两个多项式的度数的性质。
我们来证明它。设 M = [ P Q R S ] M = [ P Q R S ] ,首先注意到 deg A ′ 0 > deg B ′ 0 deg A 0 ′ > deg B 0 ′ 并且 A 0 = A ′ 0 P + B ′ 0 Q A 0 = A 0 ′ P + B 0 ′ Q ,那么通过 Lemma 1 可以推知
deg A 0 = deg A ′ 0 + deg P deg A 0 = deg A 0 ′ + deg P
然后又由于 deg A 0 ≤ 2 deg A ′ 0 deg A 0 ≤ 2 deg A 0 ′ ,所以有
deg P ≤ deg A ′ 0 deg P ≤ deg A 0 ′
注意到 M M 是 regular 矩阵,这意味着 det M = ± 1 det M = ± 1 ,于是
M − 1 = ± [ S − Q − R P ] M − 1 = ± [ S − Q − R P ]
于是有 A ′ 1 = ± ( A 1 S − B 1 Q ) A 1 ′ = ± ( A 1 S − B 1 Q ) ,那么就有
deg A ′ 1 ≤ max ( deg A 1 + deg S , deg B 1 + deg Q ) ≤ max ( deg A 1 , deg B 1 ) + max ( deg S , deg Q ) < m + deg P ≤ m + deg A ′ 0 = deg ( x m A ′ 0 ) deg A 1 ′ ≤ max ( deg A 1 + deg S , deg B 1 + deg Q ) ≤ max ( deg A 1 , deg B 1 ) + max ( deg S , deg Q ) < m + deg P ≤ m + deg A 0 ′ = deg ( x m A 0 ′ )
于是我们现在已经得到了第一个子式:
deg A ′ = m + deg A ′ 0 deg A ′ = m + deg A 0 ′
第二个子式的证明是类似的。■ ◼
这个引理告诉了我们什么:我们取高次项分治,然后把这个线性变换作用于整体多项式,得到的多项式的度数和低次项无关。也就是说,我们在上一节末尾的猜测确实是正确的!接下来只需要应用这个东西两次就可以得到真实答案。
我们来阐述一下具体的细节:
Theorem 1(HGCD Correctness). Algorithm HGCD is correct.
Proof.
我们重新写出我们的 reduce 的路线:
[ A B ] R ⟶ [ C D ] ⟨ Q ⟩ ⟶ [ D E ] S ⟶ [ D ′ E ′ ] [ A B ] ⟶ R [ C D ] ⟶ ⟨ Q ⟩ [ D E ] ⟶ S [ D ′ E ′ ]
返回的矩阵是 regular 是显然的,我们之后只用证明返回的多项式的次数是合法的。
显然度数是合法的
应用 Lemma 2,可以得到 deg C = m + deg A ′ 0 ≥ m deg C = m + deg A 0 ′ ≥ m ,于是也是合法的。
由于在第二处没有 return,于是一定有 deg D ≥ m > deg E deg D ≥ m > deg E ,于是也是合法的。
设 m ′ = deg A 0 = deg A − m , l = deg D m ′ = deg A 0 = deg A − m , l = deg D ,于是 k = 2 m − l k = 2 m − l 。
有个结论:
l ≥ k ≥ 0 l ≥ k ≥ 0
这是因为首先有 l ≥ m ≥ m + ( m − l ) = k l ≥ m ≥ m + ( m − l ) = k ,然后应用 Lemma 2 还有 l = deg D ≤ m + max ( deg B ′ 0 , deg A 0 − deg A ′ 0 + 1 ) l = deg D ≤ m + max ( deg B 0 ′ , deg A 0 − deg A 0 ′ + 1 ) ,于是 l ≤ m + max ( ⌈ m ′ 2 ⌉ − 1 , ⌊ m ′ 2 ⌋ + 1 ) ≤ m + ⌊ m ′ 2 ⌋ + 1 l ≤ m + max ( ⌈ m ′ 2 ⌉ − 1 , ⌊ m ′ 2 ⌋ + 1 ) ≤ m + ⌊ m ′ 2 ⌋ + 1 ,于是 l − m ≤ ⌊ m ′ 2 ⌋ + 1 ≤ m l − m ≤ ⌊ m ′ 2 ⌋ + 1 ≤ m ,那么 k = 2 m − l ≥ 0 k = 2 m − l ≥ 0 。
有了这个结论之后再来考虑一下第二次调用 hgcd 的过程,一定有
deg D ′ 0 ≥ ⌈ deg D 0 2 ⌉ > deg E ′ 0 deg D 0 ′ ≥ ⌈ deg D 0 2 ⌉ > deg E 0 ′
而 deg D 0 = l − k = 2 ( l − m ) deg D 0 = l − k = 2 ( l − m ) ,于是
deg D ′ 0 ≥ l − m > deg E ′ 0 deg D 0 ′ ≥ l − m > deg E 0 ′
再次应用 Lemma 2,有
deg D ′ = k + deg D ′ 0 ≥ k + l − m = m deg D ′ = k + deg D 0 ′ ≥ k + l − m = m
而
deg E ′ ≤ k + max ( deg E ′ 0 , deg D 0 − deg D ′ 0 − 1 ) ≤ k + max ( l − m − 1 , l − m − 1 ) = k + l − m − 1 = m − 1 deg E ′ ≤ k + max ( deg E 0 ′ , deg D 0 − deg D 0 ′ − 1 ) ≤ k + max ( l − m − 1 , l − m − 1 ) = k + l − m − 1 = m − 1
于是得证。■ ◼
关于复杂度:
Half-GCD 的复杂度显然是 T ( n ) = 2 T ( n 2 ) + O ( n log n ) = O ( n log 2 n ) T ( n ) = 2 T ( n 2 ) + O ( n log n ) = O ( n log 2 n )
于是 gcd 算法的复杂度就是 T ( n ) = T ( n 2 ) + O ( n log 2 n ) = O ( n log 2 n ) T ( n ) = T ( n 2 ) + O ( n log 2 n ) = O ( n log 2 n )
这是一份参考代码 ,实现的时候需要尤其注意多项式最高次项为 0 0 的 border case。
参考文献
[1] Half-GCD algorithm
[2] HALF-GCD 算法的阐述
[3] HGCD 算法的感性理解 & 伪代码(重置版)
[4] Chee Keng Yap. Fundamental Problems of Algorithmic Algebra.
[5] 彭雨翔: Introduction to Polynomials.
[6] 最短线性递推式求解与有理函数重建
注:[4] 和 [5] 可以在 U 群找到。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理