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)[2:])):
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.)
As in Section 7.3, we can rewrite and factor the congruence \(x^2\equiv 1\) as \(p\mid x^2-1=(x+1)(x-1)\text{.}\) Given that \(p\) is an odd 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
Algorithm 12.3.3. Miller's test for base .
We will proceed by repeatedly dividing and then checking a congruence.
-
Begin with
divide it by two, and then check the powerIf the result is
we say passes Miller's test. If the result is not we say it fails Miller's test (since if is prime, the result would certainly be ). If the result is we continue. If we have arrived at a point where we can no longer divide
by two, we say passes Miller's test. Otherwise, assuming we continue by dividing the power itself by two and then taking to that new power. Once again, if the result is we say passes the test, and if it is not we say it fails.If the result is
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 by all possible powers of two and the result is still then we say passes the test.
Example 12.3.4.
Let's see a few examples of this. First, the number
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
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
Let's try Algorithm 12.3.3 with another number,
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