I am writing a program to decrypt a message and only given assumption that the maximum occur letter of decrypted message is "e". No shift number is given. Below code are my workdone. I can only hardcode the shift number to decrypt the given message, but if the message changed my code of course didn't work.
from collections import Counter
import string
message = "Zyp cpxpxmpc ez wzzv fa le esp delcd lyo yze ozhy le jzfc qppe Ehz ypgpc rtgp fa hzcv Hzcv rtgpd jzf xplytyr lyo afcazdp lyo wtqp td pxaej hteszfe te Escpp tq jzf lcp wfnvj pyzfrs ez qtyo wzgp cpxpxmpc te td espcp lyo ozye esczh te lhlj Depaspy Slhvtyr"
#frequency of each letter
letter_counts = Counter(message)
print(letter_counts) # Print the count of each element in string
#find max letter
maxFreq = -1
maxLetter = None
letter_counts[' '] = 0 # Don't count spaces zero count
for letter, freq in letter_counts.items():
print(letter, ":", freq)
maxLetter = max(letter_counts, key = letter_counts.get) # Find max freq letter in the string
print("Max Ocurring Letter:", maxLetter)
#right shift for encrypting and left shift for descripting.
#predict shift
#assume max letter is 'e'
letters = string.ascii_letters #contains 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
shift = 15 #COMPUTE SHIFT HERE (hardcode)
print("Predicted Shift:", shift)
totalLetters = 26
keys = {} #use dictionary for letter mapping
invkeys = {} #use dictionary for inverse letter mapping, you could use inverse search from original dict
for index, letter in enumerate(letters):
# cypher setup
if index < totalLetters: #lowercase
# Dictionary for encryption
letter = letters[index]
keys[letter] = letters[(index shift) % 26]
# Dictionary for decryption
invkeys = {val: key for key, val in keys.items()}
else: #uppercase
# Dictionary for encryption
keys[letter] = letters[(index shift) % 26 26]
# Dictionary for decryption
invkeys = {val: key for key, val in keys.items()}
print("Cypher Dict", keys)
#decrypt
decryptedMessage = []
for letter in message:
if letter == ' ': #spaces
decryptedMessage.append(letter)
else:
decryptedMessage.append(keys[letter])
print("Decrypted Message:", ''.join(decryptedMessage)) #join is used to put list inot string
# Checking if message is the same as the encrypt message provided
#Encrypt
encryptedMessage = []
for letter in decryptedMessage:
if letter == ' ': #spaces
encryptedMessage.append(letter)
else:
encryptedMessage.append(invkeys[letter])
print("Encrypted Message:", ''.join(encryptedMessage)) #join is used to put list inot string
The encrypt part of code is not necessary to exist, it is for checking only. It would be great if someone could help to modify my code/ give me some hints for the predict shift part. Thanks!
Output of the code:
Cypher Dict {'a': 'p', 'b': 'q', 'c': 'r', 'd': 's', 'e': 't', 'f': 'u', 'g': 'v', 'h': 'w', 'i': 'x', 'j': 'y', 'k': 'z', 'l': 'a', 'm': 'b', 'n': 'c', 'o': 'd', 'p': 'e', 'q': 'f', 'r': 'g', 's': 'h', 't': 'i', 'u': 'j', 'v': 'k', 'w': 'l', 'x': 'm', 'y': 'n', 'z': 'o', 'A': 'P', 'B': 'Q', 'C': 'R', 'D': 'S', 'E': 'T', 'F': 'U', 'G': 'V', 'H': 'W', 'I': 'X', 'J': 'Y', 'K': 'Z', 'L': 'A', 'M': 'B', 'N': 'C', 'O': 'D', 'P': 'E', 'Q': 'F', 'R': 'G', 'S': 'H', 'T': 'I', 'U': 'J', 'V': 'K', 'W': 'L', 'X': 'M', 'Y': 'N', 'Z': 'O'}
Decrypted Message: One remember to look up at the stars and not down at your feet Two never give up work Work gives you meaning and purpose and life is empty without it Three if you are lucky enough to find love remember it is there and dont throw it away Stephen Hawking
Encrypted Message: Zyp cpxpxmpc ez wzzv fa le esp delcd lyo yze ozhy le jzfc qppe Ehz ypgpc rtgp fa hzcv Hzcv rtgpd jzf xplytyr lyo afcazdp lyo wtqp td pxaej hteszfe te Escpp tq jzf lcp wfnvj pyzfrs ez qtyo wzgp cpxpxmpc te td espcp lyo ozye esczh te lhlj Depaspy Slhvtyr
CodePudding user response:
This has three components:
- Finding the character with max frequency:
test_str = "" # your string
counter = Counter(test_str)
keys = sorted(counter, key=counter.get, reverse=True)
res = keys[1] if keys[0] == " " else keys[0]
- Calculating the shift:
shift = ord('e') - ord(res)
- Encrypting/decrypting the string, which is trivial since you know the shift now
CodePudding user response:
Something like this should allow you to calculate the shift based on the assumption that the letter in the original message with the highest frequency is 'e'
:
letter_counts = Counter(message)
e_encrypted = [k for k, v in letter_counts.items() if v == max(count for c, count in letter_counts.items() if c != ' ')][0]
shift = (ord('e') - ord(e_encrypted)) % 26
Or, to unroll the comprehensions for ease of understanding:
letter_counts = Counter(message)
e_encrypted, max_v = None, 0
for k, v in letter_counts.items():
if v > max_v and k != ' ':
e_encrypted, max_v = k, v
shift = (ord('e') - ord(e_encrypted)) % 26
It does the following:
- take frequency counts of characters in
message
using theCounter
class - find the maximum frequency, and the character with that maximum frequency
- set the shift equal to the difference between the ascii value of that character and the letter
'e'
(modulo 26)