Square-free decomposition
The square-free decomposition of a polynomial writes it as a product of powers of polynomials that are square-free and pairwise coprime, , so that is the product of the irreducible factors that occur exactly times. Computing it does not require factoring: the greatest common divisor of and its derivative contains every repeated factor with its multiplicity reduced by one, so a sequence of gcds and exact divisions separates the multiplicities[3]. Yun's algorithm does this at a cost comparable to a single gcd[1]. The decomposition is the first step of polynomial factorization, of partial fraction decomposition and of symbolic integration of rational functions. Over fields of characteristic , a -th power has zero derivative and the algorithm must also take -th roots[5].
Definition
A polynomial is square-free if no irreducible polynomial divides it twice. Over a field, every nonzero polynomial has a decomposition
with the leading coefficient, and it is unique. If is the factorization into distinct monic irreducibles, then . Some may be 1. The square-free part of is , the product of the distinct irreducible factors.
Derivatives and repeated factors
The method rests on the derivative. If then , so divides . In characteristic zero does not divide when and are coprime, so exactly divides the gcd[3]:
In particular is square-free exactly when , and dividing by the gcd gives the square-free part. Repeating the idea separates the multiplicities.
Musser's algorithm
Let and , the square-free part. Then is the product of the factors of multiplicity at least 2, and those of multiplicity exactly 1, which is . Replacing by and by shifts every multiplicity down by one, and the next round yields [2].
Yun's algorithm
Yun rearranged the computation so that every gcd after the first directly produces one [1]:
stopping when . The invariant is that and , so the gcd is exactly . The degrees of and shrink as factors are removed, and Yun showed that the total cost is of the same order as computing once[1][3].
Implementation
Polynomials over the prime field F_p.
(* 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
Musser's and Yun's algorithms over F_p with p = 1000003, larger than any degree used, so the characteristic-zero reasoning applies.
module F = Fpoly.Make (struct let p = 1_000_003 end)open F(* Cost measure: the total degree of the arguments of every gcd. *)let gcd_work = ref 0let gcd a b = gcd_work := !gcd_work + deg a + deg b; gcd a b(* Musser: c = gcd(f, f') holds every repeated factor once less; eachround takes the part of w that divides c, and what does not is thefactor of the current multiplicity. *)let musser f =let c = ref (gcd f (deriv f)) and w = ref (div f (gcd f (deriv f))) and i = ref 1 and out = ref [] ingcd_work := !gcd_work - (deg f + deg (deriv f));while deg !w > 0 dolet y = gcd !w !c inlet z = div !w y inif deg z > 0 then out := (z, !i) :: !out;w := y; c := div !c y; incr idone;List.rev !out(* Yun: after one gcd with f', each round's gcd has the next factor asits answer, and the arguments shrink as fast as the factors do. *)let yun f =let a0 = gcd f (deriv f) inlet b = ref (div f a0) and c = ref (div (deriv f) a0) inlet d = ref (sub !c (deriv !b)) and i = ref 1 and out = ref [] inwhile deg !b > 0 dolet a = gcd !b !d inif deg a > 0 then out := (a, !i) :: !out;b := div !b a;c := div !d a;d := sub !c (deriv !b);incr idone;List.rev !outlet expand fs = List.fold_left (fun acc (g, i) -> mul acc (pow g i)) one fslet show fs = String.concat " * " (List.map (fun (g, i) -> Printf.sprintf "(%s)^%d" (to_string g) i) fs)
Running it.
f has degree 17Yun: (x + 1)^1 * (x + 2)^2 * (x^3 - 3x^2 + x - 3)^3 * (x + 5)^5Musser: (x + 1)^1 * (x + 2)^2 * (x^3 - 3x^2 + x - 3)^3 * (x + 5)^5both multiply back to f: true truetotal degree of gcd arguments: Musser 70, Yun 61
The polynomial is . The two factors of multiplicity 3 come out together as : square-free decomposition groups factors by multiplicity and does not split them further, which is the job of factorization. Yun's version does less gcd work on the same input.
Positive characteristic
Over a field of characteristic the derivative of is . Any polynomial in has zero derivative, and the argument above fails for every factor whose multiplicity is divisible by : such a factor divides with its full multiplicity, and can be itself. Over a finite field every polynomial with zero derivative is a -th power, because the Frobenius map is a bijection on the coefficients[8]:
The algorithm for finite fields therefore runs the loop above, which finds the factors whose multiplicity is not divisible by , and recurses on the -th root of what remains, multiplying the multiplicities it finds by [3][4]:
Yun's algorithm used unchanged over F_3, and the version that takes p-th roots.
module F = Fpoly.Make (struct let p = 3 end)open Flet yun f =let a0 = gcd f (deriv f) inlet b = ref (div f a0) and c = ref (div (deriv f) a0) inlet d = ref (sub !c (deriv !b)) and i = ref 1 and out = ref [] inwhile deg !b > 0 dolet a = gcd !b !d inif deg a > 0 then out := (a, !i) :: !out;b := div !b a; c := div !d a; d := sub !c (deriv !b); incr idone;List.rev !out(* Characteristic p: the loop finds the factors whose multiplicity is notdivisible by p; what is left over, c, has zero derivative, so it is ap-th power, and the algorithm recurses on its p-th root. *)let rec sqf f =let c = ref (gcd f (deriv f)) and w = ref (div f (gcd f (deriv f))) and i = ref 1 and out = ref [] inwhile deg !w > 0 dolet y = gcd !w !c inlet z = div !w y inif deg z > 0 then out := (z, !i) :: !out;w := y; c := div !c y; incr idone;let rest = if deg !c > 0 then List.map (fun (g, k) -> (g, k * p)) (sqf (pth_root !c)) else [] inList.sort (fun (_, a) (_, b) -> compare a b) (List.rev !out @ rest)let expand fs = List.fold_left (fun acc (g, i) -> mul acc (pow g i)) one fslet show fs = if fs = [] then "(nothing)" else String.concat " * " (List.map (fun (g, i) -> Printf.sprintf "(%s)^%d" (to_string g) i) fs)
Running it.
(x-1)^9 = x^9 - 1f' = 0Yun, as in characteristic 0: (nothing) (multiplies back: false)with p-th roots: (x - 1)^9 (multiplies back: true)(x^2+1)^3 (x-1)^4 (x+1) = x^11 - x^9 - x^8 + x^6 + x^5 - x^3 - x^2 + 1f' = -x^10 + x^7 - x^4 + xYun, as in characteristic 0: (x^2 - 1)^1 (multiplies back: false)with p-th roots: (x + 1)^1 * (x^2 + 1)^3 * (x - 1)^4 (multiplies back: true)
For the derivative vanishes, and the characteristic-zero algorithm returns nothing. In the second example is invisible to the derivative, and the unmodified algorithm finds only a wrong factor. Over fields of characteristic that are not perfect, such as , -th roots need not exist, and Gianni and Trager gave algorithms for that case[5].
Over the integers and rationals
Over the algorithms apply directly, but the gcds suffer from coefficient growth in the intermediate remainders. Practical systems work with primitive polynomials over , compute the gcds with subresultant sequences or modular methods, and divide exactly. A polynomial over the integers that is square-free stays square-free modulo all but finitely many primes, those dividing its discriminant, which is what lets a factorizer reduce modulo a suitable prime before Berlekamp's algorithm and Hensel lifting.
Uses
Polynomial factorization over finite fields and over the integers begins with square-free decomposition, since the algorithms that follow, distinct-degree factorization, Berlekamp and Cantor–Zassenhaus, require square-free input[6]. In symbolic integration, the denominator of a rational function is decomposed first, so that Hermite reduction can remove the parts of multiplicity above one without factoring, and partial fraction decomposition can be carried out on the square-free factors[7]. Real root isolation also works on the square-free part, since a repeated root is a common root of the polynomial and its derivative.
History
Using the gcd with the derivative to find repeated factors is classical. Musser gave the algorithm above in his 1971 thesis on polynomial factorization[2], and Horowitz used square-free decomposition in rational function integration in the same year[7]. Yun's 1976 paper gave the improved algorithm and the analysis of its cost[1]. Gianni and Trager treated fields of positive characteristic that are not perfect in 1996[5].
see also
- 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.
- 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.
- 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.
- Hermite reductionHermite reduction is the step of symbolic integration that splits the integral of a rational function A/D into a rational function and an integral whose denominator is square-free, using only gcds and the extended Euclidean algorithm, with no factorization of D. The rational part of the antiderivative comes entirely from the repeated factors of D, and each reduction step lowers their multiplicity by one. What remains integrates to a sum of logarithms, whose coefficients are the roots of the Rothstein–Trager resultant. Together the two steps integrate any rational function over a field of characteristic zero exactly, introducing algebraic numbers only where the answer needs them.
- 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.
- Risch algorithmThe Risch algorithm is a decision procedure for integration in finite terms: given an elementary function, it either finds an elementary antiderivative or proves that none exists. It rests on Liouville's theorem, which says that an elementary antiderivative, if one exists, is a function of the same field plus constant multiples of logarithms. The algorithm writes the integrand in a tower of logarithmic, exponential and algebraic extensions of the rational functions and works down it, using Hermite reduction, the Rothstein–Trager resultant and the Risch differential equation y′ + fy = g. Its transcendental case is completely algorithmic; its algebraic case is much harder; and both are decision procedures only relative to deciding whether a constant is zero.
referenced by
further reading
- [1]D. Y. Y. Yun, “On square-free decomposition algorithms”, SYMSAC (1976).
- [2]D. R. Musser, Algorithms for Polynomial Factorization, PhD thesis, University of Wisconsin (1971).
- [3]J. von zur Gathen, J. Gerhard, Modern Computer Algebra, §14.6, Cambridge University Press (3rd ed., 2013).
- [4]K. O. Geddes, S. R. Czapor, G. Labahn, Algorithms for Computer Algebra, ch. 8, Kluwer (1992).
- [5]P. Gianni, B. Trager, “Square-free algorithms in positive characteristic”, Applicable Algebra in Engineering, Communication and Computing 7 (1996).
- [6]D. E. Knuth, The Art of Computer Programming, Vol. 2, §4.6.2, Addison-Wesley (3rd ed., 1997).
- [7]E. Horowitz, “Algorithms for partial fraction decomposition and rational function integration”, SYMSAM (1971).
- [8]R. Lidl, H. Niederreiter, Finite Fields, Cambridge University Press (2nd ed., 1997).
last updated