patternpythonMinor
Increase the speed of this Caesar Cipher
Viewed 0 times
thisthecaesarincreasespeedcipher
Problem
Here's my attempt at a Caesar Cipher encoder/decoder.
If given a key, it will encrypt the given string. However, if you do not specify a key, it checks each of the 26 possible keys and returns the one with the highest percentage of words that appear in this file of English words (with a couple additions including 'a' and 'I'). The program also returns the 'assurance' in its choice; for a specified key it is always 100%, for an unspecified key it is the percentage of words that are in English.
How can I improve the speed of the program? I have gone through 5 versions and improved the method that I use to decrypt each time, but now I would like to improve the speed. As I am a beginner, I am not very sure on how to do this.
The code is fairly readable, I think.
CaesarCipher_v5.py [47 lines]
```
# CaesarCipher_v5.py
#
# CSTAICH 2014
from string import maketrans, ascii_uppercase, ascii_lowercase
import re
from operator import itemgetter
from itertools import chain
input = ''
key = ''
# variable input and processing
while input == '': input = raw_input('Input ciphertext: ')
if key == '': key = raw_input('Input key; leave blank for auto-detection: ')
if key != '':
key = int(key)
if key >= 26: raise Exception("invalid key, must be [0-25]")
alphabet = list(chain(*zip(ascii_uppercase, ascii_lowercase)))
english_words = re.sub(r'\r', '', open('sowpods.txt', 'r').read().lower()).split('\n')
# =================================-------------------------------
def key_shift(input, key): # shifts an input by a key number of characters
return input.translate(maketrans(str(alphabet), str(alphabet[key 2:] + alphabet[:key 2])))
def english(sentence): #returns percentage of words in input that are english words
sentence = re.sub(r'[?,.!:;/]', '', sentence).split(' ') #strip punctuation and split into words
number_english_words = 0
number_words = len(sentence)
for word in sentence:
if word.lower() in english_words: number_english_words
If given a key, it will encrypt the given string. However, if you do not specify a key, it checks each of the 26 possible keys and returns the one with the highest percentage of words that appear in this file of English words (with a couple additions including 'a' and 'I'). The program also returns the 'assurance' in its choice; for a specified key it is always 100%, for an unspecified key it is the percentage of words that are in English.
How can I improve the speed of the program? I have gone through 5 versions and improved the method that I use to decrypt each time, but now I would like to improve the speed. As I am a beginner, I am not very sure on how to do this.
The code is fairly readable, I think.
CaesarCipher_v5.py [47 lines]
```
# CaesarCipher_v5.py
#
# CSTAICH 2014
from string import maketrans, ascii_uppercase, ascii_lowercase
import re
from operator import itemgetter
from itertools import chain
input = ''
key = ''
# variable input and processing
while input == '': input = raw_input('Input ciphertext: ')
if key == '': key = raw_input('Input key; leave blank for auto-detection: ')
if key != '':
key = int(key)
if key >= 26: raise Exception("invalid key, must be [0-25]")
alphabet = list(chain(*zip(ascii_uppercase, ascii_lowercase)))
english_words = re.sub(r'\r', '', open('sowpods.txt', 'r').read().lower()).split('\n')
# =================================-------------------------------
def key_shift(input, key): # shifts an input by a key number of characters
return input.translate(maketrans(str(alphabet), str(alphabet[key 2:] + alphabet[:key 2])))
def english(sentence): #returns percentage of words in input that are english words
sentence = re.sub(r'[?,.!:;/]', '', sentence).split(' ') #strip punctuation and split into words
number_english_words = 0
number_words = len(sentence)
for word in sentence:
if word.lower() in english_words: number_english_words
Solution
Your code would be much clearer if split into multiple functions, with clear inputs and outputs, e.g.:
This is much better than relying on scope for access to the objects your functions need.
You could then have a loop at the end to run it all, something like:
This improves the reusability of your code by allowing you to
In terms of speed, one obvious improvement would be to use a
As you can now loop multiple times, you could also think about storing the translations in a dictionary
def encode(plaintext, key):
...
def decode(ciphertext, key=None, dictionary=None):
...
def get_int_input(prompt, max_=26):
...This is much better than relying on scope for access to the objects your functions need.
You could then have a loop at the end to run it all, something like:
if __name__ == '__main__':
english_words = create_dictionary()
while True:
choice = get_int_input("1. Encode\n2. Decode\n3. Exit\n", 3)
if choice == 1:
plaintext = raw_input("Enter the plain text: ")
key = get_int_input("Enter the key: ", 26)
print encode(plaintext, key)
elif choice == 2:
ciphertext = raw_input("Enter the cipher text: ")
key = None
if raw_input("Do you know the key? (y/n) ".lower()) == "y":
key = get_int_input("Enter the key: ", 26)
plaintext, match = decode(ciphertext, key, dictionary)
print "{0} :: {1:.2f}".format(plaintext, match)
else:
breakThis improves the reusability of your code by allowing you to
import the functions elsewhere without actually running theIn terms of speed, one obvious improvement would be to use a
set, which provides very fast membership testing using hashing, for your dictionary. Also, you seem to be building it in an awkward way, try:def create_dictionary(filename='sowpods.txt'):
with open(filename) as f:
return set(line.strip().lower() for line in f)As you can now loop multiple times, you could also think about storing the translations in a dictionary
{key: translation}, so you only build them once.Code Snippets
def encode(plaintext, key):
...
def decode(ciphertext, key=None, dictionary=None):
...
def get_int_input(prompt, max_=26):
...if __name__ == '__main__':
english_words = create_dictionary()
while True:
choice = get_int_input("1. Encode\n2. Decode\n3. Exit\n", 3)
if choice == 1:
plaintext = raw_input("Enter the plain text: ")
key = get_int_input("Enter the key: ", 26)
print encode(plaintext, key)
elif choice == 2:
ciphertext = raw_input("Enter the cipher text: ")
key = None
if raw_input("Do you know the key? (y/n) ".lower()) == "y":
key = get_int_input("Enter the key: ", 26)
plaintext, match = decode(ciphertext, key, dictionary)
print "{0} :: {1:.2f}".format(plaintext, match)
else:
breakdef create_dictionary(filename='sowpods.txt'):
with open(filename) as f:
return set(line.strip().lower() for line in f)Context
StackExchange Code Review Q#60961, answer score: 3
Revisions (0)
No revisions yet.