FLINT3.7.0-dev
Guide · nmod.h

Computing discrete logarithms

The nmod_discrete_log_pohlig_hellman functions solve gx≡y(modp)g^x \equiv y \pmod p for a prime pp, where gg is the least primitive root modulo pp. The work that depends only on pp (factoring p−1p - 1, finding gg, building lookup tables) is done once by nmod_discrete_log_pohlig_hellman_precompute_prime, after which each logarithm is computed by nmod_discrete_log_pohlig_hellman_run.

When to use#

The running time depends on the largest prime factors of p−1p - 1, not on the size of pp. If p−1p - 1 has only small prime factors (if it is smooth), a logarithm costs a few exponentiations. If p−1p - 1 has a large prime factor q>104q > 10^4, the search for each base-qq digit costs up to about q/50q / 50 multiplications, since the lookup tables are limited to 100 entries. The return value of the precomputation is an estimate of the number of multiplications per logarithm, which can be used to decide whether a prime is suitable.

Usage#

  1. Initialise an nmod_discrete_log_pohlig_hellman_t with nmod_discrete_log_pohlig_hellman_init.
  2. Set it up for pp with nmod_discrete_log_pohlig_hellman_precompute_prime. This can be called again with another prime; the old data is freed.
  3. Call nmod_discrete_log_pohlig_hellman_run for any number of y∈[1,p)y \in [1, p). Each result xx satisfies 0≤x<p−10 \le x < p - 1.
  4. Free the memory with nmod_discrete_log_pohlig_hellman_clear.

The base is chosen internally and returned by nmod_discrete_log_pohlig_hellman_primitive_root. To take logarithms to another base hh, use that hx=yh^x = y if and only if xlog⁡gh≡log⁡gy(modp−1)x \log_g h \equiv \log_g y \pmod{p - 1}, a linear congruence that nmod_divides solves (see the second example).

Algorithm#

For each prime power qeq^e exactly dividing p−1p - 1, the problem is mapped to the subgroup of order qeq^e by raising yy to the power (p−1)/qe(p-1)/q^e. There the logarithm modulo qeq^e is found one base-qq digit at a time, each digit by a baby-step giant-step search in a table of at most 100 powers of g(p−1)/qg^{(p-1)/q}. The results are combined by the Chinese remainder theorem.

AlgorithmDiscrete logarithms modulo a prime (Pohlig–Hellman)Computes xx with gx=yg^x = y modulo a prime pp by solving the problem in each subgroup of prime-power order of (Z/pZ)×(\mathbb{Z}/p\mathbb{Z})^\times, one base-qq digit at a time, and combining the results by the Chinese remainder theorem. Fast when p−1p - 1 has only small prime factors.

For many logarithms to a fixed base, also modulo prime powers, the dlog module amortises a larger precomputation; see Discrete logarithm tables.

Examples#

Logarithms to the base of the least primitive root, which is 5 for p=1000033p = 1000033:

Examplenmod_discrete_log.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/nmod.h>

int main(void)
{
    nmod_discrete_log_pohlig_hellman_t L;
    ulong p = 1000033;                  /* p - 1 = 2^5 * 3 * 11 * 947 */
    ulong ys[3] = { 2, 1000, 1000032 };
    ulong g, x;
    int i;

    nmod_discrete_log_pohlig_hellman_init(L);
    nmod_discrete_log_pohlig_hellman_precompute_prime(L, p);
    g = nmod_discrete_log_pohlig_hellman_primitive_root(L);
    flint_printf("base g = %wu\n", g);

    for (i = 0; i < 3; i++)
    {
        x = nmod_discrete_log_pohlig_hellman_run(L, ys[i]);
        flint_printf("log(%wu) = %wu, g^x = %wu\n", ys[i], x,
                     nmod_pow_ui(g, x, L->mod));
    }

    nmod_discrete_log_pohlig_hellman_clear(L);
    return 0;
}
Output
base g = 5
log(2) = 773396, g^x = 2
log(1000) = 320127, g^x = 1000
log(1000032) = 500016, g^x = 1000032

The logarithm of −1=1000032-1 = 1000032 is (p−1)/2=500016(p-1)/2 = 500016, as it must be.

Logarithms to a base hh that is not a primitive root. A solution exists only if yy lies in the subgroup generated by hh:

Examplenmod_discrete_log_base.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/nmod.h>

int main(void)
{
    nmod_discrete_log_pohlig_hellman_t L;
    nmod_t mod, ord;
    ulong p = 1000033, h = 9, lh, ly, x;
    ulong ys[2] = { 81, 3 };
    int i;

    nmod_init(&mod, p);
    nmod_init(&ord, p - 1);             /* exponents live modulo p - 1 */

    nmod_discrete_log_pohlig_hellman_init(L);
    nmod_discrete_log_pohlig_hellman_precompute_prime(L, p);
    lh = nmod_discrete_log_pohlig_hellman_run(L, h);

    for (i = 0; i < 2; i++)
    {
        ly = nmod_discrete_log_pohlig_hellman_run(L, ys[i]);

        /* h^x = y  <=>  x * log(h) = log(y) mod p - 1 */
        if (nmod_divides(&x, ly, lh, ord))
            flint_printf("%wu^%wu = %wu mod %wu\n", h, x, nmod_pow_ui(h, x, mod), p);
        else
            flint_printf("%wu is not a power of %wu mod %wu\n", ys[i], h, p);
    }

    nmod_discrete_log_pohlig_hellman_clear(L);
    return 0;
}
Output
9^2 = 81 mod 1000033
3 is not a power of 9 mod 1000033
Cite this page

The FLINT team, “Computing discrete logarithms (nmod.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod/discrete_log.html (generated 2026-11-02).