Section 12.3 Another Primality Test
ΒΆSubsection 12.3.1 Another pattern
ΒΆTo answer this, we turn to another result visible in our modular power graphic.
xxxxxxxxxx
import matplotlib.pyplot as plt
from matplotlib.ticker import IndexLocator, FuncFormatter
def power_table_plot(p=(11,prime_range(100))):
mycmap = plt.get_cmap('gist_earth',p-1)
myloc = IndexLocator(floor(p/5),.5)
myform = FuncFormatter(lambda x,y: int(x+1))
cbaropts = { 'ticks':myloc, 'drawedges':True, 'boundaries':srange(.5,p+.5,1)}
P=matrix_plot(matrix(p-1,[mod(a,p)^b for a in range(1,p) for b in srange(p)]),cmap=mycmap, colorbar=True, colorbar_options=cbaropts, ticks=[myloc,myloc], tick_formatter=[None,myform])
show(P,figsize=6)
Theorem 12.3.2. The Square Root of Fermat's Little Theorem.
Proof.
Since \(a^{p-1}\equiv 1\) we know that \(a^{(p-1)/2}\) is a solution to \(x^2\equiv 1\text{.}\) (Note that \(p\) is odd so \((p-1)/2\) makes sense.)
We can rewrite and factor the congruence \(x^2\equiv 1\) as \(p\mid x^2-1=(x+1)(x-1)\) and given that \(p\) is prime, that means \(p\mid x-1\) or \(p\mid x+1\text{.}\)
Then \(x\equiv \pm 1\) (mod \(p)\text{.}\) (This is restated in Subsection 16.1.1.) Since \(a^{(p-1)/2}\) is one such solution, then \(a^{(p-1)/2}\equiv \pm 1\) (mod \(p\)).
xxxxxxxxxx
mod(2,561)^((561-1)/2)
xxxxxxxxxx
mod(3,561)^((561-1)/2)
Subsection 12.3.2 Miller's test
ΒΆA slightly stronger variant of this test is called Miller's test base a for primality.Algorithm 12.3.3. Miller's test for base a.
We will proceed by repeatedly dividing and then checking a congruence.
-
Begin with nβ1; divide it by two, and then check the power
a(nβ1)/2 (mod n).If the result is β1 we say n passes Miller's test. If the result is not Β±1, we say it fails Miller's test (since if n is prime, the result would certainly be Β±1). If the result is +1, we continue.
If we have arrived at a point where we can no longer divide nβ1 by two, we say n passes Miller's test. Otherwise, assuming a(nβ1)/2β‘1, we continue by dividing the power itself by two and then taking a to that new power. Once again, if the result is β1 we say n passes the test, and if it is not Β±1, we say it fails.
If the result is +1 and we can continue dividing the power by two, do so and check the result, as often as need be. If we arrive at the point where we have divided nβ1 by all possible powers of two and the result is still Β±1, then we say n passes the test.
Example 12.3.4.
Let's see a few examples of this. First, the number 1387 is a pseudoprime base 2 β but it does not pass Miller's test, which is good since it's composite. Try the following cell to see exactly what happens.
xxxxxxxxxx
n=1387
pretty_print(html("We know $%s$ is composite because it factors as $%s$"%(n,factor(n))))
pretty_print(html("Let's check $2^{(%s-1)/2}$ modulo $%s$: it's $%s$"%(n,n,mod(2,n)^((n-1)/2))))
Looking good β¦ But let's try another pseudoprime number (the Mersenne number M11, in fact) to see if it passes, just to be sure.
xxxxxxxxxx
n=2047
pretty_print(html("We know $%s$ is composite because it factors as $%s$"%(n,factor(n))))
pretty_print(html("Let's check $2^{(%s-1)/2}$ modulo $%s$: it's $%s$"%(n,n,mod(2,n)^((n-1)/2))))
As we can see, this shows that n=2047 passes the first part of Miller's test base 2, and that there is no further to go because (2047β1)/2=1023 is odd. So, as far as we know thus far, 2047 is prime (though actually it is the lowest Mersenne number with prime exponent not to be prime).
Let's try Algorithm 12.3.3 with another number, 1009.
xxxxxxxxxx
n=1009
pretty_print(html("We know $%s$ is prime because it factors as $%s$"%(n,factor(n))))
pretty_print(html("Let's check $2^{(%s-1)/2}$ modulo $%s$: it's $%s$"%(n,n,mod(2,n)^((n-1)/2))))
pretty_print(html("Let's check $2^{(%s-1)/2/2}$ modulo $%s$: it's $%s$"%(n,n,mod(2,n)^((n-1)/2/2))))
This passes Miller's test the first time, but the algorithm keeps going since our first computation was β‘1. The second time we got β‘β1, so we stop and hope the number is prime. (It is, in this case!)