Is anyone here working on 6.189 Homework 1: Exercise OPT.2 – Secret Messages? If so, one of the key concepts is how modular arithmetic works, which is implemented as the modulus, or remainder, operator, %
. It’s like a clock, especially a 24-hour one, as it cycles repeatedly through the same series of times each day. It’s useful to note that the cycle continues uninterrupted even if we go negative, that is, if the left operand goes from 1
to 0
to -1
, etc.
Following is a 24-hour clock example, using a modulus of 24
. It will execute until the user enters 0
. Enter positive or negative integers as input, or 0
, when you get bored …
# TwentyFourHourClock.py
import random
while True:
t = random.randint(0, 23)
print "The clock says " + str(t) + "."
delta = int(raw_input("By how many hours should be change the time? "))
if delta == 0:
print "Bye!"
break
new_t = (t + delta) % 24
print "The clock now says " + str(new_t) + "."
Here’s some interaction with IDLE, with a modulus of 26
, the number of letters in the alphabet, as a segue into the Secret Message problem …
>>> 4 % 26
4
>>> 3 % 26
3
>>> 2 % 26
2
>>> 1 % 26
1
>>> 0 % 26
0
>>> -1 % 26
25
>>> -2 % 26
24
>>> -3 % 26
23
>>>
While it is obvious how to handle remainders with positive numbers, the above verifies that as we turn the clock backwards, or the alphabet, in this case, the cycle continues. So, we can easily cycle through X
, Y
, Z
, A
, B
, C
…, or the reverse, as in C
, B
, A
, Z
, Y
, X
…, etc. This can help us handle both positive and negative shift values.
The given example output is …
Enter sentence to encrypt: Mayday! Mayday!
Enter shift value: 4
The encoded phrase is: Qechec! Qechec!
So M
shifts forward by 4
letters to Q
, and y
goes forward by 4
letters, and wraps around, like a clock, to c
.
Here is a solution to the Secret Messages problem, open for discussion …
# SecretMessages.py
# MIT 6.189 Homework 1 Exercise OPT.2
phrase = raw_input("Enter sentence to encrypt: ")
# Ask for shift value; convert user input string to int
shift = int(raw_input("Enter shift value: "))
# Start with an empty string for the encoded phrase
encoded_phrase = ""
asc_a = ord("a")
asc_A = ord("A")
# Loop through each character in the phrase; use c to represent it
for c in phrase:
# Get the ascii code for the character
asc = ord(c)
if asc_a <= asc <= asc_a + 26:
# c is a lowercase letter; encode it.
asc_enc = asc_a + (asc - asc_a + shift) % 26
elif asc_A <= asc <= asc_A + 26:
# c is an uppercase letter; encode it.
asc_enc = asc_A + (asc - asc_A + shift) % 26
else:
# c is not a letter; do not encode it.
asc_enc = asc
# Add the character to the encoded phrase.
encoded_phrase = encoded_phrase + chr(asc_enc)
print "The encoded phrase is: " + encoded_phrase
Output, using a negative shift value …
>>>
Enter sentence to encrypt: Mechanical MOOC!
Enter shift value: -1
The encoded phrase is: Ldbgzmhbzk LNNB!
>>>
The line that performs the shifting of the ascii value for lowercase letters is this one …
asc_enc = asc_a + (asc - asc_a + shift) % 26
We calculate the difference between the ascii code of the letter being encoded and the ascii code for a
. Then we add in the shift value and apply the %
operator using 26
as the divisor. The result gets added to the ascii code for a
. That gives us the ascii code of the letter that will be put into the encrypted message in place of the original letter.