Skip to main content

cas_poly/
gcd.rs

1//! 多变元除法与 gcd(M3,设计 D3)。
2//!
3//! - [`Poly::div_rem`]:域系数(ℚ)下的主单项式除法(多除子),
4//!   Gröbner 归约的原语;余式的主单项式不被任何除式主单项式整除。
5//! - [`Poly::gcd`]:递归**本原 PRS**——把 ℚ[x₁..xₙ] 视作 R[x₁]
6//!   (R = ℚ[x₂..xₙ]),伪除法(只乘不除系数,环上精确)+ 每步取本原
7//!   部分控制系数膨胀;系数环上的内容与公因子递归到一元(Euclid)。
8//! - 规范化:本原(ℚ 内容归一)且主项系数为正——gcd 在相差 ℚ 常数
9//!   意义下唯一,规范化后完全确定。
10
11use crate::{MonOrder, Poly, PolyRing, cmp_monomials};
12use cas_domain::{Integer, Rational};
13use std::collections::HashMap;
14use std::sync::Arc;
15
16impl PolyRing {
17    /// 去掉第 idx 个变元后的系数环(同项序),供递归 gcd。
18    fn tail(&self, idx: usize) -> Arc<PolyRing> {
19        let vars: Vec<Box<str>> = self
20            .vars
21            .iter()
22            .enumerate()
23            .filter(|(i, _)| *i != idx)
24            .map(|(_, v)| v.clone())
25            .collect();
26        PolyRing::new(vars, self.order)
27    }
28}
29
30impl Poly<Rational> {
31    /// 主单项式除法。确定性:每步消去被除式当前**最大**主单项式,
32    /// 多除子按给定次序匹配。
33    pub fn div_rem(&self, divs: &[&Self]) -> (Vec<Self>, Self) {
34        for d in divs {
35            assert!(!d.is_zero(), "除式为零");
36            assert_eq!(&*self.ring, &*d.ring, "多项式须属于同一 PolyRing");
37        }
38        let n = self.ring.nvars();
39        let order = self.ring.order;
40        // 各除式的主单项式(项序最大项 = 升序存储的末项;注意 terms() 是升序)
41        let divs_lt: Vec<(Vec<u32>, Rational)> = divs
42            .iter()
43            .map(|d| {
44                let (e, c) = d.terms().last().expect("除式非零");
45                (e.to_vec(), c.clone())
46            })
47            .collect();
48        let mut qs: Vec<HashMap<Vec<u32>, Rational>> = vec![HashMap::new(); divs.len()];
49        let mut rem: HashMap<Vec<u32>, Rational> = HashMap::new();
50        let mut p: HashMap<Vec<u32>, Rational> =
51            self.terms().map(|(e, c)| (e.to_vec(), c.clone())).collect();
52
53        while let Some((lt_e, lt_c)) = leading(&p, order) {
54            let mut matched = None;
55            for (i, (le, lc)) in divs_lt.iter().enumerate() {
56                if (0..n).all(|j| lt_e[j] >= le[j]) {
57                    let qe: Vec<u32> = lt_e.iter().zip(le).map(|(&a, &b)| a - b).collect();
58                    let qc = lt_c.div(lc).expect("主系数非零");
59                    matched = Some((i, qe, qc));
60                    break;
61                }
62            }
63            match matched {
64                Some((i, qe, qc)) => {
65                    let qslot = qs[i].entry(qe.clone()).or_insert_with(Rational::zero);
66                    *qslot = qslot.add(&qc);
67                    for (de, dc) in divs[i].terms() {
68                        let key: Vec<u32> = de.iter().zip(&qe).map(|(&a, &b)| a + b).collect();
69                        let slot = p.entry(key).or_insert_with(Rational::zero);
70                        *slot = slot.sub(&qc.mul(dc));
71                    }
72                }
73                None => {
74                    let slot = rem.entry(lt_e.clone()).or_insert_with(Rational::zero);
75                    *slot = slot.add(&lt_c);
76                    p.remove(&lt_e);
77                    continue;
78                }
79            }
80            p.retain(|_, c| !c.is_zero());
81        }
82
83        let qs: Vec<Poly<Rational>> = qs
84            .into_iter()
85            .map(|q| Poly::from_terms(self.ring.clone(), q))
86            .collect();
87        let r = Poly::from_terms(self.ring.clone(), rem);
88        (qs, r)
89    }
90
91    /// 精确除法:g 整除 self 时返回商,否则 None。
92    pub fn exact_div(&self, g: &Self) -> Option<Self> {
93        let (mut qs, r) = self.div_rem(&[g]);
94        if r.is_zero() && qs.len() == 1 {
95            qs.pop()
96        } else {
97            None
98        }
99    }
100
101    /// 最大公因子(本原规范化:ℚ 内容归一、主项系数为正)。
102    /// gcd(0, g) = normalize(g);gcd(c₁, c₂) = 1(ℚ 上常数互素)。
103    pub fn gcd(&self, other: &Self) -> Self {
104        assert_eq!(&*self.ring, &*other.ring, "多项式须属于同一 PolyRing");
105        self.gcd_raw(other).normalize_primitive()
106    }
107
108    fn gcd_raw(&self, other: &Self) -> Self {
109        if self.is_zero() {
110            return other.clone();
111        }
112        if other.is_zero() {
113            return self.clone();
114        }
115        if self.ring.nvars() == 1 {
116            // ℚ[x]:Euclid
117            let (mut a, mut b) = (self.clone(), other.clone());
118            while !b.is_zero() {
119                let (_, r) = a.div_rem(&[&b]);
120                a = b;
121                b = r;
122            }
123            return a;
124        }
125        // 多变元:视作 R[x₁],R = ℚ[x₂..xₙ],递归本原 PRS
126        let ring0 = self.ring.tail(0);
127        let ua = Uni::from_poly(self, &ring0);
128        let ub = Uni::from_poly(other, &ring0);
129        let ca = ua.content();
130        let cb = ub.content();
131        let c = ca.gcd_raw(&cb);
132        let mut a = ua.exact_div_content(&ca);
133        let mut b = ub.exact_div_content(&cb);
134        while !b.is_zero() {
135            let r = a.pseudo_rem(&b);
136            if r.is_zero() {
137                a = b;
138                break;
139            }
140            let cr = r.content();
141            let r = r.exact_div_content(&cr);
142            a = b;
143            b = r;
144        }
145        // a 为主变元视角的本原 gcd;乘回内容公因子并回嵌全部变元
146        let g = a.to_poly(self.ring.clone());
147        let c_full = embed_tail(&c, &self.ring.clone());
148        g.mul(&c_full)
149    }
150
151    /// 本原规范化:除以 ℚ 内容使分子整系、分子 gcd 为 1,主项(项序最大
152    /// 项)系数取正。零多项式原样。
153    pub fn normalize_primitive(&self) -> Self {
154        if self.is_zero() {
155            return self.clone();
156        }
157        let mut d_lcm = Integer::one();
158        for (_, c) in self.terms() {
159            d_lcm = lcm(&d_lcm, &c.den());
160        }
161        let mut m = Integer::zero();
162        for (_, c) in self.terms() {
163            let scaled = c.num().mul(&d_lcm.div_exact(&c.den()));
164            m = if m.is_zero() { scaled } else { m.gcd(&scaled) };
165        }
166        if m.is_zero() {
167            m = Integer::one();
168        }
169        // p' = p · d_lcm / m;符号按主项(项序最大,即迭代末项)
170        let mut terms: Vec<(Vec<u32>, Rational)> = self
171            .terms()
172            .map(|(e, c)| {
173                let scaled = c.mul(&Rational::from_integer(&d_lcm));
174                let v = scaled.div(&Rational::from_integer(&m)).expect("内容整除");
175                (e.to_vec(), v)
176            })
177            .collect();
178        if let Some((_, lc)) = terms.last() {
179            if lc.is_negative() {
180                for (_, c) in &mut terms {
181                    *c = c.neg();
182                }
183            }
184        }
185        Poly::from_terms(self.ring.clone(), terms)
186    }
187}
188
189/// map 中按项序最大的非零项。
190fn leading(p: &HashMap<Vec<u32>, Rational>, order: MonOrder) -> Option<(Vec<u32>, Rational)> {
191    p.iter()
192        .filter(|(_, c)| !c.is_zero())
193        .max_by(|(a, _), (b, _)| cmp_monomials(order, a, b))
194        .map(|(e, c)| (e.clone(), c.clone()))
195}
196
197fn lcm(a: &Integer, b: &Integer) -> Integer {
198    if a.is_zero() || b.is_zero() {
199        return Integer::zero();
200    }
201    a.div_exact(&a.gcd(b)).mul(b)
202}
203
204/// 系数环(少一个变元)上的多项式回嵌到全环:指数向量前插 0。
205fn embed_tail(p: &Poly<Rational>, ring: &Arc<PolyRing>) -> Poly<Rational> {
206    let items: Vec<(Vec<u32>, Rational)> = p
207        .terms()
208        .map(|(e, c)| {
209            let mut full = Vec::with_capacity(e.len() + 1);
210            full.push(0);
211            full.extend_from_slice(e);
212            (full, c.clone())
213        })
214        .collect();
215    Poly::from_terms(ring.clone(), items)
216}
217
218/// 主变元视角的稀疏一元表示:系数为剩余变元上的 Poly(ring0)。
219#[derive(Clone)]
220pub(crate) struct Uni {
221    /// 按主变元次数降序;系数非零。
222    terms: Vec<(u32, Poly<Rational>)>,
223    ring0: Arc<PolyRing>,
224}
225
226impl Uni {
227    pub(crate) fn from_poly(p: &Poly<Rational>, ring0: &Arc<PolyRing>) -> Self {
228        let mut by_deg: HashMap<u32, Vec<(Vec<u32>, Rational)>> = HashMap::new();
229        for (e, c) in p.terms() {
230            by_deg
231                .entry(e[0])
232                .or_default()
233                .push((e[1..].to_vec(), c.clone()));
234        }
235        let mut terms: Vec<(u32, Poly<Rational>)> = by_deg
236            .into_iter()
237            .map(|(d, items)| (d, Poly::from_terms(ring0.clone(), items)))
238            .filter(|(_, p)| !p.is_zero())
239            .collect();
240        terms.sort_by_key(|(d, _)| std::cmp::Reverse(*d));
241        Uni {
242            terms,
243            ring0: ring0.clone(),
244        }
245    }
246
247    fn is_zero(&self) -> bool {
248        self.terms.is_empty()
249    }
250
251    fn deg(&self) -> u32 {
252        self.terms[0].0
253    }
254
255    fn lc(&self) -> &Poly<Rational> {
256        &self.terms[0].1
257    }
258
259    /// 系数(剩余变元多项式)的公因子——递归 gcd。
260    pub(crate) fn content(&self) -> Poly<Rational> {
261        let mut acc = Poly::zero(self.ring0.clone());
262        for (_, c) in &self.terms {
263            acc = acc.gcd_raw(c);
264        }
265        acc
266    }
267
268    /// 各系数被内容整除(构造保证整除)。
269    pub(crate) fn exact_div_content(&self, c: &Poly<Rational>) -> Self {
270        let terms: Vec<(u32, Poly<Rational>)> = self
271            .terms
272            .iter()
273            .map(|(d, p)| (*d, p.exact_div(c).expect("内容必整除")))
274            .collect();
275        Uni {
276            terms,
277            ring0: self.ring0.clone(),
278        }
279    }
280
281    /// 伪余式:r ← lc(b)·r − lc(r)·x^Δ·b,循环至 deg(r) < deg(b)。
282    /// 只用环乘法(无系数除法),在 R[x₁] 上精确。
283    fn pseudo_rem(&self, b: &Uni) -> Self {
284        let db = b.deg();
285        let mut r = self.clone();
286        while !r.is_zero() && r.deg() >= db {
287            let shift = r.deg() - db;
288            let lcb = b.lc().clone();
289            let lcr = r.lc().clone();
290            let mut acc: HashMap<u32, Poly<Rational>> = HashMap::new();
291            for (d, c) in &r.terms {
292                let slot = acc
293                    .entry(*d)
294                    .or_insert_with(|| Poly::zero(self.ring0.clone()));
295                *slot = slot.add(&lcb.mul(c));
296            }
297            for (d, c) in &b.terms {
298                let slot = acc
299                    .entry(d + shift)
300                    .or_insert_with(|| Poly::zero(self.ring0.clone()));
301                *slot = slot.sub(&lcr.mul(c));
302            }
303            let mut terms: Vec<(u32, Poly<Rational>)> =
304                acc.into_iter().filter(|(_, p)| !p.is_zero()).collect();
305            terms.sort_by_key(|(d, _)| std::cmp::Reverse(*d));
306            // 注:朴素伪除 lc(b)^k 的系数膨胀在 2 变元高次(deg ≳ 6)时
307            // 不可算。内层本原化可治但把递归 gcd 扇出放大到小输入超时,
308            // 正解为子结果式 PRS(挂 M5b,见 DESIGN §18)。
309            r = Uni {
310                terms,
311                ring0: self.ring0.clone(),
312            };
313        }
314        r
315    }
316
317    /// 回嵌全环:指数向量 = [主变元次数, 剩余指数…]。
318    pub(crate) fn to_poly(&self, ring: Arc<PolyRing>) -> Poly<Rational> {
319        let mut items: Vec<(Vec<u32>, Rational)> = Vec::new();
320        for (d, coef) in &self.terms {
321            for (e, c) in coef.terms() {
322                let mut full = Vec::with_capacity(ring.nvars());
323                full.push(*d);
324                full.extend_from_slice(e);
325                items.push((full, c.clone()));
326            }
327        }
328        Poly::from_terms(ring, items)
329    }
330}
331
332#[cfg(test)]
333mod tests {
334    use super::*;
335    use crate::MonOrder;
336
337    fn ringn(names: &[&str]) -> Arc<PolyRing> {
338        PolyRing::new(names.iter().copied(), MonOrder::DegRevLex)
339    }
340
341    fn rat(n: i64, d: i64) -> Rational {
342        Rational::from_ints(&Integer::from_i64(n), &Integer::from_i64(d)).unwrap()
343    }
344
345    fn t(e: Vec<u32>, c: (i64, i64)) -> (Vec<u32>, Rational) {
346        (e, rat(c.0, c.1))
347    }
348
349    #[test]
350    fn 一元_gcd() {
351        let ring = ringn(&["x"]);
352        // (x^2 - 1) 与 (x - 1) 的 gcd = x - 1
353        let f = Poly::from_terms(ring.clone(), [t(vec![2], (1, 1)), t(vec![0], (-1, 1))]);
354        let g = Poly::from_terms(ring.clone(), [t(vec![1], (1, 1)), t(vec![0], (-1, 1))]);
355        let d = f.gcd(&g);
356        assert_eq!(d.nterms(), 2);
357        assert_eq!(d.terms().next().unwrap(), (&[0][..], &rat(-1, 1)));
358        assert_eq!(d.terms().nth(1).unwrap(), (&[1][..], &rat(1, 1)));
359
360        // 常数与任何式互素 → 1
361        let c = Poly::from_terms(ring.clone(), [t(vec![0], (4, 6))]);
362        assert!(g.gcd(&c).is_constant());
363
364        // gcd(f·h, g·h) = h·gcd(f,g)(h 本原)
365        let h = Poly::from_terms(ring.clone(), [t(vec![3], (2, 1)), t(vec![1], (1, 1))]);
366        let d1 = f.mul(&h).gcd(&g.mul(&h));
367        let d2 = f.gcd(&g).mul(&h).normalize_primitive();
368        assert_eq!(d1, d2);
369    }
370
371    #[test]
372    fn 多元_gcd_六变元() {
373        let ring = ringn(&["q1", "q2", "q3", "p1", "p2", "p3"]);
374        let mk = |e: [u32; 6], c: (i64, i64)| t(e.to_vec(), c);
375        // h = p1*q2 + q3(本原),f、g 与 h 互素
376        let h = Poly::from_terms(
377            ring.clone(),
378            [
379                mk([0, 1, 0, 1, 0, 0], (1, 1)),
380                mk([0, 0, 1, 0, 0, 0], (1, 1)),
381            ],
382        );
383        let f = Poly::from_terms(
384            ring.clone(),
385            [
386                mk([1, 0, 0, 0, 0, 0], (1, 1)),
387                mk([0, 0, 0, 0, 1, 0], (-2, 3)),
388            ],
389        );
390        let g = Poly::from_terms(
391            ring.clone(),
392            [
393                mk([0, 0, 0, 0, 0, 1], (5, 1)),
394                mk([2, 0, 0, 1, 0, 0], (1, 2)),
395            ],
396        );
397        let d = f.mul(&h).gcd(&g.mul(&h));
398        assert_eq!(d, h);
399        // 互质对 → 1
400        assert!(f.gcd(&g).is_constant());
401    }
402
403    #[test]
404    fn 精确除法与除法不变量() {
405        let ring = ringn(&["x", "y"]);
406        let mk = |e: [u32; 2], c: (i64, i64)| t(e.to_vec(), c);
407        let f = Poly::from_terms(
408            ring.clone(),
409            [mk([2, 0], (1, 1)), mk([1, 1], (3, 2)), mk([0, 2], (-1, 5))],
410        );
411        let g = Poly::from_terms(ring.clone(), [mk([1, 0], (2, 1)), mk([0, 1], (1, 1))]);
412        // f·g 精确除以 g == f
413        let q = f.mul(&g).exact_div(&g);
414        assert_eq!(q.as_ref(), Some(&f));
415        // 不整除 → None(x 除以 y)
416        let x = Poly::from_terms(ring.clone(), [mk([1, 0], (1, 1))]);
417        let y = Poly::from_terms(ring.clone(), [mk([0, 1], (1, 1))]);
418        assert_eq!(x.exact_div(&y), None);
419        // 除法不变量:p = q·d + r
420        let p = f.mul(&g);
421        let (qs, r) = p.div_rem(&[&f]);
422        let lhs = qs[0].mul(&f).add(&r);
423        assert_eq!(lhs, p);
424        assert!(r.is_zero());
425    }
426
427    #[test]
428    fn 随机性质() {
429        // gcd(f·h, g·h) = gcd(f,g)·h(h 本原)× 随机组
430        let ring = ringn(&["q1", "q2", "q3", "p1", "p2", "p3"]);
431        let mut xs = 4242u64;
432        let mut nxt = move || {
433            xs ^= xs << 13;
434            xs ^= xs >> 7;
435            xs ^= xs << 17;
436            xs
437        };
438        fn rand_poly(
439            nxt: &mut dyn FnMut() -> u64,
440            deg: u64,
441            ring: &Arc<PolyRing>,
442        ) -> Poly<Rational> {
443            let items: Vec<(Vec<u32>, Rational)> = (0..4)
444                .map(|_| {
445                    let r = nxt();
446                    let e: Vec<u32> = (0..6)
447                        .map(|k| ((r >> (k * 4)) % (deg + 1) % 3) as u32)
448                        .collect();
449                    (e, rat((r % 13) as i64 - 6, 1))
450                })
451                .collect();
452            Poly::from_terms(ring.clone(), items)
453        }
454        for _ in 0..10 {
455            let f = rand_poly(&mut nxt, 2, &ring);
456            let g = rand_poly(&mut nxt, 2, &ring);
457            // h 取两项整数系数(本原)
458            let h = Poly::from_terms(
459                ring.clone(),
460                [
461                    t(vec![1, 0, 0, 0, 0, 0], (1, 1)),
462                    t(vec![0, 0, 0, 1, 1, 0], ((nxt() % 7) as i64, 1)),
463                ],
464            );
465            if f.is_zero() || g.is_zero() || h.is_zero() {
466                continue;
467            }
468            let d1 = f.mul(&h).gcd(&g.mul(&h));
469            let d2 = f.gcd(&g).mul(&h).normalize_primitive();
470            assert_eq!(d1, d2, "f={f:?} g={g:?} h={h:?}");
471            // gcd 确为公因子
472            assert!(f.mul(&h).exact_div(&d1).is_some());
473            assert!(g.mul(&h).exact_div(&d1).is_some());
474        }
475    }
476
477    #[test]
478    fn 本原规范化() {
479        let ring = ringn(&["x", "y"]);
480        // (2/3)x + 4 → 本原化 = x + 6(内容 2/3 除尽)
481        let p = Poly::from_terms(ring.clone(), [t(vec![1, 0], (2, 3)), t(vec![0, 0], (4, 1))]);
482        let n = p.normalize_primitive();
483        let items: Vec<_> = n.terms().map(|(e, c)| (e.to_vec(), c.clone())).collect();
484        assert_eq!(
485            items,
486            vec![(vec![0, 0], rat(6, 1)), (vec![1, 0], rat(1, 1))]
487        );
488        // 主项为负 → 整体取正
489        let p = Poly::from_terms(ring.clone(), [t(vec![1, 0], (-1, 1))]);
490        let n = p.normalize_primitive();
491        assert_eq!(n.terms().next().unwrap().1, &rat(1, 1));
492    }
493}