Вы находитесь на странице: 1из 59

Computational discrete mathematics with Python

Fred Richman Florida Atlantic University

June 21, 2013

Contents

 0 Python 2 1 Integers and strings 3 1.1 Decimal and other bases . . . . . . . . . . . . . . . . . . . . . 5 2 Prime numbers 7 2.1 The smallest prime factor . . . . . . . . . . . . . . . . . . . . 7 2.2 Using a for-statement . . . . . . . . . . . . . . . . . . . . . . 9 2.3 Testing the algorithm . . . . . . . . . . . . . . 10 2.4 DeÖning a function . . . . . . . . . . . . . . . . . . . . . . . . 10 2.5 Speeding the algorithm up . . . . . . . . . . . . . . . . . . . . 11 2.6 Goldbachís conjecture . . . . . . . . . . . . . . . . . . . . . . 12 2.7 Sophie Germain primes . . . . . . . . . . . . 14 2.8 There are inÖnitely many primes 16 2.9 Largest prime factor . . . . . . . . . . . . . . . . . . . . . . . 19 2.10 Complete factorization . . . . . . . . . . . . . . . . . . . . . . 20 2.11 More on recursive functions . . . . . . . . . . . . . . . . . . . 21 2.12 Working with lists . . . . . . . . . . . . . . . . . . . . . . . . 23 3 The Euclidean algorithm 23 3.1 The Euler ' -function . . . . . . . . . . . . . . . . . . . . . . . 25 3.2 The extended Euclidean algorithm 27 3.3 Orders of units modulo n . . . . . . . . . . . . . . . . . . 29

1

 4 Sieving for primes 30 4.1 Counts . . . . . . . . . . . . . . . . . . . . . . . . . . 32 5 Mersenne primes 34 5.1 The Lucas-Lehmer test . . . . . . . . . . . . . . . 36 5.2 Repunits . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 5.3 Emirps and palindromic primes 37 6 Fermatís theorem 37 6.1 Carmichael numbers . . . . . . . . . . . . . . . . . . . . . . . 39 6.2 The Miller-Rabin test . . . . . . . . . . . . . . . . . . . 40 7 The sum of the squares of the digits of a number 41 7.1 Happy numbers . . . . . . . . . . . . . . . . . . . . . . . . . . 42 7.2 Numbers whose digits increase 44 7.3 Unhappy numbers . . . . . . . . . . . . . . . . . . . . . . . . . 46 7.4 Changing exponents and bases 47 8 Finding orbits 48 9 Aliquot sequences 51 10 The Collatz function 51 11 Bulgarian solitaire 52 12 The digits of n -factorial 53 12.1 Benfordís law . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 13 Sequences of zeros in 2 n 56 14 Ciphers 57 15 Stu§ 59

0 Python

You canít do computational mathematics without writing programs. In this course, all of the programs are to be written in Python. There is a lot of

2

material on Python on the web. The particular version Iím going to use is Python 2.7.3. The Örst thing you need to do is to download Python 2.7.3. You can do this at

http://www.python.org/getit/

After you have downloaded Python, open IDLE, the Python GUI (graph- ical user interface). This gives you an interactive window in which you can play with Python. The prompt line looks like

>>>

You can try various arithmetic operations Örst. If you type 2+7, and then ìEnterî, you should see

9

>>>

Try typing 2*7 and 2/7 and 2**7. The last one should give you 2 7 which is 128 . Now try 2**100. The L at the end of the number means that it is a ìlong integerî. Now type range(10). You should get a list of the Örst ten numbers, starting with 0 . Lists in Python are enclosed with square brackets, and the entries are separated by commas. Now try range(5,10) and range(-5,10) . Try range(a,b) with various values of a and b until you understand how it works. Now type range(0,100,7) and range(-20,20,3) . What do these look like? Try a few more. Exercise 1. Write down a description of what range(a,b) is for arbi- trary integers a and b. Exercise 2. Write down a description of what range(a,b,c) is for arbitrary integers a, b, and c.

1 Integers and strings

We will be working mostly with the integers:

: : : 5 ; 4 ; 3 ; 2 ; 1 ; 0 ; 1 ; 2 ; 3 ; 4 ; 5 ; : : :

3

Your Örst project is to familiarize yourself with the Python operator %. Get into the Python interactive mode (shell or console). One way to do this is just to open IDLE from the start menu. You should see the prompt:

>>>

If you type 2+3, and then ìEnterî, you should see

5

>>>

If you type 100%17, and then ìEnterî, you should see

15

>>>

Thatís because when you divide 100 by 17 you get a remainder of 15 . That is, 100 = 5 17 + 15 . I have no idea why they use the percent sign to indicate that functionó the same thing is done in the language C. What happens when you type 100%-17 or -100%17 or -100%-17? If you type 100/17, and then ìEnterî, you should see

5

>>>

Thatís because when you divide 100 by 17 you get a quotient of 5 (and a remainder of 15 ). So 100 is equal to (100/17)*17+(100%17) . Type that last expression into Python, in the interactive mode, and press ìEnterî. What do you get? Exercise 1. Come up with a description of a%b when a and b are arbi- trary integers. Exercise 2. Describe b/a for arbitrary integers a and b . Exercise 3. Despite its name, the division algorithm is a mathematical theorem, not an algorithm. One way of stating it is:

If a and b are integers, and a 6= 0 , then b = qa + r for unique integers q and r , with 0 r < j a j . The integer q is called the quotient, and the integer r , the remainder.

Your question is, what is the relationship between q and a/b, and between r and b%a? You can only Önd out by experimenting with Python. You are trying to describe how Python behaves. Experiment with both negative and positive values of a and b .

4

1.1 Decimal and other bases

When we write a number like 1776 , we use the standard decimal notation.

That is, we use ten digits,

their position. The Örst occurrence of the digit 7 in the string of digits ë1776 í stands for seven hundred; the second stands for seventy. The digit 1 stands for one thousand, and the digit 6 stands for six. That is,

0 ; 1 ; 2 ; 3 ; 4 ; 5 ; 6 ; 7 ; 8 ; 9 , and their value depends on

1776 = 1 1000 + 7 100 + 7 10 + 6 1

or

1776 = 1 10 3 + 7 10 2 + 7 10 1 + 6 10 0

Technically, we can distinguish the numeral ë1776 ífrom the number 1776 , just like we distinguish the three-letter word ëcatí from a cat. Another numeral that stands for 1776 is ëMDCCLXXVIí. We say that the string of digits ë1776 íis the decimal representation of the number 1776 , or the base-10 representation. If we want to get hold of the last digit in the decimal representation of the number we can use the Python operation %. The number n%10 is the last digit in the decimal representation of n. Try it with 1776 . Thatís because 1776 = 177 10+6 . If we want to get hold of the next to the last digit of 1776 , we look at 177%10. The number 177 is obtained by by writing 1776/10. Here is a short program that illustrates this:

n = 1776

print n, n/10, n%10

Run this program. You should see: 1776 177 6 . The two commas separate the three numbers on the same line, but they donít appear on that line with the numbers. Now we want to do the same thing with 177 , so we add two lines to the program:

n = 1776

print n, n/10, n%10

n = n/10

print n, n/10, n%10

The third line replaces n by n= 10 , that is, it will replace 1776 by 177 . The fourth line is the same as the second line because we want to see the same things, but with 177 rather than with 1776 .

5

Run this program. You should see

 1776 177 6 177 17 7

We can continue by repeating those two lines several more times:

n = 1776

print n, n/10, n%10

n = n/10

print n, n/10, n%10

n = n/10

print n, n/10, n%10

n = n/10

print n, n/10, n%10

n = n/10

print n, n/10, n%10

Maybe I went overboard there. Run this program. You should see

1776 177 6

177 17 7

17 1 7

1

0

1

0

0 0

Notice that we have picked o§ the four digits of 1776 as the last numbers on each line. I suppose we can consider the number 0 at the end of the Öfth line as the coe¢ cien of 10 4 in the representation of 1776 as

0 10 4 + 1 10 3 + 7 10 2 + 7 10 1 + 6 10 0

but we never write the digit 0 as the Örst digit in a number. So I should have quit as soon as n became 0 . We want to repeat the lines

print n, n/10, n%10

n = n/10

as long as n remains positive. But we donít want to write those lines over and over again. Instead we write

6

n = 1776

while n > 0:

print n, n/10, n%10 n = n/10

There are several things to notice here. The Örst is the colon after ìwhile n >

0î. That is essential. The second is that the next two lines are both indented. Thatís so Python will know to execute both of them until n becomes zero.

If you write this program in the IDLE editor (which you should), the colon

will cause the next line to be indented automatically. If we had written

n = 1776

while n > 0:

print n, n/10, n%10

n = n/10

our program would have printed out 1776 177 6 until doomsday. What do you think would happen if we had written

n = 1776

while n > 0:

print n, n/10, n%10

n = n/10

Try it.

 2 Prime numbers A prime number is an integer that is greater than 1 , but is not the product of two integers that are greater than 1 . The two smallest prime numbers are

2 and 3 . The number 4 is not prime because 4 = 2 2 ; the number 51 is not prime because 51 = 3 17 . The Örst twenty prime numbers are

2 ; 3 ; 5 ; 7 ; 11 ; 13 ; 17 ; 19 ; 23 ; 29 ; 31 ; 37 ; 41 ; 43 ; 47 ; 53 ; 59 ; 61 ; 67 ; 71

2.1 The smallest prime factor

We want to write a Python program that computes the smallest prime factor p of a given number n . That is, we want to implement Proposition 31 of Book VII of Euclidís Elements which says that any number greater than 1

is divisible by a prime.

7

You can do this in the interactive mode, but it is less harrowing to do it in the IDLE mode. In that mode, we work with a (built in) text editor on a Öle containing a Python program. You can create such a Öle from the IDLE interactive mode: open a new window by clicking on the ìFileî button and then choosing ìNew Windowî. An IDLE editing window comes up. You are now set to write some Python code which you can save afterwards by clicking on the ìFileî button and then choosing ìSave Asî. At that point you will have to choose where you want to save your Öle, and what its name will be. Call it ìÖddle.pyî, or something more descriptive, but with the ì.pyî extension. In the IDLE editing window, type the short version of the classic Örst program:

print "Hello"

If you are writing a program in the IDLE editing window, you have to use the word print in order to get any output. The quotation marks indicate that the word Hello is text. Without the quotation marks, Python would assume that it was a variable. You can use single quotes or double quotes. The single quote on my keyboard is on the same key as the double quote. I canít seem to get an appropriate single quote with the word processor Iím using. The best I can do is

print íHelloí

To run the program, press the F5 key. If you havenít named the program Öle before, you will be asked to do so now. Otherwise, Python will simply ask you if it is okay to save the Öle, which you will normally agree to. The output should then appear in another window called "Python shell". This window is essentially the interactive window that you worked with before. So you will normally have two windows going: one where you write your program, and one where you see your output. The output for this program should be Hello. Try running the program after removing the quotation marks. You should get an ugly error message in red saying that Hello is not deÖned. Without putting back the quotation marks, insert the line

Hello = 3

at the beginning of the Öle, before the print command. This will set the vari- able named Hello equal to 3. Run the program again to see what happens.

8

The smallest prime factor of n is the smallest integer p greater than 1 which divides n . We might as well assume that n 0 because if n < 0 , we could look at n instead. If n = 0 , then I guess its smallest prime factor is 2 ; what do you think? If n = 1 , then it has no prime factors. (Nobody considers 1 to be a prime, although one can argue that it is.) Those are the only exceptional cases: The number 0 is exceptional because it is smaller than its smallest prime factor; the number 1 is exceptional because it has no prime factor. So, for the computation, you may assume that n > 1 . You want to try to divide n successively by 2 ; 3 ; 4 ; 5 ; : : : until you are successful. You can do that with the function n % m . So you want to see when n % m is Örst equal to zero. The condition that n % m is equal to zero is written

n % m == 0

with two equal signs (as in C). A single equal sign is used in an assignment

to set the value of x equal to a + b . The expression

x == a + b doesnít say to do anythingó it has the value True if x is equal to a + b , and the value False if x is not equal to a + b . An expression that takes on the values True or False is called a boolean expression. Another boolean expression is x < y . A typical use of boolean expressions is in an if statement. The statement

statement: x = a + b says

if n % m == 0: print íHelloí

prints out ëHelloíif m divides n . The boolean expression is n % m == 0. Note the colon. You need that. It separates the boolean expression from the commands. Here we have only one command: print íHelloí. Exercise 1. What do you see if you print the boolean expression 3 % 2 == 0? The boolean expression 2 < 3? What if you print í2 < ?

2.2 Using a for-statement

Suppose n > 1 is an integer. We want to run through the integers m , starting at 2 , to see if any of them divide the positive integer n . When we come across one that does, weíll print it out so we can see it. There is at least one that does, namely n itself. We can do this as follows for the integer n = 91 :

n = 91

9

for m in range(2,n+1):

if n % m == 0:

print m

break

Note the colons after the for-statement and after the if-statement. The ìbreakî instruction says ìget out of this for-statementî. The indentations are very important in Python. They tell you that the for-statement goes on until the statement print m. The list range(2,n+1) consists of those integers m such that 2 m < n +1 . Note that 2 is an allowable value of m , but n +1 is not. In general, the set range(a,b) consists of those integers m such that a m < b . The value of m when we exit the for-loop will be the smallest integer greater than 1 that divides n . Exercise 1. Write this program in your Öle Öddle.py, or write it in a another Öle with a name like temp.py. Run this program using di§erent values of n . What can you say about n if the program prints out n ?

2.3 Testing the algorithm

You should test the program to see if it works. Of course you just could try it on a few selected numbers, and you should do that. Another thing you could do is print out what it does on the Örst 100 numbers greater than 1 (for example) and eyeball the results to see if they look okay. This, of course, would be done with another for-loop, which would start:

for n in range(2,102):

Inside that for-loop we have to put our original for-loop. Moreover, you should not just print out m , you should print out both n and m , so you will get a list of pairs: the number, and its smallest prime factor. Something like

print n,m

which prints out n followed by a space followed by m .

2.4 DeÖning a function

Better yet, you can deÖne a function that takes a natural number n > 1 and returns its least prime factor. Then you can call that function instead of having to worry about nested for-loops. You already have the required lines of code, you just have to write it down as a function:

10

def spf(n):

for m in range(2,n+1):

if n % m == 0:

return m

The Örst line says that we are deÖning a function called spf that will act on a variable n. The command ìreturn mî makes m the result of applying the function spf to n . You donít have to write break to get out of the loop because a return statement takes you out of the whole function. To test this function, write a for-loop that runs n from 2 to 50 , say, and print out n together with the smallest prime dividing n . Your print statement will look something like

print n, spf(n)

2.5 Speeding the algorithm up

This is a dumb algorithm. For one thing, itís pointless to see if m divides n when m 2 is greater than n . Why is that? Well, if m 2 > n and m divides n , then n=m < m also divides n so we would have already broken out of the loop when we tested the smaller number n=m . Once m 2 is greater than n , we already know that n is prime, so we should just return n . Notice that if n is around 1000000 , this means that you will be doing around 1000 tests rather than 1000000 tests, so this improvement is well worth doing. It doesnít take much, just (essentially) one more line:

def spf(n):

for m in range(2,n+1):

if n % m == 0:

return m if m*m > n:

return n

You can write this in fewer lines by putting the return-statements up on the same lines as the if-statements:

def spf(n):

for m in range(2,n+1):

if n % m == 0: return m

11

if m*m > n: return n

The upper limit n + 1 is only necessary when n = 2 , otherwise we could use the upper limit n (why is that?). We could add a line after the for-loop that returns 0 , so that the function will return something even if n is less than 2 , but we are really only interested in what happens when n 2 .

2.6 Goldbachís conjecture

Goldbachís conjecture is that you can write any even number greater than 2 as the sum of two primes. Here is a short table showing how that works:

 4 = 2 + 2 6 = 3 + 3 8 = 10 = 3 + 7 12 = 5 + 7 14 24 = = 5 + 19 3 + 11 16 26 = 3 + 23 3 + 13 = 3 + 5 5 + 13 28 = 5 + 23 18 = = 30 = 20 3 + 17 7 + 23 = 32 = 3 + 29 3 + 19 22

Some even numbers can be written as sums of two primes in more than one way. For example, 10 = 3 + 7 = 5 + 5 and 22 = 3 + 19 = 5 + 17 = 11 + 11 . Goldbachís conjecture is one of the big unsolved problems in mathematics. Nobody knows whether it is true or false. About twenty years ago, Apostolos Doxiadis wrote a novel called Uncle Petros and Goldbachís conjecture . The main character of this novel was determined to settle Goldbachís conjecture. The publishers o§ered a prize of one million dollars to anyone who proved Goldbachís conjecture within two years. As I recall, Uncle Petros goes o§ the deep end when he Önds out that it is conceivable that Goldbachís conjecture is true, yet not provable. Thatís from a famous result in logic by Kurt Gˆdel, who was a bit crazy himself. Be that as it may, we can write a program that tries to write even numbers as sums of two primes. The number 4 is an anomaly, as it is the only even number that can be written as the sum of two primes where one of the primes is 2 . In fact, Goldbachís conjecture is often stated in the form: any even number greater than 4 is the sum of two odd primes. So we want to run through some even numbers and try to write each one as the sum of two odd primes. An even number is a multiple of 2 , so we want

numbers of the form 2 n for n = 2 ; 3 ; 4 ; 5 ; 6 ; : : :. Letís start

out by running n from 2 to 16 , so that we will duplicate the results in the

table above. We can do that with the line

to run through

for n in range(2,17):

12

If 2 n is the sum of two primes, then the smaller prime is at most n , so we want to run through the primes p with p n . We also want 2 n p to be a prime. So we run through the numbers p with p n , and test to see if p and 2 n p are both primes. Thatís why we write the second line as follows:

for n in range(2,17):

for p in range(2,n+1):

To test whether p and 2 n p are both primes, we use an if-statement and our function spf. Here is what a third line might look like:

for n in range(2,17):

for p in range(2,n+1):

if spf(p) == p and spf(2*n-p) == 2*n-p:

print 2*n,"=",p,"+",2*n-p

Notice the use of our function spf to check whether p and 2 n p are primes. Not also the use of the word ìandî to make the if-statement dependent on two conditions. Finally, notice that we have to write 2*n, not 2n to indicate the number 2 n . Run this program. If everything is working right, it should print out all possible ways of writing each even number as the sum of two primes. Try using di§erent numbers for 17 . Exercise 1. Write a program that, instead of printing out the di§erent ways each even number can be written as the sum of two primes, prints the number of ways each even number can be written as the sum of two primes. So next to the number 90 it would print the number 9 , because 90 can be written in 9 ways as the sum of two primes. At least according to my calculations. Exercise 2. What is the smallest even number that can be written as a sum of two primes in 11 ways? What is the smallest even number that can be written as the sum of two primes in exactly 11 ways? Itís a little cryptic to use the function spf to see if a number is prime. Here is an function that does this directly. def prime(n):

if n < 2: return False for m in range(2,n):

if n % m == 0: return False return True

13

The purpose of the Örst line is to avoid returning True for negative numbers and for the numbers 0 and 1 . The next two lines test to see if n is divisible by a number m such that 2 m < n , and returns False if it is. Note that if n = 2 , then there are no such numbers m . If n does not get eliminated this way, then the last line returns True. A reÖnement on this function is to add a line that returns True as soon as m 2 > n , because in that case we already know, without further testing, that n must be prime. So we could write def prime(n):

if n < 2: return False for m in range(2,n):

if n % m == 0: return False if m*m > n: return True return True This reÖnement helps if we are testing a very large prime.

2.7 Sophie Germain primes

Sophie Germain, a French mathematician who lived from 1776 to 1831, worked on one of the most famous problems in mathematics: Fermatís last theorem. One of her results concerning this problem involved primes p such that 2 p + 1 is also a prime. These primes have come to be known as Sophie

Germain primes. The Örst few Sophie Germain primes are 2 , 3 , 5 , 11 , and

23 . You

2 11 + 1 = 23 , and 2 23 + 1 = 47 . The primes 7

Germain primes because 2 7 + 1 = 15 and 2 13 + 1 = 27 are not primes. Fermatís last theorem says that if p is an odd prime, then there are no solutions to the equation

x p + y p = z p

with x , y , and z positive integers. The so-called ìÖrst caseîof Fermatís last theorem is to show that there are no solutions with x , y , and z , not divisible by p . Sophie Germain proved that the Örst case of Fermatís last theorem is true if p is a Sophie Germain prime. Fermatís last theorem was not the last theorem that Fermat proved, or the last theorem that Fermat claimed to be true. For a long time it was the only theorem of Fermat that remained unproved, among the theorems that he had claimed were true. In 1995, Andrew Wiles published a proof of Fermatís last theorem, 358 years after it was stated.

and 13 are not Sophie

can see that 2 2+ 1 = 5 is a prime, as are 2 3+ 1 = 7 , 2 5+ 1 = 11 ,

14

If p is a Sophie Germain prime, then the prime q = 2 p + 1 is said to be

a safe prime. This term has its origin in cryptography. It refers to the fact that q 1 does not have many prime factorsó in fact it has only two: 2 and

p . That makes the prime q useful for certain encryption algorithms. A prime

q is safe exactly when the number ( q 1) = 2 is prime. The largest known Sophie Germain prime is

18543637900515 2 666667 1

A Cunningham chain (of the Örst kind) is a sequence of primes such that each one is twice the preceding one plus 1 . So the sequence 2 , 5 , 11 , 23 , 47 is

 a Cunningham chain. This chain cannot be extended because 2 47 + 1 = 95 is not a prime. Another Cunningham chain is the sequence 89 , 179 , 359 , 719 ,

1439 , 2879 . The longest known Cunningham chain consists of 17 primes, the Örst of which is 2759832934171386593519 . A Cunningham chain is complete if it cannot be extended, in either di- rection, to form a larger Cunningham chain. So the Örst term in a complete Cunningham chain is not a safe prime, and the last term is not a Sophie

Germain prime. Note that in the Cunningham chain 89 , 179 , 359 , 719 , 1439 , 2879 , the number 89 is not safe because (89 1) = 2 = 44 is not a prime, and 2879 is not a Sophie Germain prime because 2 2879 + 1 = 5759 = 13 443

is not a prime.

Here is a simple function that returns True when applied to numbers that are prime but not safe (unsafe primes), and False otherwise. def unsafe(n):

if prime(n) and not prime((n-1)/2): return True return False Exercise 1. Write a program to Önd the longest Cunningham chain whose Örst prime is less than 1000 . This chain will necessarily be complete. Exercise 2. One can think of the primes as being partitioned into com- plete Cunningham chains. The chains of length one are the primes that are neither Sophie Germain primes nor safe primes. The Örst such chain is 13 . What is the Örst chain of length two? Exercise 3. Write a program that lists all the Sophie Germain primes

p less than 1000 that are not safe primes, together with the length of the complete Cunningham chain starting at p .

15

2.8 There are inÖnitely many primes

In Book VII, Proposition 37, Euclid proves that any number greater than 1 is divisible by a prime number. The function spf(n) is an implementation of that proposition: you give it a number n > 1 , and it returns a prime number

that divides n . Of course what spf(n) actually does is to return the smallest number p > 1 that divides n . This number p is necessarily prime because if

p had a nontrivial factor, that factor would be smaller than p and would also divide n . There is a great presentation of Euclidís elements by David Joyce on the web, complete with Java applets to illustrate the propositions. The url is

aleph0.clarku.edu/~djoyce/java/elements/elements.html

Click on the number of the book that you want, then click on the proposition that you are interested in. In Book IX, Proposition 20, Euclid proves that prime numbers are more than any assigned multitude of prime numbers . By that he meant that if you are given any (Önite) list of prime numbers, then you can construct a prime number that is not on that list. This is a positive formulation of the statement that there are inÖnitely many primes. We can implement this proposition of Euclid also. Euclidís proof says to multiply all the primes in the given list together to get a product P , and then consider the number P + 1 . The number P + 1 cannot be divisible by any of the primes on the list, because P is divisible by all those primes. But Proposition 37 of Book VII says that P + 1 is divisible by some prime. So that prime cannot be on the list. This procedure can be used to generate a sequence of primes, starting from nothing. To get a prime, we have to start with a list of primes, but we can take the initial list to be empty! What is the product P of the primes on an empty list? As we shall see, when we actually write down the program, the product of an empty list of numbers is equal to 1 . That might seem a

little surprising, but it turns out to be very natural. So P + 1 is equal to 2 , the Örst prime generated by Euclidís construction. So our list starts out empty, which in Python is the list [ ]. Then we get the list . Applying Euclidís construction to that list gives P = 2 and

P +1 = 3 . So the next prime we get is 3 , and our list of primes becomes [2,3].

Continuing in this way, you can see that we get the prime 7 = 2 3 + 1 , then the prime 43 = 2 3 7+1 . The next number we consider is 2 3 7 43+1 = 1807 .

16

But 1807 = 13 139 is not prime. So, since we are taking the smallest prime dividing P + 1 , the next prime on our list is 13 . Here is a program that multiplies all the numbers on a list L together:

P = 1

for i in L: P = P*i

After this program has executed, the variable P will be the product of the numbers in the list L. For example, if L = [2,3,5] , then P will start out as 1 , then become P i = 1 2 = 2 , then P i = 2 3 = 6 , then 6 5 = 30 . You

can see that if the list L is empty, then P remains equal to 1 , its initial value, because there are no numbers i in L. So how do we implement Book IX, Proposition 20? First we decide how many times we want to repeat the basic construction. Letís call that number

m , and set it equal to 5 at Örst. So our Örst line will be m = 5. Then we set

up the list L which is initially empty. So our second line will be L = [ ]. Then have some variable run through the numbers from 0 to m 1 . It makes no di§erence what we call that variable, the idea is simply to repeat the basic construction m times. Then we multiply all the numbers in the list L , as before. So our Örst few lines will be

m = 5 = [ ]

L

for n in range(m):

P = 1

for i in L: P = P*i

Now what? The construction says to Önd a prime that divides P + 1 . So our next line should be to compute spf(P+1). Call that prime p and print it out so we can see whatís happening. Then, and this is crucial, we have to tack

p onto the end of the list L. Perhaps the easiest way to do that is with the

command L.append(p) . Another way is L = L + [p] , or even L += [p] . We can use a plus sign to put two lists together. I Önd that easier to remember, but you have to be sure you are putting two lists together, so we need to write [p] rather than p. So our program becomes

m = 5 = [ ]

L

for n in range(m):

P = 1

for i in L: P = P*i

17

p = spf(P+1) print p L.append(p)

Notice that this program calls on the function spf, so the deÖnition of that function has to be in our Öle also. When I try to run this program for m = 9 , Python, or my computer, gets angry because it canít hold a list of the size required in the function spf. Because of that, I modiÖed the program for spf so that it doesnít use the range operation, which is what creates the big list. I rewrote it this way:

def spf(n):

i = 2 while i*i < n+1:

if n%i == 0: return i

i = i+1 return n

Do you see what that does? I got rid of the for-statement and replaced it by a while-statement. The indented commands below the while-statement keep repeating as long as the condition i 2 < n + 1 holds. So we have to change i by hand, so to speak, whereas before the for-statement changed it automatically. If we get to the point where the condition i 2 < n + 1 fails to hold, then we know that n is prime because we have tried to divide n by on all numbers whose squares are at most n . With this new deÖnition, I managed to get the program to run with m = 14 . The fourteenth prime that it generated was 5471 . With a little more patience on my part, it ran with m = 15 . The Öfteenth prime generated was 52662739 . What is the eighth prime in this sequence? If your program is taking a long time to factor some large number, there is help on the web. A good site is www.alpertron.com.ar/ECM.HTM which will factor very large numbers in very little time. There are at least two other ways to implement Book IX, Proposition 20. The Örst is to multiply the Örst few primes together, add one, and Önd a prime factor of the result. So we would start with 2 . As 2 + 1 = 3 is prime, the next prime we get is 3 . As 2 3 + 1 = 7 is prime, the next prime we get

31 is prime, the next prime we get is 31 . See how this our Örst in which we looked at 2 3 7+1 = 43 , omitting

the prime 5 . The next prime we get in this new sequence is 2 3 5 7+1 = 211 ,

is 7 . As 2 3 5 + 1 = algorithm di§ers from

18

which is also prime. If 211 were not a prime, we would calculate its smallest prime factor. Exercise 1. Write a program that computes this sequence. Do you always get primes without factoring, or do we sometimes get a composite number which we have to factor to get our new prime? Another way to show there are inÖnitely many primes is to show that for each number n , prime or not, there is a prime greater than n . In a way, this is the simplest algorithm: we simply Önd a prime factor of n ! + 1 . That number cannot be divisible by a prime i n , because if i n , then i divides n ! . The Örst few numbers we get that way are 1! + 1 = 2 , 2! + 1 = 3 , and 3! + 1 = 7 . The next number is 4! + 1 = 25 , which is not a prime but all of its prime factors are bigger than 4 . The next number is 5! + 1 = 121 which (I think) is a prime. Exercise 2. Write a program that uses this procedure to Önd a prime greater than n for as many numbers n as you can. Print out n , followed by the prime that is greater than n , for each number n .

2.9 Largest prime factor

Suppose we want to calculate the largest prime factor of a number n . Here is a plan. First compute the smallest prime factor p of n . We already have a program for that. If p = n , then p is the largest prime factor of n . Otherwise, the largest prime factor of n is equal to the largest prime factor of n=p . Thatís because p is the smallest prime factor of n , and n = p n=p . How to we turn this plan into a program? The Örst couple of lines are clear:

def Lpf(n):

 p = spf(n) if p == n: return n

Now what? What we want to do now is Önd the largest prime factor of n=p . That is the function that we are in the middle of deÖning! In fact, we can use that function as part of its own deÖnition. This is known as a recursive deÖnition. So we simply add one more line:

def Lpf(n):

 p = spf(n) if p == n: return n

19

return Lpf(n/p)

This works because n=p is always smaller than n . What actually happens when we compute Lpf(50)? The Örst line of the deÖnition calculates the smallest prime factor p = 2 of 50 . As 2 is not equal to 50 , we go to the third line which has us compute Lpf(25). Now we are at the Örst line of the program with n = 25 , which calculates the smallest prime factor p = 5 of 25 . As 5 is not equal to 25 , we go to the third line which computes Lpf(5). To do that, we Önd the smallest prime factor p = 5 of 5 . But then n = p , so, in accordance with the second line of the deÖnition, we return 5 , the largest prime factor of 50 .

2.10 Complete factorization

How do we get a program to tell us what all the prime factors of a number are? We want to enter a number n and have the output be a list of the primes dividing n . Probably we want to allow repetitions on this list, so when we enter the number 72 , the list that gets returned is [2 ; 2 ; 2 ; 3 ; 3] rather than just [2 ; 3] . Note that a list is Python is written as a sequence of elements, separated by commas, and enclosed in square brackets. If we ìaddîtwo lists in Python, we get the concatenation of the two lists. Thus

 +  [1 ; 2 ; 10] + [2 ; 13]

= [1 ; 2] = [1 ; 2 ; 10 ; 2 ; 13]

 + [] = 

The natural way to handle this problem is via a recursive function, one that calls on itself. Letís denote the function we want to deÖne by allpf ( n ) . Then what we want to do is Örst compute

m = spf ( n )

and then form a list allpf ( n ) whose Örst element is m and whose remaining elements form the list allpf ( n=m ) . The list allpf ( n ) can be constructed in Python as the sum of two lists:

allpf ( n ) = [ m ] + allpf ( n=m )

20

This last equation is the recursion: we deÖne allpf in terms of itself. How does it work in practice? If n = 12 , then m = spf (12) = 2 , and n=m = 6 , so we have

allpf (12) =  + allpf (6)

Proceeding, we get

allpf (6) =  + allpf (3)

so

allpf (12) =  +  + allpf (3)

Finally,

allpf (3) =  + allpf (1) =  + [] = 

so

allpf (12) =

 +  +  =  + [2 ; 3] = [2 ; 2 ; 3]

This works because of two things. One is that the number n=m that we apply the function allpf to on the right, is always smaller than the number n that we apply allpf to on the left. The other is that if n is small enough (equal to 1 ), then we know directly what allpf ( n ) is. So here is the general idea. If n = 1 , then return the empty list. If n > 1 , then set m = spf ( n ) , and return the list [m ] + allpf ( n=m ) . Here is a formal deÖnition in Python:

def allpf(n):

if n == 1: return [] m = spf(n) return [m] + allpf(n/m)

2.11 More on recursive functions

The prototype recursive function is one that computes the factorial function, the product of the integers from 1 to n :

n ! = n ( n 1) ( n 2) 3 2 1

The key fact is that n ! is equal to n times ( n 1)! , at least if n is positive, as you can see by looking at the product. Thus if we could compute ( n 1)! we

21

would know how to compute n ! . The computation for 4! would go as follows:

 4! = 4 3! 3! = 3 2! 2! = 2 1! 1! = 1 0!

but now we canít go further, we have to know what 0! is. Well, 0! is equal to 1 . Thatís our exit condition. Then we work our way backwards through the equations to Önd that

 1! = 1 0! = 1 1 = 1 2! = 2 1! = 2 1 = 2 3! = 3 2! = 3 2 = 6 4! = 4 3! = 4 6 = 24

The program would look like:

def fac(n):

if n == 0: return 1 return n*fac(n-1)

What happens when it runs on n = 4 ? When we run fac(4), it tries to return 4*fac(3) , so it has to run fac(3). We now have two programs running at once: fac(4) and fac(3), with fac(4) waiting for the result from fac(3). Similarly fac(3) tries to return 3*fac(2) , so it has to run fac(2). We now have three programs running at once. And so on. We can indicate this by a table:

program returns

 fac(4) 4*fac(3) fac(3) 3*fac(2) fac(2) 2*fac(1) fac(1) 1*fac(0) fac(0) 1

At the end we have Öve fac programs running at once. But fac(0) knows what to do without calling fac any more: it returns 1. Now fac(0) = 1 is passed to the fac(1) program giving the result fac(1) = 1*1 = 1. Then fac(1) = 1 is passed to the fac(2) program giving the result fac(2) = 2*1 = 2. This result is passed to the fac(3) program, yielding fac(3) = 3*2 = 6, and Önally this result is passed to the fac(4) program yielding fac(4) = 4*6 = 24.

22

2.12 Working with lists

A list is a sequence indexed by the integers 0 ; 1 ; 2 ; : : : ; n 1 . The n elements

, a[n-1] . The number n is the

length of the list and can be accessed by writing len(a). A string s can be thought of as a special type of list whose elements are alphanumeric characters. Its length is also denoted len(s), but a string is handled a little bit di§erently from a list. The simplest way to create a list is by the statement

a = []

of the list a are denoted a, a, a,

which creates a list with no elementsó an empty list. An empty list has

length 0 . So, if after deÖning the list a above, you printed out len(a), you should see 0 . You can put an element m onto the end of an array a by writing a.append(m).

If a is an empty list, then a.append(17) changes a into the list . If you

want a list containing the squares of the numbers from 0 to 9 , you can get

one by Örst setting a = [] and then running the loop

for i in range(0,10): a.append(i*i)

We can stick an element m onto the beginning of the list a by writing

a.insert(0,m). If take the list a generated by the loop above, and then write a.insert(0,22) , the list a would become [22,0,1,4,9,16,25,36,49,64,81] . Actually, I have trouble remembering how to use append and insert. I Önd

it easier to use addition of lists. So instead of writing a.append(m), I write

a = a + [m], and instead of writing a.insert(0,m), I write a = [m] + a. The general idea is that when you add two lists, the result is a list consisting of the two lists put together. The list

[22,0,1,4,9] + [16,25,36,49,64,81]

is the list

[22,0,1,4,9,16,25,36,49,64,81]

3 The Euclidean algorithm

The Euclidean algorithm is one of the oldest and best algorithms we have.

It computes the greatest common divisor of two positive integers a and b .

23

You can Önd it in Book VII Proposition 2 of Euclidís Elements. Euclid also showed that any number that divides both a and b divides their greatest common divisor. I think of that fact as being the ìalgebraic gcd propertyî. Here is a short program that computes the gcd :

def gcd(a,b):

while a!=0: a,b = b%a,a return abs(b)

The Euclidean algorithm is most naturally written recursively. Euclid pointed out that the common divisors of a and b (those numbers that divide both a and b ) are the same as the common divisors of a and b a . So if 0 < a b , we can reduce the problem of Önding gcd ( a; b ) to that of Önding gcd( a; b a ) . The latter is a simpler problem because the numbers are smaller (certainly their sum is smaller). We can reÖne that observation a bit for our purposes by noticing that we can repeatedly subtract a from b until we get a number that is smaller than a . Actually, Euclid noted that also: he talked about repeatedly subtracting the smaller number from the larger number. If you repeatedly subtract a from b until b becomes smaller than a , you end up with the remainder you get when you divide b by a. (Recall that division of positive integers can be thought of as repeated subtraction.) So using the Python % notation for the remainder, the key equation is

gcd ( a; b) = gcd ( b % a; a)

Here the b % a is written Örst because it is (strictly) smaller than a . Of course b % a might be equal to zero, in which case we are done because gcd (0 ; a) = a . This gives us our exit condition from the recursion: if a is 0 , then gcd ( a; b) = b . The usual convention is that gcd (0 ; 0) = 0 even though, strictly speaking, there is no greatest common divisor of 0 and 0 . However, zero is obviously the algebraic gcd of 0 and 0 because any number that divides both 0 and 0 divides zero (and zero is the only number with that property). Rather than making your algorithm foolproof, at least at Örst, just make it work when it is handed integers a and b with 0 a b . Use the exit condition

gcd(0 ; b ) = b

and the recursion displayed above. Test your algorithm on a few small pairs a and b where you know what the answer is.

24

There is a built-in Python function that will return gcd ( a; b) . You can import it from the module fractions. If you put the line from fractions import gcd at the top, then you can use their function gcd(a,b) in your programs.

3.1 The Euler ' -function

Two positive integers a and b are said to be relatively prime, or coprime, if they have no common divisors except for 1 . Notice that 1 is relatively prime

to any positive integer. Itís easy to see that two numbers a and b are relatively prime exactly when gcd ( a; b) = 1 , so the Euclidean algorithm provides a test for when a and b are relatively prime. The Euler ' -function is deÖned, for

n > 1 , by setting ' ( n ) equal to the number of positive integers less than n

that are relatively prime to n . Thus ' (10) = 4 because the numbers less than 10 that are relatively prime to 10 are 1 , 3 , 7 , and 9 . Usually people set ' (0) = 0 and ' (1) = 1 , but we really donít care about those values, especially ' (0) . We can justify ' (1) = 1 by modifying the deÖnition of

' ( n ) to read ìthe number of positive integers less than or equal to n that

are relatively prime to n .îThis, in fact, is the usual deÖnition. It also gives

' (0) = 0 . Once you get your gcd function working, you can use it to compute the

' function. For each m just run through the numbers 1 ; 2 ; : : : ; m , checking

each one to see if it is relatively prime to m , and counting how many times that happens. After writing such a function, test it by printing out, in two columns, the values m and ' ( m ) for m = 2 ; : : : ; n . The Örst few values of the Euler ' -function are

 n 0 1 2 3 4 5 6 7 8 9 10 ' ( n ) 0 1 1 2 2 4 2 6 4 6 4

Verify this table. As a Önal check on your ' function, you can compute it in a di§erent way. The formula is

' ( n ) = n Q P 1

p

2

p

1

where P is the set of primes that divide n . So

' (10) = 10 1 1 2 1

25

1

5

and

' (12) = 12 1 1 2 1

1

3

If you write it that way, you will be dealing with real numbers, at least as intermediate values. In order to deal only with integers, you can rewrite the formula as

n Q P 1 p = n Q

p

2

1

p

2

P p 1

p

=

n

Q p 2 P p

Q

P ( p 1)

p

2

So for n = 10 , the set P is f2 ; 5 g and the product Q p 2 P p is 10 . The product Q p 2 P ( p 1) is 1 4 = 4 so

' (10) = 10 4 = 4

10

For n = 12 , the set P is f 2 ; 3 g and the product Q p 2 P p is 6 . The product Q p 2 P ( p 1) is 1 2 = 2 so

' (12) = 12 6 2 = 4

You can use allpf(n) to Önd the set P . What you need to do is to eliminate the duplicates from the list that allpf(n) returns. Itís probably better to modify the function allpf(n) so that it doesnít return any duplicates. When you Önd the smallest prime dividing n , keep dividing n by that prime until you canít anymore, then go on to Önd the smallest prime dividing whatís left. You should probably give the modiÖed function a di§erent name. Letís do it. Weíll call the function allpd(n) for ìall prime divisorsî.

def allpd(n):

if n == 1: return [] i = 2 while i*i < = n:

if n%i == 0:

#the number 1 has no prime divisors #the number 2 is the smallest prime

#looking for the smallest prime divisor of n #found it!

while n%i == 0: n = n/i return [i] + allpd(n)

#keep dividing n by i #put i in front of the list

i = i+1 return [n]

#increase i and keep trying #n must be prime, so the list is [n]

Once you get a list A whose entries are the elements of the set P with no duplicates, you can easily compute the two required products Q p 2 P p and

26

Q p 2 P ( p 1) . Now you can get a third column to your output consisting of the values of ' ( n ) computed by this formula, and you can see if it agrees with the values of ' ( n ) that you got by counting. Eulerís Theorem. Fermatís theorem says that if n is a prime, and n does not divide a , then a n 1 1 (mod n ) . When n is a prime, then ' ( n ) = n 1 , and n does not divide a exactly when a and n are relatively prime. So we can rewrite Fermatís theorem as ìif n is a prime, and a and n are relatively prime, then a ' ( n ) 1 (mod n ) . Euler showed that this is true even when n is not prime. So, for example, 2 6 1 (mod 9) , as you can easily check. Indeed, you should write a program that checks Eulerís theorem for the numbers n from 2 to 100 .

3.2 The extended Euclidean algorithm

The extended Euclidean algorithm is a reÖnement of the Euclidean algorithm that Önds integers s and t so that sa + tb divides both a and b . If sa + tb is positive, which we can always arrange, it follows that sa + tb is the greatest common divisor of a and b , which is why this is a reÖnement of the Euclidean algorithm The equation

sa + tb = gcd ( a; b)

is known as Bezoutís equation. Usually either s or t is negative. An interesting aspect of Bezoutís equation is that if the left-hand side, sa + tb , divides both a and b , then sa + tb must be the greatest common divisor of a and b . Indeed, if d is any common divisor of a and b , then d must divide sa + tb (by the distributive law) so, if everything is positive, d must be smaller (or equal to) sa + tb . We will assume that 0 a b . If thatís not the case, we can always replace a and b by their absolute values, and interchange them if necessary. The simplest, if not the most understandable, algorithm is a recursive one. The ìexit strategyî is that if a = 0 , then we will choose s = 0 and t = 1