Skip to main content

Indices — The Discrete Logarithm

note

This is Lab 12 from Clint by Hugh Montgomery, edited by Codex and Elad Zelingher. All rights reserved to Hugh Montgomery.

Suppose that gg is a primitive root of the prime number pp. If (a,p)=1(a,p)=1 then there is a number ν\nu such that gνa(modp)g^\nu\equiv a\pmod p; moreover, the value of ν\nu is uniquely determined modulo p1p-1. This ν\nu is called the index of aa with respect to the primitive root gg. In symbols we write ν=indga\nu=\operatorname{ind}_g a when the value of pp has already been specified. By way of analogy, any positive real number xx can be written uniquely in the form eye^y where y=lnxy=\ln x. Thus indga\operatorname{ind}_g a is a discrete analogue of lnx\ln x.

For a given prime p<104p<10^4, the program IndTab displays a table of the indices of the reduced residue classes modulo pp. If there are more values than can be displayed on a single page, then you may use the pagination controls to move around in the table. Initially, gg is the least positive primitive root of pp, but you are free to switch to a different primitive root. The program will prevent you from choosing a base that is not a primitive root. The program also provides a table of the powers of gg, which is obtained by selecting “Powers.” To return to the table of indices from the table of exponentials, select “Indices.” These tables may be used in the manner of tables of logarithms and exponentials, to find the solutions of multiplicative congruences.

For example, to find the solutions of the congruence x312(mod97)x^3\equiv12\pmod{97}, we take g=5g=5, and write x5μ(mod97)x\equiv5^\mu\pmod{97}. From IndTab we discover that ind512=42\operatorname{ind}_5 12=42. That is, 12542(mod97)12\equiv5^{42}\pmod{97}. Hence the initial congruence may be rewritten as 53μ542(mod97)5^{3\mu}\equiv5^{42}\pmod{97}. This is equivalent to asserting that 3μ42(mod96)3\mu\equiv42\pmod{96}. From LinCon we discover that this is equivalent to μ14(mod32)\mu\equiv14\pmod{32}. That is, μ14\mu\equiv14, 4646, or 7878 modulo 9696. Returning to the IndTab program, we enter p=97p=97 again, and then select “Powers” to switch to the table of exponentials, i.e. powers of the primitive root 5. From this table we deduce that 51448(mod97)5^{14}\equiv48\pmod{97}, that 54631(mod97)5^{46}\equiv31\pmod{97}, and that 57818(mod97)5^{78}\equiv18\pmod{97}. Hence the desired solutions are x48x\equiv48, 3131, and 1818 modulo 9797. As a check, one may use the program Power to verify that 48331318312(mod97)48^3\equiv31^3\equiv18^3\equiv12\pmod{97}.

If the actual roots of the congruence x312(mod97)x^3\equiv12\pmod{97} are not needed, but only the number of roots, then one may proceed more simply, using Euler's criterion (Corollary 2.38 on p. 101 of NZM) and the program Power: Since 1296/(3,96)=12321(mod97)12^{96/(3,96)}=12^{32}\equiv1\pmod{97}, it follows that the given congruence has exactly (3,96)=3(3,96)=3 solutions.

Problem 1

Use the program IndTab to find the solutions of the congruence x4693(mod1093)x^4\equiv693\pmod{1093}.

Problem 2

Use IndTab to find all solutions of the congruence x5693(mod1093)x^5\equiv693\pmod{1093}.

Problem 3

Use IndTab in the manner above to show that the congruence x7693(mod1093)x^7\equiv693\pmod{1093} has no solution. At what point in the argument does it become apparent that there is no solution? Use Theorem 2.37 of NZM to provide a simpler proof that this congruence has no solution.

Problem 4

Use IndTab to find all solutions of the congruence x10475(mod9973)x^{10}\equiv475\pmod{9973}.

Problem 5

Use IndTab to find all xx such that 2x133(mod9973)2^x\equiv133\pmod{9973}.

Problem 6

With p=9973p=9973, use IndTab to determine the value of ind103877\operatorname{ind}_{103}877.

The program IndTab is restricted to p<104p<10^4 because the entire table is computed at the outset, and held in active memory (RAM). We have efficient means to compute powers, and efficient means to locate the least positive primitive root gg, and thus we can easily compute values of gμ(modp)g^\mu\pmod p as μ\mu runs over any given interval. What seems to be hard is to calculate values of indga\operatorname{ind}_g a for a general aa. Indeed, methods of encryption have been proposed whose security depends on the supposition that evaluating indices is computationally difficult. This computational snag is often referred to in the literature as the problem of the discrete logarithm.

The program IndTab first constructs a list of the values gνg^\nu (i.e., exponentials), and then uses it to form a table of the indices. Thus inda\operatorname{ind}a is found for each aa (mod pp), but the amount of work is proportional to pp. A first step toward improving on this has been suggested by Shanks: Suppose that you wish to calculate indgx(modp)\operatorname{ind}_g x\pmod p. Let ss be a base to be described later; we want to find ii and jj so that gis+jx(modp)g^{is+j}\equiv x\pmod p. To this end, use the extended Euclidean algorithm (i.e., the program LinCon) to find g\overline g so that gg1(modp)g\overline g\equiv1\pmod p. Construct a table of the values xgjx\overline g^j (mod pp) for 0j<s0\leq j<s. Then, for i=0,1,i=0,1,\ldots, compute gisg^{is} (mod pp), and look to see if the number computed is found in the table. When it is found, we have the desired values of ii and jj, and indgx=is+j\operatorname{ind}_g x=is+j.

When searching for a particular value of gisg^{is} in the table, it would be very slow to inspect all ss values. Instead, we sort the table of values xgj(modp)x\overline g^j\pmod p by size, into increasing order. (A useful algorithm for sorting, called HeapSort, is discussed later in this laboratory.) Then one can search for a specified value in the table by binary subdivisions. To motivate the choice of ss, we consider the amount of work required. The time required to construct the table is O(s)O(s), but the time required to sort it is a little greater, O(slogs)O(s\log s). Searching by binary subdivision takes O(logs)O(\log s) steps, and we expect that it will be necessary to conduct p/s\approx p/s such searches. Thus the total amount of work is proportional to

(ps+s)logs.\left(\frac ps+s\right)\log s.

This is minimized by taking sps\approx\sqrt p, and then the time involved is O(p1/2logp)O(p^{1/2}\log p), a little slower than proving that pp is prime by trial division. Thus we see that Shanks' algorithm is not very fast for big pp, although it represents a big improvement over O(p)O(p).

In practice, the parameter ss above is constrained also by the amount of available memory. For example, the program Ind calculates indgx(modp)\operatorname{ind}_g x\pmod p by Shanks' method for p<109p<10^9; for p<108p<10^8 it takes ss to be the integer nearest p\sqrt p, but for larger pp it takes s=10000s=10000 so that the data fits into one 64K segment of memory. (Each entry of the table occupies 4 bytes, and a companion table 2 bytes each, so the tables require 60K of memory.) To witness Shanks' algorithm in action, apply IndDem with g=2g=2, a=45a=45, p=101p=101. Also try g=2g=2, a=3a=3, p=1093p=1093.

For more information concerning algorithms used to calculate indices see the following papers.

  • D. Coppersmith, A. M. Odlyzko, R. Schroeppel, “Discrete logarithms in GF(pp),” Algorithmica 1 (1986), 1–15.
  • D. M. Gordon, “Discrete logarithms in GF(pp) using the number field sieve,” SIAM J. Discrete Math. 6 (1993), 124–138.
  • B. A. LaMacchia, A. M. Odlyzko, “Computation of discrete logarithms in prime fields,” Des. Codes Cryptogr. 1 (1991), 47–62.
  • K. S. McCurley, “The discrete logarithm problem,” Cryptology and computational number theory (Boulder, 1989), Amer. Math. Soc., Providence, 1990, pp. 49–74.
  • A. M. Odlyzko, “Discrete logarithms in finite fields and their cryptographic significance,” Advances in Cryptography (Proc. 1984 EUROCRYPT Workshop), Springer-Verlag, New York, 1985, pp. 224–314.

Problem 7

Use PrimRoot to find a primitive root gg modulo p=123456791p=123456791. Use the program Ind to determine indg57085185(modp)\operatorname{ind}_g57085185\pmod p. For example, use Ind with g=17g=17, a=57085185a=57085185, and p=123456791p=123456791. Use this information to find all roots of the congruence x557085185(mod123456791)x^5\equiv57085185\pmod{123456791}.

Problem 8

The program Ind searches for a specified value xx among the powers of g(modp)g\pmod p. It is essential that (g,p)=1(g,p)=1, but it is not necessary that pp be prime or that gg be a primitive root. Is 3 a power of 2 modulo 123456791? That is, does the congruence 2ν3(mod123456791)2^\nu\equiv3\pmod{123456791} have a solution? Use Ind with g=2g=2, a=3a=3, and p=123456791p=123456791. Here the modulus is prime, but the base is not a primitive root. Note that the program returns only the least non-negative solution. The period of the solutions is p1p-1 if gg is a primitive root (mod pp), but it is smaller in other cases. Find all ν\nu (mod 123456790) such that 2ν3(mod123456791)2^\nu\equiv3\pmod{123456791}. (Hint: Use the program Order to determine the order of 2 (mod 123456791).) Confirm that Ind still works when pp is composite by using g=2g=2, a=23a=23, and p=91p=91. Use g=2g=2, a=17a=17, and p=123456791p=123456791. What happens?

Problem 9

Assume that p=1234567897531p=1234567897531 is prime. Use the programs GCD and Power to determine the number of roots of the congruence x7713(mod1234567897531)x^{77}\equiv13\pmod{1234567897531}. (Theorem 2.37 of NZM is relevant here.) Note that you do not have any tool available to find these roots, since pp is so large. Such tools do exist; for example one might elaborate on the technique developed in the next laboratory. Alternatively, the polynomial x7713x^{77}-13 can be factored quickly (mod pp), for example by the method of D. G. Cantor and H. Zassenhaus, “A new algorithm for factoring polynomials over finite fields,” Math. Comp. 36 (1981), 587–592.

We now consider the problem of sorting numbers by size. While not a number-theoretic problem, we find it useful (as above) to be able to sort numbers with reasonable efficiency. Suppose that a1,a2,,ana_1,a_2,\ldots,a_n are nn distinct numbers that we want to sort into increasing order. First, in Bubble Sort, one passes repeatedly through the list, transposing pairs that are found to be out of order, until a pass discloses no transpositions. This takes time O(n2)O(n^2), which is terrible! Never use Bubble Sort!

So how much faster can we hope for? We derive a lower bound. When two elements aia_i and aja_j are compared, the set of all possible orderings is divided into two classes, those with ai>aja_i>a_j and those with ai<aja_i<a_j. After kk such comparisons have been made, the set of all possible orderings has been divided into at most 2k2^k classes. If 2k<n!2^k<n! then there is a class containing two different orderings. Consequently, if the original ordering of our aia_i is one of these orderings, then we have not yet distinguished it from all other possible orderings, and at least one more comparison is necessary. Thus for any sorting algorithm there is an ordering of the aia_i that gives rise to more than (logn!)/log2(\log n!)/\log2 comparisons. Since n!>(n/e)nn!>(n/e)^n, the worst-case running time of any sorting algorithm is nlogn\gg n\log n. This lower bound is of the correct order of magnitude, since we have algorithms that run in O(nlogn)O(n\log n) time.

Among the possible methods that one might consider, we confine our attention to HeapSort, invented by J. W. J. Williams. This method runs in O(nlogn)O(n\log n) time, with the worst case only about 20% longer than the average. It is very easy to understand and to program, and requires little memory. In HeapSort, we think of the aia_i as forming a binary tree, in which aia_i has subordinates a2ia_{2i} and a2i+1a_{2i+1}, as long as these indices do not exceed nn. Conversely, if i>1i>1 then aia_i reports to its superior, ai/2a_{\lfloor i/2\rfloor}. The situation for n=28n=28 is depicted below:

Binary tree containing entries a subscript 1 through a subscript 28a1a2a3a4a5a6a7a8a9a10a11a12a13a14a15a16a17a18a19a20a21a22a23a24a25a26a27a28

If aia_i is smaller than one of its underlings then we exchange aia_i with the larger of a2ia_{2i} and a2i+1a_{2i+1}. We repeatedly demote a particular entry until it majorizes its subordinates. (Executives above their level of competence are demoted.) We begin at the bottom of the table (high indices), and work up. Thus in the example above, we would compare a14a_{14} with a28a_{28}, and exchange them if a14a_{14} is the smaller. Then we compare a26a_{26} with a27a_{27} to determine which is the larger, and then compare that one with a13a_{13}. We demote a13a_{13} if one of the numbers under it is larger. We continue with this, until aia2ia_i\geq a_{2i} and aia2i+1a_i\geq a_{2i+1} whenever the indices lie between 1 and nn. Such a configuration we call a heap.

Once the heap has been formed, it is clear that a1a_1 is the largest number in the entire collection. We swap a1a_1 with ana_n. This destroys the heap property, so we demote the new a1a_1 until it is restored. At this point we have a heap of n1n-1 numbers, and ana_n is ignored at the bottom. The new entry a1a_1 is the largest member in the heap (second largest, overall), so we exchange a1a_1 and an1a_{n-1}. At this point both an1a_{n-1} and ana_n have reached their final resting places. We demote the new a1a_1 until we again have a heap, and then again the top of the heap is retired. Continuing in this manner, with successively smaller heaps, we eventually have no heap left, and a1a2ana_1\leq a_2\leq\cdots\leq a_n.

For extremely large collections (such as the Manhattan telephone directory), it is important to sort as quickly as possible. In such cases it may be worth using the more complicated QuickSort algorithm. For a detailed discussion of sorting, see D. E. Knuth, The Art of Computer Programming, Vol. 3: Sorting and Searching, Addison-Wesley, Reading, 1973. In particular, HeapSort is described on pp. 145–149, 153–158.

Problem 10

The program HSortDem demonstrates the HeapSort algorithm. Choose the number nn of integers to be sorted, and witness the process. How does the number of comparisons compare with the lower bound derived above?