FLINT3.7.0-dev
Algorithm

Discrete logarithms modulo a prime (Pohlig–Hellman)

Cost
For p−1=∏qieip - 1 = \prod q_i^{e_i}, one call costs O(∑iei(log⁡p+Di))O(\sum_i e_i (\log p + D_i)) multiplications modulo pp, where DiD_i is about qi\sqrt{q_i} for qiq_i up to about 10410^4 and between qi/100q_i / 100 and qi/50q_i / 50 for larger qiq_i.

Let pp be prime and let gg be a primitive root modulo pp, so that every y∈(Z/pZ)×y \in (\mathbb{Z}/p\mathbb{Z})^\times is gxg^x for a unique x∈[0,p−1)x \in [0, p - 1), the discrete logarithm of yy. For a general pp no fast algorithm is known. When the group order p−1p - 1 factors into small primes, the method of Pohlig and Hellman [PohHel1978] reduces the problem to logarithms in groups of prime order, which are solved by Shanks' baby-step giant-step method [Sha1971]. See also [CraPom2005].

FLINT implements this method in the nmod_discrete_log_pohlig_hellman functions.

Reduction to prime-power order#

Write p−1=∏i=1kqieip - 1 = \prod_{i=1}^{k} q_i^{e_i} with distinct primes qiq_i, and let ci=(p−1)/qieic_i = (p-1)/q_i^{e_i}. The element hi=gcih_i = g^{c_i} has order exactly qieiq_i^{e_i}, and yci=hixy^{c_i} = h_i^{x}. So xi=x mod qieix_i = x \bmod q_i^{e_i} is the logarithm of yciy^{c_i} to the base hih_i, a problem in a group of order qieiq_i^{e_i}. The xix_i determine xx by the Chinese remainder theorem:

x≡∑i=1kxi ϵi(modp−1),x \equiv \sum_{i=1}^{k} x_i \, \epsilon_i \pmod{p - 1},

where the idempotent ϵi=ci⋅(ci−1 mod qiei)\epsilon_i = c_i \cdot (c_i^{-1} \bmod q_i^{e_i}) satisfies ϵi≡1(modqiei)\epsilon_i \equiv 1 \pmod{q_i^{e_i}} and ϵi≡0(modqjej)\epsilon_i \equiv 0 \pmod{q_j^{e_j}} for j≠ij \ne i.

Digits in a prime-power group#

Fix ii and write q=qiq = q_i, e=eie = e_i, h=hih = h_i and xi=∑j=0e−1djqjx_i = \sum_{j=0}^{e-1} d_j q^j with 0≤dj<q0 \le d_j < q. The element γ=hqe−1=g(p−1)/q\gamma = h^{q^{e-1}} = g^{(p-1)/q} has order qq.

Algorithm 1: Digits of the logarithm

Input: z=ycz = y^{c} with c=(p−1)/qec = (p-1)/q^e, the elements h−1h^{-1} and γ\gamma.

Output: xi=log⁡hz mod qex_i = \log_h z \bmod q^e.

  1. β←h−1\beta \gets h^{-1}, X←0X \gets 0.
  2. For j=0,1,…,e−1j = 0, 1, \ldots, e - 1:

    1. w←zqe−1−jw \gets z^{q^{e-1-j}}.
    2. Find dj∈[0,q)d_j \in [0, q) with γdj=w\gamma^{d_j} = w.
    3. z←zβdjz \gets z \beta^{d_j}, β←βq\beta \gets \beta^{q}, X←X+djqjX \gets X + d_j q^j.
  3. Return XX.

Theorem 1. In Algorithm 1, step 2.2 always has a solution, it is the digit djd_j of xix_i, and the algorithm returns xix_i.

Proof. We show that at the start of iteration jj, β=h−qj\beta = h^{-q^j} and z=hxi−Xjz = h^{x_i - X_j} with Xj=∑l<jdlqlX_j = \sum_{l < j} d_l q^l. This holds for j=0j = 0. Since qjq^j divides xi−Xjx_i - X_j, write xi−Xj=qj(dj+qm)x_i - X_j = q^j (d_j + q m). Then

w=h(xi−Xj)qe−1−j=hqe−1dj hqem=γdj,w = h^{(x_i - X_j) q^{e-1-j}} = h^{q^{e-1} d_j} \, h^{q^e m} = \gamma^{d_j},

because hh has order qeq^e. As γ\gamma has order qq, the solution of step 2.2 is unique in [0,q)[0, q) and equals djd_j. Step 2.3 multiplies zz by h−qjdjh^{-q^j d_j}, giving hxi−Xj+1h^{x_i - X_{j+1}}, and replaces β\beta by h−qj+1h^{-q^{j+1}}.

Logarithms of prime order#

Step 2.2 is a discrete logarithm in the group of order qq generated by γ\gamma. FLINT uses a baby-step giant-step search with a precomputed table of bounded size.

For q=2q = 2 no search is needed: γ=−1\gamma = -1, and dj=0d_j = 0 if w=1w = 1, dj=1d_j = 1 if w=γw = \gamma.

For odd qq, choose a stride mm and let C=⌈q/m⌉C = \lceil q / m \rceil. The table holds the pairs (γcm,cm)(\gamma^{cm}, cm) for 0≤c<C0 \le c < C, sorted by the first entry. Any d∈[0,q)d \in [0, q) can be written d=cm+d′d = cm + d' with 0≤c<C0 \le c < C and 0≤d′<m0 \le d' < m, so γd=w\gamma^{d} = w if and only if wγ−d′=γcmw \gamma^{-d'} = \gamma^{cm}.

Algorithm 2: Table search

Input: ww in the group generated by γ\gamma, the sorted table, and γ−1\gamma^{-1}.

Output: dd with γd=w\gamma^{d} = w.

  1. For d′=0,1,…,m−1d' = 0, 1, \ldots, m - 1:

    1. If w=γcmw = \gamma^{cm} for some table entry (found by binary search), return d=cm+d′d = cm + d'.
    2. w←wγ−1w \gets w \gamma^{-1}.
  2. Fail.

The search returns the correct digit. Let d∈[0,q)d \in [0, q) be the true logarithm of the input ww. For d′≤dd' \le d, the current value is γd−d′\gamma^{d - d'} with 0≤d−d′<q0 \le d - d' < q. Since 0≤cm<q0 \le cm < q for every table entry, it matches an entry exactly when d−d′=cmd - d' = cm. The first such d′d' is d mod md \bmod m, with c=⌊d/m⌋<Cc = \lfloor d/m \rfloor < C.

The table costs CC entries and the search at most mm multiplications and mm binary searches. Balancing the two gives m≈qm \approx \sqrt q. FLINT starts from m=⌈q⌉m = \lceil \sqrt q \rceil and doubles mm until C≤100C \le 100. So the table has at most 100 entries, and the search costs O(q)O(\sqrt q) multiplications for qq up to about 10410^4, but between q/100q/100 and q/50q/50 for larger qq. This keeps the precomputation small and favours primes pp for which p−1p - 1 is smooth, which is how the functions are used in FLINT.

Precomputation#

nmod_discrete_log_pohlig_hellman_precompute_prime performs the parts that depend only on pp:

  1. Factor p−1p - 1 with n_factor.
  2. For each qiq_i, compute cic_i, ϵi mod (p−1)\epsilon_i \bmod (p - 1) and qiei−1q_i^{e_i - 1}.
  3. Take gg as the least a≥1a \ge 1 with a(p−1)/qi≠1a^{(p-1)/q_i} \ne 1 for all ii, that is, the least primitive root. If pp is prime, one exists. If none is found below pp, an exception is thrown.
  4. For each ii, compute γi=g(p−1)/qi\gamma_i = g^{(p-1)/q_i}, γi−1\gamma_i^{-1}, hi−1=g−cih_i^{-1} = g^{-c_i}, the stride mim_i and the sorted table.

It returns an estimate of the number of multiplications of one run: for each ii, the cost of the exponentiation yciy^{c_i}, plus for each of the eie_i digits the cost of the exponentiation in step 2.1 of Algorithm 1, mi(1+ln⁡Ci)m_i (1 + \ln C_i) for the search and 2ln⁡qi2 \ln q_i for the power of β\beta.

The full algorithm#

Algorithm 3: Pohlig–Hellman

Input: the precomputed data for pp, and 0<y<p0 < y < p.

Output: x∈[0,p−1)x \in [0, p - 1) with gx=yg^x = y.

  1. For each ii: compute z=yciz = y^{c_i} and xix_i by Algorithm 1, using Algorithm 2 for each digit.
  2. Return ∑ixiϵi mod (p−1)\sum_i x_i \epsilon_i \bmod (p - 1).

The sum in step 2 is accumulated in three words and reduced once at the end. If a search in Algorithm 2 fails, or w∉{1,−1}w \notin \{1, -1\} when q=2q = 2, an exception is thrown. For an odd prime pp this happens only for y=0y = 0, which has no logarithm.

Theorem 2. For prime pp and 0<y<p0 < y < p, Algorithm 3 returns the discrete logarithm of yy to the base gg.

Proof. By Theorem 1, step 1 computes xi=x mod qieix_i = x \bmod q_i^{e_i}, where the search of Algorithm 2 succeeds because ww lies in the group generated by γi\gamma_i. By the properties of the idempotents, ∑ixiϵi≡xi(modqiei)\sum_i x_i \epsilon_i \equiv x_i \pmod{q_i^{e_i}} for every ii, so it is congruent to xx modulo p−1p - 1.

Cost. Each digit needs an exponentiation by qe−1−j<pq^{e-1-j} < p, a search and an exponentiation of β\beta, so the total is O(∑iei(log⁡p+mi))O(\sum_i e_i (\log p + m_i)) multiplications, plus one exponentiation yciy^{c_i} for each ii. When all qiq_i are small, this is O(log⁡2p)O(\log^2 p).

Use in FLINT#

Referenced from#

References#

  • [PohHel1978] S. C. Pohlig and M. E. Hellman, "An improved algorithm for computing logarithms over GF(p) and its cryptographic significance", IEEE Transactions on Information Theory 24 (1978), 106–110, https://doi.org/10.1109/TIT.1978.1055817
  • [Sha1971] D. Shanks, "Class number, a theory of factorization, and genera", Proc. Symp. Pure Math. 20, American Mathematical Society (1971), 415–440
  • [CraPom2005] R. Crandall and C. Pomerance, Prime Numbers: A Computational Perspective, second edition, Springer (2005).
Cite this page

The FLINT team, “Discrete logarithms modulo a prime (Pohlig–Hellman),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/number-theory/discrete-log.html (generated 2026-11-02).