Certified sonnet primes

Last week I wrote about primailty certificates. These certificates offer a way to verify that a number is prime using less computation than was used to discover than the number was prime.

This post gives a couple more examples of primality certificates using sonnet primes. As described here,

These are primes of the form ababcdcdefefgg, the rhyme scheme of an English (Shakespearean) sonnet, where the letters a through g represent digits and a is not zero.

There are 16,942 sonnet primes, ranging from 10102323454577 to 98987676505033.

Here are Pratt certificates verifying that the largest and smallest sonnet primes are indeed primes.

As before, I assume primes less than 100 are recognizable as primes, though the certificate algorithm continue until all branches of the tree end in a 2.

Pratt Primality Certificates

The previous post implicitly asserted that J = 8675309 is a prime number. Suppose you wanted proof that this number is prime.

You could get some evidence that J is probably prime by demonstrating that

2J-1 = 1 mod J.

You could do this in Python by running the following [1].

    >>> J = 8675309
    >>> assert( pow(2, J-1, J) == 1 )

This shows J is a probable prime to base 2.

If you want more evidence, you could also show J is a probable prime to base 3.

    >>> assert( pow(3, J-1, J) == 1 )

But no matter how many bases you try, you won’t have proof that J is prime, only evidence. There are pseudoprimes, (rare) composite numbers that satisfy the necessary-but-not-quite-sufficient conditions of Fermat’s primality test.

Primality certificates

A primality certificate is a proof that a number is prime. To be practical, a certificate must be persuasive and efficient to compute.

We could show that J is not divisible by any integer less than √J. That would actually be practical because J is not that large.

    >>> for n in range(2, int(J**0.5)+1):
    ...     assert(J % n > 0)

But we’d like to use J to illustrate a method that scales to much larger numbers than J.

Pratt certificates

Pratt primality certificates are based on a theorem by Lucas [2] that says a number n is prime if there exists a number a such that two conditions hold:

an-1 = 1 mod n,

and

a(n-1)/p ≠ 1 mod n

for all primes p that divide n-1.

How do you find a? See this post.

Example

To find a Pratt certificate for J, we have to factor J-1. I assert that

J-1 = 8675308 = 4 × 2168827

and that 2168827 is prime. Here’s verification that Lucas’ theorem holds:

    >>> assert( pow(2, (J-1)//2, J) != 1 )
    >>> assert( pow(2, (J-1)//2168827, J) != 1 )

What’s that you say? You’re not convinced that 2168827 is prime? Well then, we’ll come up with a Pratt certificate for 2168827.

Pratt certificates are generally recursive. To prove that p is prime, we have to factor p-1, then prove all the claimed prime factors of p-1 are prime, etc. The recursion ends when it gets down to some set of accepted primes.

Now I assert that

    2168827 - 1 = 2168826 = 2 × 3 × 11 × 17 × 1933

and that all these numbers are prime. I’ll assume you’re OK with that, except you’re skeptical that 1933 is prime.

The following code is proof that 2168827 is prime, assuming 1933 is prime.

    >>> m = 2168827
    >>> for p in [2, 3, 11, 17, 1933]:
    ...     assert( pow(3, (m-1)//p, m) != 1 )

Finally, we’ll prove that 1933 is prime.

You can verify that

    1933 - 1 = 1932 = 2² × 3 × 7 × 23

and I assume you’re convinced that each of these factors is prime.

    >>> m = 1933
    >>> for p in [2, 3, 7, 23]:
    ...     assert( pow(5, (m-1)//p, m) != 1 )

Pratt certificates can be written in a compact form that verification software can read. Here I’ve made the process more chatty just to illustrate the parts.

Update: Here’s a visualization of the process above, drawing arrows from each prime p to the prime factors of p-1.

In this post we’ve agree to recognize 2, 3, 7, 11, 17, and 23 as primes. But you only have to assume 2 is prime. This would make the software implementation more elegant but would make the example tedious for a human consumption.

Efficiency

A primality certificate does not have to be efficient to produce, though of course that would be nice. It has to be efficient to verify. You could imagine that the prime number salesman has more compute power available than the prime number customer. In the example above, I used a computer to generate the Pratt certificate, but it wouldn’t be unreasonable to verify the certificate by hand.

The brute force certificate above, trying all divisors up to √p, obviously takes √p calculations to verify. A Pratt certificate, however, takes about

4 log2 p

calculations. So verifying a 10-digit prime requires on the order of 100 calculations rather than on the order of 100,000 calculations.

Atkin-Goldwasser-Kilian-Morain certificates

Producing Pratt certificates for very large numbers is difficult. Other certificate methods, like Atkin-Goldwasser-Kilian-Morain certificates, scale up better. Atkin-Goldwasser-Kilian-Morain certificates are more complicated to describe because they involve elliptic curves.

Just as Pratt took a characterization of primes by Lucas and turned it into a practical certification method, Atkin and Morain turned a characterization of primes by Goldwasser and Kilian, one involving elliptic curves, and turned it into an efficient certification method.

These certificates have the same recursive nature as Pratt certificates: proving that a number is prime requires proving that another (smaller) number is prime.

Update: More on elliptic curve primality proofs.

***

[1] This is a more efficient calculation than it seems. It can be done quickly using fast exponentiation. Note that it is not necessary to compute 2J-1 per se; we can carry out every intermediate calculation mod J.

[2] Lucas was French, and so the final s in his name is silent.

Quadratic reciprocity algorithm

The quadratic reciprocity theorem addresses the question of whether a number is a square modulo a prime. For an odd prime p, the Legendre symbol

\left(\frac{a}{p}\right)

is defined to be 0 if a is a multiple of p, 1 if a is a (non-zero) square mod p, and -1 otherwise. It looks like a fraction, but it’s not. It acts something like a fraction, which was presumably the motivation for the notation.

The quadratic reciprocity theorem says that if p and q are odd primes, then

\left(\frac{p}{q}\right) \left(\frac{q}{p}\right) = (-1)^{\frac{p-1}{2} \frac{q-1}{2}}

We can rewrite this in the less symmetric but more useful form

\left(\frac{p}{q}\right) = (-1)^{(p-1)(q-1)/4} \left(\frac{q}{p}\right)

The idea behind quadratic reciprocity as an algorithm is that when p < q, we can use the equation above to turn a question about the existence of square roots mod q into a question about the existence of square roots mod p. This is progress because p is smaller, and we get to reduce q modulo p.

The quadratic reciprocity theorem only applies to the case of odd primes. So we need a couple more facts to complete our algorithm: how to deal with numbers that are not odd or not prime.

If the “numerator” of our Legendre symbol is not prime, we factor it using the theorem that

\left(\frac{m}{p}\right) \left(\frac{n}{p}\right) = \left(\frac{mn}{p}\right)

for numbers m and n that are not multiples of p. And if we get a factor of 2 in a numerator there is a theorem that says

result

\left(\frac{2}{p}\right) = (-1)^{\frac{p^2 - 1}{8}}

Is today a square for Jenny?

As an example, we will determine whether 112023, today’s date, is a square mod 8675309, the telephone number in the 1981 pop song 867-5309/Jenny.

We begin by factoring 112023. A consequence of the factoring rule above is that we can throw away prime factors with an even exponent, and we can replace odd exponents with 1. Therefore

\begin{align*} \left(\frac{112023}{8675309}\right) &= \left(\frac{3^5\cdot 461}{8675309}\right) \\ &= \left(\frac{3}{8675309}\right)^5 \left(\frac{461}{8675309}\right)\\ &= \left(\frac{3}{8675309}\right) \left(\frac{461}{8675309}\right) \end{align*}

The full calculation is worked out in detail here. We apply the factoring rule and quadratic reciprocity over and over until we get Legendre symbols with such small numbers that we can evaluate them directly. In our case, all we need to know is that 1 is a square mod 3 and 2 is not.

The bottom line of our calculation is 1, so yes, 112023 is a square mod 8675309.

Calculating modular square roots

The quadratic reciprocity theorem gives us a way to determine whether a number has a square root mod p but it does not give us a way to calculate that root. Or rather those roots. Always two there are, like siths.

We could calculate the square roots of 112023 mod 8675309 using the Tonelli-Shanks algorithm, which is unfortunately more complicated than the quadratic reciprocity algorithm.

Mathematica can give us the roots, presumably by using some variation of the Tonelli-Shanks algorithm, or perhaps a newer algorithm. In any case,

    Reduce[x^2 == 112023, x, Modulus->8675309]

tells us the roots are 1955529 and 6719780. The latter is the negative of the former, modulo 8675309, i.e. -1955529 = 6719780 mod 8675309.

You can verify that the roots are correct by computing

1955529*1955529 – 440802*8675309 = 112023

and

6719780*6719780 – 5205053*8675309 = 112023.

Pascal’s triangle mod row number

Almost all binomial coefficients are divisible by their row number.

This is a theorem from [1]. What does it mean?

If you iterate through Pascal’s triangle, left-to-right and top-to-bottom, noting which entries C(m, k) are divisible by m, the proportion approaches 1 in the limit.

The author proves that the ratio converges to 1, but doesn’t say anything about the rate at which it approaches 1. By writing a little Python script we can observe how the ratio approaches 1.

First, let’s create a generator that will let us walk through Pascal’s triangle.

    def pascal():
        m, k = 0, 0
        while True:
            yield (m, k)
            k += 1
            if k > m:
                m += 1
                k = 0

We can use this generator to illustrate Pascal’s triangle mod row number.

    from math import comb

    p = pascal()
    m, k = next(p)
    while m < 10:
        ch = "*"
        if m > 0 and comb(m, k) % m == 0:
            ch = "0"
        print(ch, end="")
        if m == k:
            print()
        m, k = next(p)

This produces the following.

    *
    00
    *0*
    *00*
    *0*0*
    *0000*
    *0***0*
    *000000*
    *0*0*0*0*
    *00*00*00*

The theorem says that as we keep going, the proportion of 0’s in a diagram like the one above approaches 1.

Now let’s plot the proportion of zeros as we traverse Pascal’s triangle mod row number.

    N = 1000
    x = np.zeros(N)
    p = pascal()
    for n in range(N):
        m, k = next(p)
        if m > 0 and comb(m, k) % m == 0:
            x[n] = 1
    y = np.arange(N)
    plt.plot(y, x.cumsum()/y)
    plt.show()

Here’s what we get.

The ratio doesn’t seem to be converging to 1. If I had to guess, I might say it’s converging to 0.7 by looking at this plot. But if we go further out and switch to a log scale it seems more plausible that the sequence is converging to 1, albeit very slowly.

[1] Heiko Harborth. Divisibility of Binomial Coefficients by Their Row Number. The American Mathematical Monthly, Jan. 1977, Vol. 84, No. 1, pp. 35–37.

Repunits: primes and passwords

A repunit is a number whose base 10 representation consists entirely of 1s. The number consisting of n 1s is denoted Rn.

Repunit primes

A repunit prime is, unsurprisingly, a repunit number which is prime. The most obvious example is R2 = 11. Until recently the repunit numbers confirmed to be prime were Rn for n = 2, 19, 23, 317, 1031. Now the case for n = 49081 has been confirmed.

R_{49081} = \frac{10^{49081} - 1}{9} = \underbrace{\mbox{111 \ldots 1}}_{\mbox{{\normalsize 49,081 ones}} }

Here is the announcement. The date posted at the top of the page is from March this year, but I believe the announcement is new. Maybe the author edited an old page and didn’t update the shown date.

Repunit passwords

Incidentally, I noticed a lot of repunits when I wrote about bad passwords a few days ago. That post explored a list of commonly used but broken passwords. This is the list of passwords that password cracking software will try first. The numbers Rn are part of the list for the following values of n:

1–45, 47–49, 51, 53–54, 57–60, 62, 67, 70, 72, 77, 82, 84, 147

So 46 is the smallest value of n such that Rn is not on the list. I would not recommend using R46 as a password, but surprisingly there are a lot of worse choices.

The bad password file is sorted in terms of popularity, and you might expect repunits to appear in the file in order, i.e. shorter sequences first. That is sorta true overall. But you can see streaks in the plot below showing multiple runs where longer passwords are more common than shorter passwords.

Average digit sum

Suppose you write down a number and take the sum of its digits. In what base will this sum be the smallest on average?

Let’s do a couple examples comparing base 10 and base 2. The number 2022 in base 10 has digit sum 6, but its binary equivalent 11111100110 has digit sum 8, so the base 10 representation has the smaller digit sum. But if we take 1024 in base 10, the digit sum is 7, but 1024 = 210 and so the sum of its binary digits is just 1.

In [1] the author proves that for sufficiently large N, the average digit sum for all positive integers less than N is the least in base 2. Moreover, the author shows that as N goes to infinity, the average digit sum of numbers less than N written in radix r is asymptotically equal to

(r – 1) log N / 2 log r.

This is an increasing function of r and so not only does base 2 have the minimum average digit sum, the average digit sum increases with the size of the base.

Let’s see how this works out with N = 1,000,000 and r ranging from 2 to 10 using the following Mathematica code.

    Table[
        Sum[ Total[IntegerDigits[n, r]], 
            {n, 0, 1000000}] /1000000., 
        {r, 2, 10}
    ]

This returns

    {9.885, 12.336, 14.8271, 16.5625, 18.5806, 
     20.5883, 22.2383, 24.1978, 27.}

Now let’s see what values the asymptotic formula predicted.

    Table[(r - 1) Log[1000000.]/(2 Log[r]), {r, 2, 10}]

This returns

    {9.96578, 12.5754, 14.9487, 17.1681, 19.2765, 
     21.2993, 23.2535, 25.1508, 27.}

So for each r the asymptotic formula produces a good approximation to the actual result.

[1] L. E. Bush. Asymptotic Formula for the Average Sum of the Digits of Integers. The American Mathematical Monthly, Mar., 1940, Vol. 47, No. 3, pp. 154-156

Cicadas and Chicken Nuggets

Yesterday I wrote about the Chicken McNugget problem: if Chicken McNuggets are sold in boxes of 6, 9, and 20, what’s the largest number of nuggets you cannot buy? That post showed that the solution was 43. The technical name for these kinds of problems is numerical monoids.

The method of solution in the previous post is reusable. But before we reuse it, let’s look at a simpler problem involving two units rather than three.

Cicada cycles

Some cicadas appear every 13 years and some every 17 years. So, for example, 43 years from now would be two 13-year cicada cycles and one 17-year cicada cycle. We could ask what the largest number of years is that cannot be expressed in terms of cicada cycles. This is known as the Frobenius number of 13 and 17.

For one thing, there is a largest number of years that cannot be expressed in terms of cicada cycles. If x and y are positive integers, there is an upper bound on the numbers that cannot be expressed as sum of non-negative integer multiples of x and y if and only if x and y are relatively prime. And if x and y are relatively prime, this upper bound is

xyxy.

J. J. Sylvester proved this back when Chester A. Arthur was US president [1]. It’s not quite obvious that x and y being relatively prime is sufficient for there to be an upper bound—it follows from Bézout’s lemma—but it is obvious it’s necessary: if x and y are multiples of k > 1, then all sums of integer multiples of x and y are also multiples of k, and so non-multiples of k are unreachable.

So Sylvester’s theorem shows that the Frobenius number of 13 and 17 is 13×17 – 13 – 17 = 191.

Throwing in Venus

Venus goes around the sun 13 times in the time it takes Earth to go around 8 times. That is, Venus and Earth line up every 8 years (from the perspective of our planet).  What if we throw in multiples of 8 year cycles? We could, for example, express 25 years as one 17-year cicada cycle and one cycle of Venus.

We know there’s an upper bound on numbers that cannot be expressed as non-negative integer combinations of 13, 17, and 8 because we can express every number greater than 191 using 13 and 17 alone. Adding a new generator cannot increase the Frobenius number. In fact, in this case we know it lowers it because we could apply Sylvester’s theorem to 8 and 13 and show that the Frobenius number of 8, 13, and 17 is at most 83.

Algorithm

There is no analog of Sylvester’s theorem for more than two generators, but we could always compute the Frobenius number in a particular case using the approach from the McNugget post: use brute force up to a point, then use theory to argue that’s enough. In that post, the smallest generator was 6, and we used brute force until we found 6 consecutive numbers that could be produced as non-negative integer combinations of our generators. We could improve on this approach by using Sylvester’s theorem: we use brute force for all combinations that could possibly generate a result lower than an upper bound obtained by Sylvester’s theorem. There are more efficient algorithms—see [2] for references—but the following will work.

    from math import gcd, floor
    from itertools import product

    def frobenius(generators):
        "assumes the smallest two generators are relatively prime"
        g = sorted(generators)
        assert(len(g) > 1)
        assert(gcd(g[0], g[1]) == 1)
        ub = g[0]*g[1] 
        
        limits = [range(int(floor(ub/n))) for n in generators]
        reachable = set()
        for p in product(*limits):
            combo = sum(p[i]*g[i] for i in range(len(g)))
            if combo < ub:
                reachable.add(combo)
        complement = set(range(ub)) - reachable
        return(max(complement))
    
    print( frobenius([13, 17, 8]) )

This shows that the Frobenius number of 8, 13, and 17 is 44.

Just to do another example, the code can show that the smallest number of days not representable by multiples of 7, 30, and 365 is 209 days.

Notes

[1] J. J. Sylvester, Mathematical questions with their solutions, Educational Times 41 (1884) 171–178.

[2] Factoring in the Chicken McNugget monoid. arXiv:1709.01606

Sum of squares mod n uniformly distributed

Let s be an integer equal to at least 5 and let n be a positive integer.

Look at all tuples of s integers, each integer being between 0 and n-1 inclusive, and look at the sum of their squares mod n. About 1/n will fall into each possible value.

So, for example, if you look at a lists of 6 digits, sum the squares of the digits in each list, then about 1/10 of the results will end in 0, about 1/10 will end in 1, about 1/10 will end in 2, etc.

This is the gist of a theorem discussed on Math Overflow here.

And here is Python code to demonstrate it.

    from itertools import product

    n = 10
    s = 6

    counts = [0]*n
    for p in product(range(n), repeat=s):
        counts[ sum(x**2 for x in p) % n ] += 1

    print(counts)

The result is

    [103200, 99200, 99200, 99200, 99200, 
     103200, 99200, 99200, 99200, 99200]

and so between 99,200 and 103,200 sums end in each digit.

Let’s run it again with n = 11 and s = 5. Now we get

    [14641, 14762, 14520, 14762, 14762, 14762, 
     14520, 14520, 14520, 14762, 14520]

which shows the counts are between 14,520 and 14,762. If they were perfectly uniformly distributed we’d expect 114 = 14,641 in each bin. The maximum deviation from a uniform distribution is 0.83%.

The code becomes impractical as s or n get larger, but we can try s = 7 and keep n = 11. Then we get a maximum deviation from uniformity of 0.075%, i.e. about 10 times closer to being uniform.

Phi Phi

I was reading something this afternoon and ran across φ(φ(m)) and thought that was unusual. I often run across φ(m), the number of positive integers less than m and relative prime to m, but don’t often see Euler’s phi function iterated.

Application of φ∘φ

This section will give an example of a theorem where φ(φ(m)) comes up.

First of all, Euler’s theorem says that given an integer m > 1 and an integer g relatively prime to m, then

gφ(m) = 1 (mod m).

Now Euler’s theorem gives a value of t such that gt = 1 (mod m). It doesn’t say this is the smallest t, only that it’s a possible value of t. If φ(m) is the smallest such value of t, then g is called a primitive root mod m.

For example, let m = 10. Then φ(10) = 4, and so a primitive root mod 10 is a number g such that g4 = 1 mod 10, but gt is not congruent to 1 mod 10 for t = 1, 2, or 3. Now 3 is a primitive root mod 10 because 34 ends in 1, but 3, 3², and 3³ do not end in 1.

There are no primitive roots mod 12. A primitive root mod m has to be relatively prime to m [1], and so there are only four candidates: 1, 5, 7, and 11. Each of these is congruent to 1 mod 12 when squared, so for none of them is 4 = φ(12) the smallest exponent that gives a number congruent to 1.

Now we’re ready to state our theorem, Theorem 2.34 from The Joy of Factoring by Samuel Wagstaff, Jr.

An integer m > 1 has a primitive root if and only if m = 2, m = 4, m = pe , or m = 2pe, where p is an odd prime and e is a positive integer. If m has a primitive root, then it has exactly φ(φ(m)) of them in 1 ≤ gm.

This theorem could have told us that 10 has primitive roots and 12 doesn’t, and it could tell us that 10 has exactly φ(φ(m)) = 2 primitive roots. We found one of them, 3, and the other is 7.

Higher iterates

Let φ1(n) = φ(n) and φ2(n) = φ(φ(n)). In general, let φk be the function defined by iterating the phi function k times. If we apply φ to any integer enough times, we’ll get 1. For each n, let k(n) be the smallest value of k such that φk(n) = 1. Then Erdős et al proved that k(n) is between log(n)/log(3) and log(n)/log(2).

To illustrate this, let n = 20220630. The theorem above predicts we’ll have to apply φ between 16 and 25 times before we get to 1. The following code shows k(n) = 21.

    from sympy import totient

    n = 20220630
    k = 0

    while n > 1:
        n = totient(n)
        k += 1
 
    print(k)

Note that “totient” is another name for Euler’s φ function.

Related posts

[1] If g shares a factor bigger than 1 with m, then so does every positive power of g, and so no power of g is congruent to 1 mod m. In particular, gφ(m) cannot be 1.