Week 3, Problem 2: Caesar (De)Cipher
[35 points; individual or pair]: hw3pr2.py

 

This problem asks you to write and test two functions (perhaps with helper functions, as well):

encipher( S, n )

and

decipher( S )


encipher

encipher( S , n ) takes as input a string S and a non-negative integer n between 0 and 25. This function returns a new string in which the letters in S have been rotated by n characters. For this problem, you should assume that upper-case letters are "rotated" to upper-case letters, lower-case letters are "rotated" to lower-case letters, and all non-alphabetic characters are left unchanged. For example, if we were to shift the letter 'y' by 3, we would get 'b' and if we were to shift the letter 'Y' by 3 we would get 'B'. (In python, you can use the test if 'a' <= c <= 'z': to determine if a character c is between 'a' and 'z' in the alphabet.)

You can write encipher any way you like as long as you use functional programming. You may wish to write a helper function that "rotates" a single character by n spots, wrapping around the alphabet as appropriate. Then, you might use this helper function to encipher your string. It's up to you how you do this! (Remember that uppercase letters wrap around the alphabet to uppercase letters, and lowercase letters wrap always to lowercase letters.)

decipher

On the other hand, decipher will be given a string of English text shifted by some amount already; decipher should return the original English string, to the best of its ability. Note: some strings have more than one English "deciphering." What's more, it is very difficult to handle short strings correctly. Thus, your decipher does not have to be perfect. However, you could use letter frequencies -- a function is provided at the bottom of this page; feel free to cut-and-paste it into your HW file. Scrabble scores have also been suggested!?! You might want also to use some additional "heuristics" (rules of thumb) of your own design. Also, you are welcome to write one or more small "helper" functions that will help decipher. Be sure to describe whatever strategies you used in writing your decipher function in a detailed comment above your decipher function.

As a reminder, the built-in functions ord and chr convert from single-character strings to integers and back. For example, ord('a') outputs 97 and chr(97) outputs 'a'. Please use these two functions when working on this problem!

Some examples:

>>> encipher('xyza', 1)

'yzab'

 

>>> encipher('Z A', 1)

'A B'

 

>>> encipher('*ab?', 1)

'*bc?'

 

>>> encipher('This is a string!', 1)

'Uijt jt b tusjoh!'

 

>>> encipher('Caesar cipher? I prefer Caesar salad.', 25)

'Bzdrzq bhogdq? H oqdedq Bzdrzq rzkzc.'

 

>>> decipher('Bzdrzq bhogdq? H oqdedq Bzdrzq rzkzc.')

'Caesar cipher? I prefer Caesar salad.'

 

>>> decipher('Hu lkbjhapvu pz doha ylthpuz hmaly dl mvynla lclyfaopun dl ohcl slhyulk.')

'An education is what remains after we forget everything we have learned.'

 

>>> decipher('Onyx balks')

'Edon rqbai'  # wrong! it was 'Onyx balks' but there might be another rule I could add...

 

Here is the letter-probability function and its source:

# table of probabilities for each letter...

def letProb( c ):

""" if c is the space character or an alphabetic character,

we return its monogram probability (for english),

otherwise we return 1.0 We ignore capitalization.

Adapted from

http://www.cs.chalmers.se/Cs/Grundutb/Kurser/krypto/en_stat.html

"""

if c == ' ': return 0.1904

if c == 'e' or c == 'E': return 0.1017

if c == 't' or c == 'T': return 0.0737

if c == 'a' or c == 'A': return 0.0661

if c == 'o' or c == 'O': return 0.0610

if c == 'i' or c == 'I': return 0.0562

if c == 'n' or c == 'N': return 0.0557

if c == 'h' or c == 'H': return 0.0542

if c == 's' or c == 'S': return 0.0508

if c == 'r' or c == 'R': return 0.0458

if c == 'd' or c == 'D': return 0.0369

if c == 'l' or c == 'L': return 0.0325

if c == 'u' or c == 'U': return 0.0228

if c == 'm' or c == 'M': return 0.0205

if c == 'c' or c == 'C': return 0.0192

if c == 'w' or c == 'W': return 0.0190

if c == 'f' or c == 'F': return 0.0175

if c == 'y' or c == 'Y': return 0.0165

if c == 'g' or c == 'G': return 0.0161

if c == 'p' or c == 'P': return 0.0131

if c == 'b' or c == 'B': return 0.0115

if c == 'v' or c == 'V': return 0.0088

if c == 'k' or c == 'K': return 0.0066

if c == 'x' or c == 'X': return 0.0014

if c == 'j' or c == 'J': return 0.0008

if c == 'q' or c == 'Q': return 0.0008

if c == 'z' or c == 'Z': return 0.0005

return 1.

 

Hint: For decipher you might have your program "look at" all 26 possible rotations of the input string and then decide on which is "best"... .

 

Submission

You should submit your hw3pr2.py file at Canvas .

Next

hw3pr3

Homework 3