Berlekamp's algorithm
Berlekamp's algorithm factors a square-free polynomial over a finite field using linear algebra. The residues modulo with form a vector space over , the Berlekamp subalgebra, whose dimension equals the number of irreducible factors of . It is the null space of , where is the matrix of the Frobenius map , so comes from one Gaussian elimination. Any element of the space that is not a constant splits through greatest common divisors[1]. The algorithm, published by Berlekamp in 1967, was the first polynomial-time factorization method for small fields, and together with Hensel lifting it made factorization of integer polynomials practical[7].
Overview
Factoring a polynomial over or is usually done by factoring it modulo a prime and lifting the factors. Over there are only finitely many polynomials of each degree, so factoring by trial division is possible in principle but takes time exponential in the degree. Berlekamp's algorithm reduces the problem to linear algebra over , with a cost polynomial in the degree and in . The input is assumed square-free, which square-free decomposition arranges beforehand without any factoring.
The Berlekamp subalgebra
Let be square-free with distinct monic irreducible factors of degrees . By the Chinese remainder theorem[10],
The Frobenius map is -linear on the ring on the left, since in characteristic and for . In each field its fixed points are exactly the elements of . So the fixed points in the whole ring are the residues that are constant modulo every :
The number of irreducible factors is therefore a dimension, and it can be computed without finding a single factor.
The matrix Q
On the basis , the Frobenius map has the matrix whose row holds the coefficients of . A residue , written as a row vector of coefficients, is fixed exactly when , so
The first row of is always zero, since the constant 1 is fixed, which accounts for the trivial solution. The matrix was considered by Petr in 1937[6], and is sometimes called the Petr–Berlekamp matrix.
Splitting
Every is a vector of constants in the product decomposition, with . Since , is divisible by and[1][3]:
where the factor for is the product of the with . If is not a constant, two of its components differ, and at least two of the gcds are nontrivial. Running through a basis of and splitting the current factors with each basis vector separates all factors, because for any two factors some basis vector has different components on them.
Example
Polynomials over F_p, as in the square-free decomposition article.
(* Polynomials over the prime field F_p, as arrays of coefficients,constant term first, with no trailing zeros. *)module Make (P : sig val p : int end) = structlet p = P.plet md x = ((x mod p) + p) mod plet rec pw b e = if e = 0 then 1 else let h = pw (b * b mod p) (e / 2) in if e land 1 = 1 then h * b mod p else hlet inv a = pw (md a) (p - 2)let trim a = let n = ref (Array.length a) in while !n > 0 && a.(!n - 1) = 0 do decr n done; Array.sub a 0 !nlet make l = trim (Array.of_list (List.map md l))let deg a = Array.length a - 1let zero = [||] and one = [| 1 |]let x_plus c = make [ c; 1 ]let lead a = a.(deg a)let add a b = trim (Array.init (max (Array.length a) (Array.length b)) (fun i ->md ((if i < Array.length a then a.(i) else 0) + if i < Array.length b then b.(i) else 0)))let neg a = Array.map (fun c -> md (-c)) alet sub a b = add a (neg b)let scale k a = trim (Array.map (fun c -> md (k * c)) a)let mul a b =if a = zero || b = zero then zero else beginlet r = Array.make (Array.length a + Array.length b - 1) 0 inArray.iteri (fun i x -> Array.iteri (fun j y -> r.(i + j) <- (r.(i + j) + x * y) mod p) b) a;trim r endlet rec pow a e = if e = 0 then one else mul a (pow a (e - 1))let divmod a b =let q = Array.make (max 0 (deg a - deg b + 1)) 0 and r = Array.copy a inlet il = inv (lead b) infor i = deg a - deg b downto 0 dolet c = r.(i + deg b) * il mod p inq.(i) <- c;Array.iteri (fun j y -> r.(i + j) <- md (r.(i + j) - c * y)) bdone;(trim q, trim r)let div a b = fst (divmod a b)let rem a b = snd (divmod a b)let monic a = if a = zero then a else scale (inv (lead a)) alet rec gcd a b = if b = zero then monic a else gcd b (rem a b)let deriv a = trim (Array.init (max 0 (Array.length a - 1)) (fun i -> md ((i + 1) * a.(i + 1))))(* If a' = 0, every exponent is a multiple of p, and since c^p = c inF_p, a is the p-th power of the polynomial with those coefficients. *)let pth_root a = trim (Array.init (deg a / p + 1) (fun i -> a.(i * p)))(* Printed with coefficients in (-p/2, p/2], highest degree first. *)let to_string a =if a = zero then "0" elselet signed c = if c > p / 2 then c - p else c inlet terms = List.rev (List.filter (fun (_, c) -> c <> 0) (List.mapi (fun i c -> (i, signed c)) (Array.to_list a))) inlet mono i c =let k = abs c inlet cs = if k = 1 && i > 0 then "" else string_of_int k inmatch i with 0 -> string_of_int k | 1 -> cs ^ "x" | _ -> cs ^ "x^" ^ string_of_int i inString.concat "" (List.mapi (fun n (i, c) ->(if n = 0 then (if c < 0 then "-" else "") else if c < 0 then " - " else " + ") ^ mono i c) terms)end
The Q matrix, its null space, and splitting with gcd(g, v - s).
(* Berlekamp's algorithm over F_p, for monic square-free f. *)module Make (P : sig val p : int end) = structinclude Fpoly.Make (P)let rec powmod a e f = if e = 0 then one elselet h = powmod (rem (mul a a) f) (e / 2) f inif e land 1 = 1 then rem (mul h a) f else h(* Row i of Q holds the coefficients of x^(ip) mod f. *)let q_matrix f =let n = deg f inlet xp = powmod (make [ 0; 1 ]) p f inlet row = ref one inArray.init n (fun _ ->let r = Array.init n (fun j -> if j < Array.length !row then !row.(j) else 0) inrow := rem (mul !row xp) f; r)(* A basis of { v : v (Q - I) = 0 }, by Gaussian elimination on thetranspose of Q - I. *)let kernel q =let n = Array.length q inlet a = Array.init n (fun i -> Array.init n (fun j -> md (q.(j).(i) - if i = j then 1 else 0))) inlet pivot_col = Array.make n (-1) inlet row = ref 0 infor col = 0 to n - 1 domatch List.find_opt (fun r -> a.(r).(col) <> 0) (List.init (n - !row) (fun k -> !row + k)) with| None -> ()| Some r ->let t = a.(r) in a.(r) <- a.(!row); a.(!row) <- t;let iv = inv a.(!row).(col) ina.(!row) <- Array.map (fun x -> x * iv mod p) a.(!row);for r' = 0 to n - 1 doif r' <> !row && a.(r').(col) <> 0 then beginlet k = a.(r').(col) ina.(r') <- Array.mapi (fun j x -> md (x - k * a.(!row).(j))) a.(r')enddone;pivot_col.(!row) <- col; incr rowdone;let pivots = Array.to_list (Array.sub pivot_col 0 !row) inlet free = List.filter (fun c -> not (List.mem c pivots)) (List.init n Fun.id) inList.map (fun fc ->let v = Array.make n 0 inv.(fc) <- 1;List.iteri (fun r pc -> v.(pc) <- md (- a.(r).(fc))) pivots;trim v) free(* Split with gcd(g, v - s) for every s in F_p, until there are r factors. *)let factor f =let basis = kernel (q_matrix f) inlet r = List.length basis inlet factors = ref [ f ] inList.iter (fun v ->if deg v > 0 thenfactors := List.concat_map (fun g ->if List.length !factors >= r || deg g <= 1 then [ g ] elselet parts = List.filter (fun h -> deg h > 0)(List.init p (fun s -> gcd g (sub v (make [ s ])))) inif List.length parts > 1 then parts else [ g ]) !factors) basis;(r, List.sort compare !factors)end
Knuth's example over F_13.
module B = Berl.Make (struct let p = 13 end)open B
Running it.
f = x^8 + x^6 - 3x^4 - 3x^3 - 5x^2 + 2x - 5, gcd(f, f') = 1Q - I:0 0 0 0 0 0 0 02 0 7 11 10 12 5 113 6 3 3 0 4 7 24 3 6 4 1 6 2 32 11 8 8 2 1 3 116 11 8 6 2 6 10 95 11 7 10 0 11 6 123 3 12 5 0 11 9 11kernel basis (3 vectors):1x^6 + 5x^5 - 4x^4 + 5x^2 + 5xx^7 - x^5 - 3x^4 - 4x^3 - 2x^2 - 4xr = 3 factors:x + 3 (its own r = 1)x^3 - 5x^2 + 4x - 1 (its own r = 1)x^4 + 2x^3 + 3x^2 + 4x + 6 (its own r = 1)product equals f: true
This is the example Knuth works through in Seminumerical Algorithms[3]. The rank of is 5, so the null space has dimension 3 and the polynomial has three irreducible factors, of degrees 1, 3 and 4. Each factor on its own gives , which confirms its irreducibility.
Checking the count
Over all monic polynomials of degree 6 can be checked. For every square-free one, the dimension of the null space is compared with the number of irreducible factors found by trial division:
An exhaustive check over F_3.
(* For every monic polynomial of degree 6 over F_3: when it is square-free,compare dim ker(Q - I) with the number of irreducible factors found bytrial division. *)module B = Berl.Make (struct let p = 3 end)open Blet monics d = (* all monic polynomials of degree d *)List.init (int_of_float (3. ** float d)) (fun k ->let c = Array.make (d + 1) 1 inlet k = ref k infor i = 0 to d - 1 do c.(i) <- !k mod 3; k := !k / 3 done;c)let irreducibles = (* monic irreducibles of degree 1 to 3, by trial division *)List.fold_left (fun acc d ->acc @ List.filter (fun f -> List.for_all (fun g -> rem f g <> zero) acc) (monics d)) [] [ 1; 2; 3 ]let count_factors f =let rec go f = function| [] -> if deg f > 0 then 1 else 0| g :: gs -> if deg g > deg f then go f [] else if rem f g = zero then 1 + go (div f g) (g :: gs) else go f gsingo f irreducibles
Running it.
729 monic polynomials of degree 6 over F_3, 486 square-freedim ker(Q - I) equals the number of irreducible factors for all of them: true1 factor : 116 polynomials2 factors: 226 polynomials3 factors: 127 polynomials4 factors: 17 polynomials
That the count needs only a rank makes the algorithm useful even when factors are found by other means. The Cantor–Zassenhaus algorithm shares none of its code, so comparing the number of factors it finds with is an independent check on an implementation.
Large fields
Trying every costs gcds per split, which is impractical for large . Berlekamp's 1970 paper gave methods for large fields[2], and the randomized approach now standard takes a random and computes one gcd. For odd , each component of a random is uniform on , and is for about half of the nonzero values and for the other half:
which is a proper factor with probability at least about one half[4]. This is the same splitting step as in the Cantor–Zassenhaus algorithm, applied to an element of instead of a random polynomial of the right degree[5].
The randomized split over F_1000003.
(* For large p, trying every s in F_p is too slow. A random element v ofthe Berlekamp subalgebra has v^((p-1)/2) equal to +1 or -1 in eachcomponent, independently, so gcd(g, v^((p-1)/2) - 1) splits g withprobability about 1/2. *)module B = Berl.Make (struct let p = 1_000_003 end)open Blet () = Random.init 5let split_random basis g =let v = List.fold_left (fun acc b -> add acc (scale (Random.int p) b)) zero basis inlet w = sub (powmod (rem v g) ((p - 1) / 2) g) one ingcd g wlet rec factor_rand basis f =if List.length (kernel (q_matrix f)) = 1 then [ f ]elselet rec try_ n = let h = split_random basis f inif deg h > 0 && deg h < deg f then (h, n) else try_ (n + 1) inlet h, _ = try_ 1 infactor_rand basis h @ factor_rand basis (div f h)
Running it.
f = x^10 + 7x^9 + 5x^8 + 41x^7 + 49x^6 + 64x^5 + 115x^4 + 81x^3 + 93x^2 + 122x + 70 over F_1000003, square-free: truer = 5x + 7x + 410588x - 410588x^3 + 3x + 5x^4 + x + 1product equals f: true
Complexity
Building takes the power , computed by repeated squaring in multiplications modulo , followed by multiplications by it. The null space costs field operations by Gaussian elimination. Deterministic splitting costs and randomized splitting an expected , so the randomized algorithm runs in time polynomial in and [4]. The cubic linear algebra dominates for large degrees. Kaltofen and Lobo replaced it with Wiedemann's black-box linear algebra, which only multiplies vectors by [8], and Kaltofen and Shoup gave algorithms subquadratic in the degree[9].
Uses
Factoring polynomials over the integers reduces modulo a prime for which the polynomial remains square-free, factors there, and lifts the factors to a larger modulus by Hensel lifting, as proposed by Zassenhaus[7]; the lifted factors are then recombined by trial or, in modern systems, by lattice reduction. The Swinnerton-Dyer polynomials, which split into many factors modulo every prime, are the standard hard case for the recombination step. Finite-field factorization is also used in coding theory, where is factored to construct cyclic codes, and in cryptography.
History
Petr studied the matrix of the Frobenius map in 1937[6]. Berlekamp, working on the decoding of error-correcting codes at Bell Labs, published the factorization algorithm in 1967[1] and methods for large fields in 1970[2]. Zassenhaus combined it with Hensel lifting to factor integer polynomials in 1969[7], Knuth presented it in Seminumerical Algorithms[3], and Cantor and Zassenhaus's 1981 algorithm replaced the linear algebra with exponentiation for most purposes[5].
see also
- 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.
- 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.
- Hensel liftingHensel lifting turns a factorization of a polynomial modulo a prime p into a factorization modulo p², p⁴ and so on, as long as the factors modulo p are coprime. Each step solves a linear equation using the Bézout relation between the factors, and in quadratic form it doubles the precision, the p-adic analogue of Newton's method. It is the core of Zassenhaus's algorithm for factoring integer polynomials: factor modulo a small prime, lift until the modulus exceeds twice the Landau–Mignotte bound, and recombine the lifted factors into true factors over the integers.
- 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.
further reading
- [1]E. R. Berlekamp, “Factoring polynomials over finite fields”, Bell System Technical Journal 46 (1967).
- [2]E. R. Berlekamp, “Factoring polynomials over large finite fields”, Mathematics of Computation 24 (1970).
- [3]D. E. Knuth, The Art of Computer Programming, Vol. 2, §4.6.2, Addison-Wesley (3rd ed., 1997).
- [4]J. von zur Gathen, J. Gerhard, Modern Computer Algebra, ch. 14, Cambridge University Press (3rd ed., 2013).
- [5]D. G. Cantor, H. Zassenhaus, “A new algorithm for factoring polynomials over finite fields”, Mathematics of Computation 36 (1981).
- [6]K. Petr, “Über die Reduzibilität eines Polynoms mit ganzzahligen Koeffizienten nach einem Primzahlmodul”, Časopis pro pěstování matematiky a fysiky 66 (1937).
- [7]H. Zassenhaus, “On Hensel factorization, I”, Journal of Number Theory 1 (1969).
- [8]E. Kaltofen, A. Lobo, “Factoring high-degree polynomials by the black box Berlekamp algorithm”, ISSAC (1994).
- [9]E. Kaltofen, V. Shoup, “Subquadratic-time factoring of polynomials over finite fields”, Mathematics of Computation 67 (1998).
- [10]R. Lidl, H. Niederreiter, Finite Fields, Cambridge University Press (2nd ed., 1997).
last updated