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(
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.)
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