Hensel lifting
Hensel lifting is the process of refining a factorization modulo a prime into factorizations modulo higher and higher powers of [2]. If and and are coprime modulo , then for every there are unique factors and with . This is Hensel's lemma, from Kurt Hensel's theory of -adic numbers of 1908[3]. Each lifting step solves a linear congruence, and the quadratic version doubles the exponent each time, as Newton's method doubles the number of correct digits. Hans Zassenhaus made it the basis of polynomial factorization over the integers in 1969[1]: a factorization modulo a small prime, which is easy to find, is lifted until the modulus is large enough for the integer coefficients of the true factors, bounded by the Landau–Mignotte bound, to be read off.
Roots
The simplest case is a root. If and is invertible modulo , then the Newton step
is a root modulo , and the only one congruent to . The Taylor expansion shows why: with divisible by , the error term vanishes modulo .
Lifting the square root of 2 modulo 7, where 3² ≡ 2.
(* Hensel's lemma for a root: if f(r) = 0 mod p and f'(r) is a unit mod p,Newton's step r - f(r)/f'(r) doubles the number of correct p-adicdigits. Here f = x^2 - 2 and p = 7, since 3^2 = 9 = 2 mod 7. *)let base_digits p n r = (* the base-p digits of r, lowest first *)let rec go r k = if k = 0 then [] else Z.to_int (Z.erem r (Z.of_int p)) :: go (Z.div r (Z.of_int p)) (k - 1) ingo r n
Running it; the digits are base 7, the lowest on the right.
mod 7^1 r = 3 digits 3mod 7^2 r = 10 digits 13mod 7^4 r = 2166 digits 6213mod 7^8 r = 1802916 digits 21216213mod 7^16 r = 9611769806236 digits 2011266421216213mod 7^32 r = 891834753593197726755637001 digits 54365536623164112011266421216213
Factors
For a factorization with monic and , the error is divisible by . Corrections and change the product by modulo , so it suffices to solve , which the Bézout relation does: and , reduced so that the degrees do not grow. The Bézout coefficients and are lifted along with the factors[2].
One quadratic step, after von zur Gathen and Gerhard, Algorithm 15.10, together with linear lifting for comparison.
(* One quadratic step: from f = g h and s g + t h = 1 modulo m, with hmonic, to the same modulo m^2 (von zur Gathen and Gerhard, 15.10). *)let hensel_step m f g h s t =incr steps;let m = Z.mul m m inlet e = red m (sub f (mul g h)) inlet q, r = divmod_monic m (mulm m s e) h inlet g' = red m (add g (add (mul t e) (mul q g))) and h' = red m (add h r) inlet b = red m (sub (add (mul s g') (mul t h')) one) inlet c, d = divmod_monic m (mulm m s b) h' inlet s' = red m (sub s d) and t' = red m (sub t (add (mul t b) (mul c g'))) in(m, g', h', s', t')(* Lift f = g h mod p to modulo at least target, quadratically. *)let lift_two p target f g h =let pz = Z.of_int p inlet _, s, t = Modp.ext_gcd p (Modp.of_z p g) (Modp.of_z p h) inlet s = Array.map Z.of_int s and t = Array.map Z.of_int t inlet rec go m g h s t = if Z.geq m target then (m, g, h) elselet m, g, h, s, t = hensel_step m f g h s t in go m g h s t ingo pz (red pz g) (red pz h) s t(* One linear step: from modulo p^k to p^(k+1), with s and t fixed mod p. *)let lift_linear p target f g h =let pz = Z.of_int p inlet _, s, t = Modp.ext_gcd p (Modp.of_z p g) (Modp.of_z p h) inlet rec go m g h =if Z.geq m target then (m, g, h)else beginincr steps;let e = Modp.of_z p (Array.map (fun c -> Z.divexact c m) (sub (red (Z.mul m pz) f) (red (Z.mul m pz) (mul g h)))) inlet q, r = Modp.divmod p (Modp.mul p s e) (Modp.of_z p h) inlet dg = Modp.add p (Modp.mul p t e) (Modp.mul p q (Modp.of_z p g)) inlet up d = Array.map (fun c -> Z.mul m (Z.of_int c)) d inlet m' = Z.mul m pz ingo m' (red m' (add g (up dg))) (red m' (add h (up r)))end ingo pz (red pz g) (red pz h)
Lifting a factorization modulo 3.
open Zpoly(* Lifting the factorization of f = (x^2 - 2)(x^3 + 5x + 11) modulo 3. *)
Running it; coefficients are shown in the symmetric range.
f = x^5 + 3x^3 + 11x^2 - 10x - 22mod 3: (x^2 + 1) · (x^3 + 2x + 2)mod 3 x^3 - x - 1 x^2 + 1mod 9 x^3 - 4x + 2 x^2 - 2mod 81 x^3 + 5x + 11 x^2 - 2mod 6561 x^3 + 5x + 11 x^2 - 2mod 43046721 x^3 + 5x + 11 x^2 - 2
Modulo 3 the factors are not the true ones, but they are their images. Modulo 81 the symmetric representatives already equal the integer factors, and further steps leave them unchanged.
Linear and quadratic lifting
Linear lifting goes from to with and fixed modulo , so all its arithmetic on corrections is modulo the small prime; quadratic lifting goes from to but must also lift and and works with large numbers. Quadratic lifting needs logarithmically many steps.
Steps needed to lift a two-factor split modulo 101 past twice the coefficient bound of the product.
open Zpoly(* Steps needed to lift a two-factor split of a product of two randompolynomials past twice its coefficient bound: linear against quadratic. *)let random n = normalize (Array.init (n + 1) (fun i -> if i = n then Z.one else Z.of_int (Random.int 19 - 9)))
Running it.
degree target bits linear steps quadratic steps20 27 4 340 48 7 380 88 13 4160 169 25 5320 329 49 6
Since the work of a quadratic step grows with the size of the modulus, the total cost of quadratic lifting is dominated by its last step and is comparable to a few multiplications at full precision, which is asymptotically better than linear lifting[2]. Implementations often combine the two.
Factoring over the integers
Zassenhaus's algorithm factors a square-free primitive polynomial as follows[1][4]:
1. Choose a prime that does not divide the leading coefficient and for which is square-free. 2. Factor into irreducibles, by Berlekamp's algorithm[7] or Cantor–Zassenhaus. 3. Lift the factorization to modulus , where bounds the coefficients of any factor of times its leading coefficient. 4. Recombine: for subsets of the lifted factors of increasing size, multiply them together with the leading coefficient, take symmetric representatives, and test whether the result divides .
The whole algorithm, with lifting of several factors by splitting off one at a time.
(* Lift all monic factors u1..ur of f (mod p) at once, splitting off onefactor at a time. *)let lift_all p target f us =let lcf = Z.erem (lc f) target inlet rec go f = function| [] | [ _ ] -> [ ]| u :: rest ->let cof = List.fold_left (fun acc v -> red (Z.of_int p) (mul acc (Array.map Z.of_int v))) (const lcf) rest inlet m, g, h = lift_two p target f cof (Array.map Z.of_int u) inlet g = red target g and h = red target h inignore m;h :: (if List.length rest = 1 then(* the last cofactor, made monic *)let inv = Z.invert (lc g) target in [ red target (scale g inv) ]else go g rest) ingo (red target f) us(* Zassenhaus: factor a square-free primitive polynomial over Z. *)let trials = ref 0let factor_squarefree f =let rec pick p = if Modp.is_prime p && not (Z.equal (Z.erem (lc f) (Z.of_int p)) Z.zero)&& Modp.deg (Modp.gcd p (Modp.of_z p f) (Modp.deriv p (Modp.of_z p f))) = 0then p else pick (p + 2) inlet p = pick 3 inlet us = Cz.factor p (Modp.of_z p f) inlet bound = Z.mul (Z.abs (lc f)) (Z.mul (Z.bin (Z.of_int (deg f)) (deg f / 2)) (norm2_ceil f)) inlet rec target m = if Z.gt m (Z.shift_left bound 1) then m else target (Z.mul m (Z.of_int p)) inlet m = target (Z.of_int p) inlet lifted = if List.length us = 1 then [ Array.map Z.of_int (List.hd us) ] else lift_all p m f us in(* try subsets of increasing size *)let rec subsets k l = if k = 0 then [ [] ] else match l with| [] -> [] | x :: rest -> List.map (fun s -> x :: s) (subsets (k - 1) rest) @ subsets k rest inlet rec recombine f us k acc =if 2 * k > List.length us then (if deg f > 0 then f :: acc else acc)elsematch List.find_opt (fun s ->incr trials;let g = primitive (symmetric m (red m (List.fold_left (mulm m) (const (lc f)) s))) individes g f) (subsets k us) with| Some s ->let g = primitive (symmetric m (red m (List.fold_left (mulm m) (const (lc f)) s))) inrecombine (fst (divmod_exact f g)) (List.filter (fun u -> not (List.memq u s)) us) k (g :: acc)| None -> recombine f us (k + 1) accin(p, List.length us, recombine f lifted 1 [])
The factorizer above agrees with SymPy on more than a hundred random products tested. Each true factor over the integers is the product of some subset of the modular factors, and the recombination step can be exponential in their number; the Swinnerton-Dyer polynomials are the extreme case, irreducible over the integers but splitting into many small factors modulo every prime. Van Hoeij's algorithm of 2002 replaced subset enumeration with lattice reduction and made recombination polynomial in practice[9].
Multivariate lifting
The same idea works with a polynomial ideal in place of the prime: to factor , substitute a value for , factor the univariate image, and lift the factors modulo powers of . Musser and Wang and Rothschild developed this for multivariate factorization over the integers in 1975[6][5], and it is also the basis of the EEZ-GCD algorithm for multivariate gcds.
History
Hensel introduced the -adic numbers around 1897 and presented the lemma that bears his name in his book of 1908[3]. Zassenhaus proposed in 1969 to factor integer polynomials by lifting a modular factorization[1], combining it with Berlekamp's algorithm for factoring over finite fields[7]; the combination, the Berlekamp–Zassenhaus algorithm, became the standard method. Musser, Wang and Rothschild extended lifting to several variables in 1975[6][5], and van Hoeij's lattice-based recombination of 2002 removed the exponential worst case from practice[9].
see also
- Landau–Mignotte boundThe Landau–Mignotte bound limits the coefficients of any factor of an integer polynomial in terms of the polynomial itself: if g of degree k divides f, then each coefficient of g is at most binom(k, j) times the Euclidean norm of f, up to the ratio of leading coefficients, so all are below 2^k ‖f‖₂. It follows from Landau's inequality for the Mahler measure. Computer algebra algorithms use it to decide how large a modulus is needed to recover a factor or a gcd from its image modulo a prime power or a product of primes, as in Hensel lifting and the modular gcd. Factors can have larger coefficients than the polynomial they divide, so some such bound is necessary.
- Cantor–Zassenhaus algorithmThe Cantor–Zassenhaus algorithm is a randomized method for factoring polynomials over a finite field. After the input is made square-free, distinct-degree factorization separates the irreducible factors by degree, using the fact that x^(p^d) − x is the product of all monic irreducibles whose degree divides d. Each group, a product of factors of the same degree d, is then split by taking the gcd with a^((p^d − 1)/2) − 1 for a random polynomial a: by the Chinese remainder theorem, this power is +1 or −1 independently in each factor's field, so the gcd separates the factors with probability about one half. It runs in expected polynomial time and is the standard method in computer algebra systems.
- Swinnerton-Dyer polynomialThe Swinnerton-Dyer polynomials are the integer polynomials SD_k(x) = ∏(x ± √p₁ ± √p₂ ± … ± √p_k), taken over all 2^k sign choices, for the first k primes. They are irreducible over the integers but factor into polynomials of degree at most two modulo every prime, because their Galois group (ℤ/2)^k has no element of order greater than two. They are the worst case for the Berlekamp–Zassenhaus factoring algorithm: every prime yields at least 2^(k−1) modular factors, and before concluding that the polynomial is irreducible the algorithm must try exponentially many subsets of them. They motivated the lattice-based factoring algorithms of Lenstra, Lenstra and Lovász and of van Hoeij.
- Berlekamp's algorithmBerlekamp's algorithm factors a square-free polynomial f over a finite field F_p by linear algebra. The polynomials v with v^p = v modulo f form a vector space whose dimension is exactly the number r of irreducible factors of f; it is the null space of Q - I, where Q is the matrix of the Frobenius map v -> v^p. Any nonconstant v in it splits f as the product of gcd(f, v - s) over s in F_p. For large p a random element of the space and one exponentiation split f with probability about one half.
- Modular GCDThe modular GCD algorithm computes the greatest common divisor of two polynomials with integer coefficients by computing it modulo many machine-word primes, where coefficients never grow, and reconstructing the integer coefficients with the Chinese remainder theorem once the product of the primes exceeds twice a bound on them. Primes that divide the leading coefficients are skipped, unlucky primes that give an image of too high a degree are detected and discarded, and a final trial division certifies the result. The intermediate numbers are no larger than the answer, which makes it far faster than remainder-sequence methods; it was introduced by Brown in 1971.
- Partial fractionsA partial fraction decomposition rewrites a rational function n/d as a polynomial plus a sum of simpler fractions, one family for each power of an irreducible factor of the denominator: n/d = q + Σ c_ij / f_i^j with deg c_ij < deg f_i. It exists and is unique once the factorization of d is fixed. Splitting across coprime factors needs only the extended Euclidean algorithm, and the terms for a power f^e are the digits of an f-adic expansion, so computing it costs no more than the factorization of the denominator; with a square-free factorization, no factoring is needed at all. It is the basis of the integration of rational functions and of closed forms for rational generating functions.
- Square-free decompositionThe square-free decomposition of a polynomial writes it as f = f_1 f_2^2 f_3^3 ..., where the f_i are square-free and pairwise coprime, so f_i collects the irreducible factors of multiplicity i. It needs only greatest common divisors with the derivative, not factorization, because gcd(f, f') contains every repeated factor with its multiplicity reduced by one. Yun's algorithm computes it at the cost of about one gcd. In characteristic p a p-th power has zero derivative, and the algorithm must take p-th roots.
referenced by
further reading
- [1]H. Zassenhaus, “On Hensel factorization, I”, Journal of Number Theory 1 (1969).
- [2]J. von zur Gathen, J. Gerhard, Modern Computer Algebra, ch. 15, Cambridge University Press (3rd ed., 2013).
- [3]K. Hensel, Theorie der algebraischen Zahlen, Teubner (1908).
- [4]D. E. Knuth, The Art of Computer Programming, Vol. 2: Seminumerical Algorithms, §4.6.2, Addison-Wesley (3rd ed., 1997).
- [5]P. S. Wang, L. P. Rothschild, “Factoring multivariate polynomials over the integers”, Mathematics of Computation 29 (1975).
- [6]D. R. Musser, “Multivariate polynomial factorization”, Journal of the ACM 22 (1975).
- [7]E. R. Berlekamp, “Factoring polynomials over large finite fields”, Mathematics of Computation 24 (1970).
- [8]H. Cohen, A Course in Computational Algebraic Number Theory, §3.5, Springer (1993).
- [9]M. van Hoeij, “Factoring polynomials and the knapsack problem”, Journal of Number Theory 95 (2002).
last updated