FLINT3.7.0-dev
Algorithm

Simultaneous inversion

Cost
One inversion and 3(k−1)3(k-1) multiplications modulo nn; kk words of temporary storage.

A modular inversion by the extended Euclidean algorithm costs far more than a multiplication. To invert many elements at once, Montgomery [Mon1987] observed that one inversion suffices: invert the product of all elements and recover the individual inverses with multiplications. See also [BZ2011, §2.5.1].

The algorithm#

Algorithm 1: Simultaneous inversion

Input: v0,…,vk−1v_0, \ldots, v_{k-1} invertible modulo nn, with k≥1k \ge 1.

Output: rj=vj−1 mod nr_j = v_j^{-1} \bmod n for 0≤j<k0 \le j < k.

  1. p0←v0p_0 \gets v_0; for j=1,…,k−1j = 1, \ldots, k - 1: pj←pj−1vj mod np_j \gets p_{j-1} v_j \bmod n.
  2. u←pk−1−1 mod nu \gets p_{k-1}^{-1} \bmod n.
  3. For j=k−1,…,1j = k - 1, \ldots, 1: rj←u pj−1 mod nr_j \gets u \, p_{j-1} \bmod n, then u←u vj mod nu \gets u \, v_j \bmod n.
  4. r0←ur_0 \gets u.

Theorem 1. Algorithm 1 is correct. It performs one inversion and 3(k−1)3(k - 1) multiplications modulo nn.

Proof. After step 1, pj=v0v1⋯vjp_j = v_0 v_1 \cdots v_j, which is invertible because every factor is. We show that u=pj−1u = p_j^{-1} at the start of iteration jj of step 3. This holds for j=k−1j = k - 1 by step 2. If it holds for jj, then rj=pj−1pj−1=vj−1r_j = p_j^{-1} p_{j-1} = v_j^{-1}, and the update gives u=pj−1vj=pj−1−1u = p_j^{-1} v_j = p_{j-1}^{-1}, which is the claim for j−1j - 1. After the last iteration u=p0−1=v0−1u = p_0^{-1} = v_0^{-1}, which step 4 stores. Step 1 uses k−1k - 1 multiplications and step 3 uses 2(k−1)2(k - 1).

If some vjv_j is not invertible, then neither is pk−1p_{k-1}, and step 2 fails without revealing which element is at fault. Step 3 reads vjv_j after writing rjr_j, so the output cannot overwrite the input.

Use in FLINT#

_nmod_vec_invert implements Algorithm 1. _nmod_vec_invert_generic stores the prefix products and multiplies with nmod_mul. _nmod_vec_invert_shoup, used when n<2FLINT_BITS−1n < 2^{\mathtt{FLINT\_BITS} - 1}, stores with every pjp_j its precomputed Shoup quotient ⌊pj2FLINT_BITS/n⌋\lfloor p_j 2^{\mathtt{FLINT\_BITS}} / n \rfloor, and with every vjv_j its quotient too. In step 1, the quotient of vjv_j costs one division, and n_mulmod_and_precomp_shoup computes pjp_j together with its quotient from those of pj−1p_{j-1} and vjv_j, without a further division. All multiplications in step 3 are then Shoup multiplications by pj−1p_{j-1} and vjv_j. This costs 3k3k words of temporary storage. The inversion in step 2 is nmod_inv, which raises a FLINT_IMPINV exception if pk−1p_{k-1} is not invertible.

Referenced from#

References#

Cite this page

The FLINT team, “Simultaneous inversion,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/arithmetic/batch-inversion.html (generated 2026-11-02).