Knuth's Algorithm D
Knuth's Algorithm D divides one multiple-precision integer by another. It is the long division taught in school, carried out in base on machine words called limbs, with one difficulty that the school version hides: each digit of the quotient has to be guessed. Algorithm D guesses it from the two leading limbs of the current remainder and the leading limb of the divisor. After the divisor is normalized so that its leading limb is at least , the guess is never too small and at most two too large; a test involving one more limb removes almost every error, and a rare add-back step corrects what remains[1]. Dividing an -limb number by an -limb number takes limb operations. The algorithm is given in the second volume of Knuth's The Art of Computer Programming, and is the division used, with variations, by most arbitrary-precision arithmetic libraries.
Overview
A multiple-precision integer is stored as a sequence of limbs, with . Addition, subtraction and multiplication by the schoolbook methods need only single-limb operations and carries. Division is harder because long division needs, at each step, the largest digit such that does not exceed the current partial remainder. In base 10 a person finds it by inspection and trial; in base or trial and error over candidates is out of the question. Algorithm D computes a close estimate with one double-limb division and corrects it in at most a few steps.
The algorithm
Let and with and . The algorithm computes and in these steps[1]:
D1. Normalize: multiply and by , chosen so that the leading limb of satisfies . gains a leading limb , possibly zero. The quotient is unchanged.
D2–D7. For , divide the -limb number by to obtain the quotient digit , and replace those limbs of by the remainder: estimate , refine it, multiply by and subtract, and if the result is negative decrease by one and add back.
D8. Unnormalize: the remainder is the low limbs of divided by .
Estimating the quotient digit
The estimate uses the two leading limbs of the partial remainder and the leading limb of the divisor:
Two facts make this work[1]. The estimate is never too small, , because the partial remainder is less than and the ignored limbs can only make the true quotient smaller. And if , the estimate is at most two too large:
Without normalization the error is unbounded in terms of the base: with the estimate ignores almost all of the divisor. Normalizing by a power of two is a shift and does not change the quotient, which is why it is done first. Pope and Stein had earlier analysed the effect of the leading digit of the divisor on such estimates[2].
Refinement
A second test with the next limbs of both numbers removes most of the remaining error before any multiplication is done. With :
stopping as soon as , since the test can then no longer succeed. It amounts to estimating with three limbs of the remainder and two of the divisor, and it removes every case in which is two too large and most in which it is one too large[1]. What is left is detected in the next step: if subtracting makes the partial remainder negative, was one too large, and adding back restores it.
Implementation
Natural numbers as arrays of limbs, with schoolbook addition and multiplication.
(* Natural numbers as little-endian arrays of limbs in base 2^bits, with noleading zero limbs. Everything except division is schoolbook. *)module Make (B : sig val bits : int end) = structlet bits = B.bitslet base = 1 lsl bitslet mask = base - 1let 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 of_int x = let rec go x acc = if x = 0 then acc else go (x lsr bits) ((x land mask) :: acc) in Array.of_list (List.rev (go x []))let compare a b =let la = Array.length a and lb = Array.length b inif la <> lb then Stdlib.compare la lbelse let rec go i = if i < 0 then 0 else if a.(i) <> b.(i) then Stdlib.compare a.(i) b.(i) else go (i - 1) in go (la - 1)let add a b =let n = max (Array.length a) (Array.length b) inlet r = Array.make (n + 1) 0 and c = ref 0 infor i = 0 to n - 1 dolet s = (if i < Array.length a then a.(i) else 0) + (if i < Array.length b then b.(i) else 0) + !c inr.(i) <- s land mask; c := s lsr bitsdone;r.(n) <- !c; trim rlet mul a b =let r = Array.make (Array.length a + Array.length b + 1) 0 inArray.iteri (fun i x ->let c = ref 0 inArray.iteri (fun j y -> let t = r.(i + j) + x * y + !c in r.(i + j) <- t land mask; c := t lsr bits) b;let k = ref (i + Array.length b) inwhile !c > 0 do let t = r.(!k) + !c in r.(!k) <- t land mask; c := t lsr bits; incr k done) a;trim r(* Multiply by a single limb and add a single limb: used for parsing. *)let mul_small_add a m d = add (mul a (of_int m)) (of_int d)let of_string s = String.fold_left (fun acc ch -> mul_small_add acc 10 (Char.code ch - 48)) [||] send
Algorithm D, with counters for how far each first estimate was from the true digit and for add-backs.
(* Knuth's Algorithm D: divide u (m + n limbs) by v (n >= 2 limbs). *)module Make (B : sig val bits : int end) = structinclude Nat.Make (B)type stats = { mutable qhat_over : int array; mutable add_backs : int; mutable digits : int }let stats = { qhat_over = Array.make 3 0; add_backs = 0; digits = 0 }let shift_left a s = (* by s < bits bits, keeping one extra limb *)let n = Array.length a inlet r = Array.make (n + 1) 0 infor i = n - 1 downto 0 dor.(i + 1) <- r.(i + 1) lor (a.(i) lsr (bits - s));r.(i) <- (a.(i) lsl s) land maskdone;if s = 0 then r.(n) <- 0;rlet divmod_small u d = (* one-limb divisor *)let q = Array.make (Array.length u) 0 and r = ref 0 infor i = Array.length u - 1 downto 0 dolet t = (!r lsl bits) lor u.(i) in q.(i) <- t / d; r := t mod ddone;(trim q, of_int !r)let divmod u v =let n = Array.length v inif n = 0 then raise Division_by_zeroelse if compare u v < 0 then ([||], u)else if n = 1 then divmod_small u v.(0)else begin(* D1. Normalize: shift so the divisor's top limb is at least base/2. *)let s = let rec go s x = if x >= base / 2 then s else go (s + 1) (x lsl 1) in go 0 v.(n - 1) inlet v = Array.sub (shift_left v s) 0 n and u = shift_left u s inlet m = Array.length u - n - 1 inlet q = Array.make (m + 1) 0 infor j = m downto 0 do(* D3. Estimate the quotient digit from the top two limbs of thecurrent remainder and the top limb of the divisor. *)let num = (u.(j + n) lsl bits) + u.(j + n - 1) inlet qhat = ref (num / v.(n - 1)) and rhat = ref (num mod v.(n - 1)) in(* Record how far the estimate is from the true digit, for thestatistics; the true digit is computed at the end. *)let estimate = min !qhat (base - 1) in(* Correct with the next limb: at most two decrements. *)let testing = ref true inwhile !testing && (!qhat >= base || !qhat * v.(n - 2) > (!rhat lsl bits) + u.(j + n - 2)) dodecr qhat;rhat := !rhat + v.(n - 1);if !rhat >= base then testing := false (* the test can no longer succeed *)done;(* D4. Multiply and subtract qhat * v from u[j .. j+n]. *)let borrow = ref 0 and carry = ref 0 infor i = 0 to n dolet p = (if i < n then !qhat * v.(i) else 0) + !carry incarry := p lsr bits;let t = u.(i + j) - (p land mask) - !borrow inif t < 0 then (u.(i + j) <- t + base; borrow := 1) else (u.(i + j) <- t; borrow := 0)done;(* D6. If the result went negative, qhat was one too large: add v back. *)if !borrow <> 0 then beginstats.add_backs <- stats.add_backs + 1;decr qhat;let c = ref 0 infor i = 0 to n dolet t = u.(i + j) + (if i < n then v.(i) else 0) + !c inu.(i + j) <- t land mask; c := t lsr bitsdoneend;q.(j) <- !qhat;stats.digits <- stats.digits + 1;let over = estimate - !qhat inif over <= 2 then stats.qhat_over.(over) <- stats.qhat_over.(over) + 1done;(* D8. Unnormalize the remainder. *)let r = Array.make n 0 infor i = 0 to n - 1 dor.(i) <- ((u.(i) lsr s) lor (if i + 1 <= n && s > 0 then (u.(i + 1) lsl (bits - s)) land mask else 0))done;(trim q, trim r)endlet to_string a =if Array.length a = 0 then "0"elselet rec go a acc = if Array.length a = 0 then acc else let q, r = divmod_small a 10 ingo q (string_of_int (if Array.length r = 0 then 0 else r.(0)) :: acc) inString.concat "" (go a [])end
Two divisions in base 2^28, both checked against Python's integers, and 100,000 random divisions checked by multiplying back.
10000000000000000000000000000000000000000 / 123456789012345678901q = 81000000729000006634r = 6661773269766170766q*b + r = a: true, r < b: true170141183460469231731687303715884105728 / 18446744073709551617q = 9223372036854775807r = 9223372036854775809q*b + r = a: true, r < b: truerandom divisions with q*b + r = a and r < b: 100000 of 100000
The second example, , is a case in which the estimate survives the refinement and the add-back step runs. Brinch Hansen described how easy it is to get the correction steps subtly wrong, and how rarely the wrong cases occur in testing: an add-back branch that is never exercised can hide a bug for years[3].
How often the corrections run
In a small base the rare cases become common enough to count. The following measures the first estimate, before refinement, and the add-back rate, over random operands in bases , and :
Statistics for 200,000 random divisions per base.
(* How far the first estimate of each quotient digit is from the truedigit, and how often the add-back step runs, for small bases where therare cases are frequent enough to count. *)module type BITS = sig val bits : int endlet run (module M : BITS) =let module N = D1_lib.Make (M) inRandom.init 7;let rand limbs = N.trim (Array.init limbs (fun _ -> Random.bits () land N.mask)) infor _ = 1 to 200_000 dolet b = rand (2 + Random.int 4) inlet a = rand (Array.length b + 1 + Random.int 6) inif Array.length b >= 2 then ignore (N.divmod a b)done;let s = N.stats inlet pct x = 100. *. float x /. float s.digits inPrintf.printf "base 2^%-2d %8d digits estimate exact %6.2f%% 1 too large %5.2f%% 2 too large %5.3f%% add-back %.4f%% (2/base = %.4f%%)\n"M.bits s.digits (pct s.qhat_over.(0)) (pct s.qhat_over.(1)) (pct s.qhat_over.(2)) (pct s.add_backs) (200. /. float N.base)
Running it.
base 2^4 871256 digits estimate exact 75.11% 1 too large 24.48% 2 too large 0.409% add-back 1.1786% (2/base = 12.5000%)base 2^8 899177 digits estimate exact 74.33% 1 too large 24.96% 2 too large 0.714% add-back 0.0790% (2/base = 0.7812%)base 2^16 900860 digits estimate exact 74.39% 1 too large 24.89% 2 too large 0.727% add-back 0.0003% (2/base = 0.0031%)
The first estimate is exact about three times in four, one too large about a quarter of the time, and two too large in under one per cent of cases, never more, as the bound requires. Knuth estimates the probability that the add-back step is needed as about [1]; with the refinement in place, the measured rate here is about ten times lower, close to in every base. In base it is negligible, which is exactly why that branch needs a test constructed to reach it.
Complexity and faster methods
Each of the quotient digits costs one multiply-and-subtract of limbs, so the algorithm uses limb operations, the same as schoolbook multiplication of the quotient by the divisor. For large operands, division is reduced to multiplication. Newton's iteration computes a reciprocal to the needed precision in a constant number of multiplications, so division costs where is the cost of multiplying -limb numbers[7]. Burnikel and Ziegler's recursive division splits the operands in halves and uses Karatsuba or faster multiplication in the recursive steps, with Algorithm D at the base[4]. For repeated reduction modulo the same number, Barrett reduction precomputes a scaled reciprocal[5] and Montgomery multiplication avoids division altogether by working in a transformed representation[6].
GMP uses schoolbook division, essentially Algorithm D, for small operands and switches to divide-and-conquer and Newton-based methods above thresholds tuned for each processor[8]. CPython's int implements Algorithm D directly, and Java's BigInteger uses it below a size threshold and Burnikel and Ziegler's method above it.
Uses
Every operation in a computer algebra system that reduces a number eventually divides: rational numbers are kept in lowest terms by greatest common divisors, and the Euclidean algorithm is a chain of divisions. Conversion of a large integer to decimal is repeated division by a power of ten. Modular arithmetic for cryptography, and the modular algorithms of computer algebra, need a remainder operation that is exact on every input, including the rare ones that trigger the add-back step. Multiplication above schoolbook size uses Karatsuba or FFT-based methods, and fast division inherits their speed through the reduction to multiplication.
History
Long division with estimated digits was part of the earliest multiple-precision arithmetic programs. Pope and Stein studied the accuracy of the digit estimate and the effect of scaling the divisor in 1960[2]. Knuth gave the algorithm, with the bound on the estimate and the refinement test, as Algorithm D in Seminumerical Algorithms in 1969[1]. Brinch Hansen's 1994 paper retraced the derivation and documented the pitfalls in implementing it[3], and Burnikel and Ziegler's 1998 recursive division made it the base case of a subquadratic method[4].
see also
- Karatsuba multiplicationKaratsuba multiplication is a divide-and-conquer algorithm for multiplying large integers or polynomials. Splitting each factor into a high and a low half, it computes the product from three half-size products, a₁b₁, a₀b₀ and (a₀ + a₁)(b₀ + b₁), instead of the four the schoolbook method needs, and recurses. Its cost is O(n^log₂3) ≈ O(n^1.585) digit operations instead of O(n²). Anatoly Karatsuba found it in 1960, disproving Kolmogorov's conjecture that n² was optimal. Because of its extra additions it is used above a size threshold, and it is the first of a family that leads through Toom–Cook to FFT-based multiplication.
- 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.
- Pseudo-divisionPseudo-division is division of polynomials over an integral domain that is not a field, such as the integers or a ring of polynomials in other variables. To avoid fractions, the dividend a is first multiplied by lc(b)^(deg a − deg b + 1), the leading coefficient of the divisor raised to one more than the degree difference; then ordinary long division succeeds and yields a pseudo-quotient q and pseudo-remainder r with lc(b)^e a = q b + r and deg r < deg b. It is the basic step of polynomial remainder sequences over the integers, of resultant and gcd computation, and of Wu's method in geometry theorem proving. Iterated naively, it makes coefficients grow exponentially.
further reading
- [1]D. E. Knuth, The Art of Computer Programming, Vol. 2: Seminumerical Algorithms, §4.3.1, Addison-Wesley (3rd ed., 1997).
- [2]D. A. Pope, M. L. Stein, “Multiple precision arithmetic”, Communications of the ACM 3 (1960).
- [3]P. Brinch Hansen, “Multiple-length division revisited: a tour of the minefield”, Software: Practice and Experience 24 (1994).
- [4]C. Burnikel, J. Ziegler, “Fast recursive division”, Research Report MPI-I-98-1-022, Max-Planck-Institut für Informatik (1998).
- [5]P. Barrett, “Implementing the Rivest Shamir and Adleman public key encryption algorithm on a standard digital signal processor”, CRYPTO ’86, LNCS 263 (1987).
- [6]P. L. Montgomery, “Modular multiplication without trial division”, Mathematics of Computation 44 (1985).
- [7]R. P. Brent, P. Zimmermann, Modern Computer Arithmetic, ch. 1, Cambridge University Press (2010).
- [8]T. Granlund and the GMP development team, GNU MP: The GNU Multiple Precision Arithmetic Library, “Division algorithms”.
last updated