Why is there a %26 (Python)? - python

I am trying to decipher a text from user without knowing the key in caesar cipher.
I probably understand most of it but I don't get it why they use the mod 26 on line 8
alphabet=['a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z']
print('write the message you want to decode?')
inputString = input()
inputString = inputString.lower()
transAlphabet = {}
def createDict(shift):
for i in range(0,26):
letter = alphabet[i]
transAlphabet[letter]=alphabet[(i+shift)%26]
def encodeMessage(message):
cypherText = ''
for letter in message:
if letter in transAlphabet:
cypherText = cypherText + transAlphabet[letter]
else:
cypherText = cypherText + letter
print(cypherText)
for i in range(0,26):
createDict(i)
encodeMessage(inputString)
Please would be really helpful if someone helps me out thank you!

The modulo %26 is to make the alphabet circular, when you're at z come back at a
Example for alph[(i + shift) % 26] with i=20 and shift=10
without modulo you'll like to reach index 30 in the array and it doesn't not exist
with modulo you'll reach index 30%26 = 4 in the array, letter e
But also your algo is a bt strange, I would have done the shifting on the fly for each car, but your solution may be more efficient for long text as you don't need to compute the shift everytime.
Not computing the shifted alphabet before would look like this
from string import ascii_lowercase
alphabet = ascii_lowercase
def encode_message(message, shift):
cypher_text = ''
for letter in message:
letter_idx = alphabet.index(letter)
cypher_text = cypher_text + alphabet[(letter_idx + shift) % 26]
return cypher_text
def decode_message(message, shift):
cypher_text = ''
for letter in message:
letter_idx = alphabet.index(letter)
cypher_text = cypher_text + alphabet[(26 + letter_idx - shift) % 26]
return cypher_text

Related

Why is ROT 13 displaying g in spaces?

I am writing code that is based on ROT13 Algorithm but when I have the message as "ROT ALGORITHM" it displays as "EBGgNYTBEVGUZ". I am unsure whether the 'g' is wrong since its meant to be a space between ROT and ALGORITHM?
def rot13(message,shift):
result = ""
for i in range(len(message)):
char = message[i]
if (char.isupper()):
result += chr((ord(char) + shift-13) % 26 + 65)
else:
result += chr((ord(char) + shift-13) % 26 + 97)
return result
shift = 13
message = "ROT ALGORITHM"
print("Shift:", shift)
print(message)
print(rot13(message,shift))
From ROT13 spec, only letters should be affected by the algorithm, here as space is not upper() you go in the else section
You may handle the 2 min usecases : lowercase and uppercase, and just use the alphabet to rotate
from string import ascii_lowercase, ascii_uppercase
def rot13(message, shift):
result = ""
for char in message:
if char in ascii_uppercase:
result += ascii_uppercase[(ascii_uppercase.index(char) + shift) % 26]
elif char in ascii_lowercase:
result += ascii_lowercase[(ascii_lowercase.index(char) + shift) % 26]
else:
result += char
return result

Text not printing at the end of my for loop for a polyalphabetic cypher

I am making a polyalphabetic cypher. My code is running, but it is not printing the "cyphertext" at the end. I even tried testing individual parts of the for loop and none of it will print either.
import string
alpha = string.ascii_lowercase
message = input('Message:')
message = message.upper()
secretWord = input('Secret word:')
secretWord = secretWord.upper()
cypherText = ''
count = 0
for letter in message:
if letter in alpha:
shift = alpha.index(secretWord[count])
letterIndex = alpha.index(letter)
cypherLetter = alpha[(letterIndex+shift)%26]
cypherText = cypherText + cypherLetter
count = count+1
print(cypherText)
Your message is in upper case but alpha is in lower so letter from your iteration over message will never be in alpha
You are also increasing count outside your loop which result in a constant shift
You are making every character uppercase and then you are checking to see if it is a lower case character. As the upper case character isn't a lower case character it doesn't get encrypted.
Use upper or lower case everywhere in your code:
import string
alpha = string.ascii_lowercase
message = input('Message: ').lower()
secret_word = input('Secret word: ').lower()
cypher_text = ''
for i, letter in enumerate(message):
if letter in alpha:
shift = alpha.index(secret_word[i]) if len(secret_word) > i else alpha.index(secret_word[0])
letter_index = alpha.index(letter)
cypher_letter = alpha[(letter_index + shift) % 26]
cypher_text = cypher_text + cypher_letter
print(cypher_text)
Output:
Message: animal
Secret word: snake
saiwed

How do I increase the position of a letter in a list by the position of letter?

I have a program that takes the position of a letter and increases the position by the value of the shift and then gives me the new position of the letters, it's a caesar list function. However i need to be able to increase the position of the letter by the value of shift and also by the position of the letter, so if i have "hello" and shift is 15, h = 7 so 7 +15 +0 = 22 and e would be 4+15+1(position of e) = 20
However, I'm not sure how to edit my code so that I can increase the position of each letter by the value of their position. The code works fine, I just need help figuring out this step.
alphabet =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
def caesar(plaintext,shift):
# initialize ciphertext as blank string
ciphertext = ""
# loop through the length of the plaintext
for i in range(len(plaintext)):
# get the ith letter from the plaintext
letter = plaintext[i]
# find the number position of the ith letter
num_in_alphabet = alphabet.index(letter)
print (num_in_alphabet)
# find the number position of the cipher by adding the shift
cipher_num = (num_in_alphabet + shift + 3) % len(alphabet)
# find the cipher letter for the cipher number you computed
cipher_letter = alphabet[cipher_num]
# add the cipher letter to the ciphertext
ciphertext = ciphertext + cipher_letter
# return the computed ciphertext
return ciphertext
def main():
plaintext = ("hello")
shift = 16
text = caesar(plaintext,shift)
print (text)
main()
cipher_num = (num_in_alphabet + shift + i) % len(alphabet)
Or much simpler
shift = 16
chiper = ''.join([chr(((ord(c)-ord('a')+shift+i)%26)+ord('a')) for i, c in enumerate("hellow")])
assert chiper == 'xvdeir'

Implementing the Ceaser Cipher function through input in Python

Im trying to create a Ceaser Cipher function in Python that shifts letters based off the input you put in.
plainText = input("Secret message: ")
shift = int(input("Shift: "))
def caesar(plainText, shift):
cipherText = ""
for ch in plainText:
if ch.isalpha():
stayInAlphabet = ord(ch) + shift
if stayInAlphabet > ord('z'):
stayInAlphabet -= 26
finalLetter = chr(stayInAlphabet)
cipherText += finalLetter
print(cipherText)
return cipherText
caesar(plainText, shift)
For example, if I put "THE IDES OF MARCH" as my message and put 1 as my shift, it outputs "UIFJEFTPGNBSDI" when it is meant to output "UIF JEFT PG NBSDI." It doesn't keep the spaces and also shifts things like exclamation marks back also when it should leave them as is. Letters should also wrap meaning if I put shift as 3, an X should go back to A.
To fix the spacing issue, you can add an else to if ch.isalpha() and just append the plain text character to the cipher text. This will also handle punctuation and other special, non-alpha characters.
To handle wrapping (e.g. X to A), you'll want to use the modulo operator %. Because A is the 65th ASCII character and not the 0th, you'll need to zero-base the alpha characters, then apply the mod, then add back the offset of 'A'. To shift with wrap-around, you can do something like: final_letter = chr((ord(ch) + shift - ord('A')) % 26 + ord('A')). Note the 26 comes from number of letters in the Latin alphabet.
With these in mind, here is a full example:
plain_text = input("Secret message: ")
shift = int(input("Shift: "))
def caesar(plain_text, shift):
cipher_text = ""
for ch in plain_text:
if ch.isalpha():
final_letter = chr((ord(ch) + shift - ord('A')) % 26 + ord('A'))
cipher_text += final_letter
else:
cipher_text += ch
print(cipher_text)
return cipher_text
caesar(plain_text, shift)
Sample input:
plain_text = "THE IDES OF MARCH"
shift = 1
cipher_text = caesar(plain_text, shift)
print(cipher_text)
# UIF JEFT PG NBSDI
The reason the cipher does not produce the expected result is your code does not account for the case where it is not a alpha non numerical letter. So, a potential fix is just adding handling for spaces.
Code
plainText = input("Secret message: ")
shift = int(input("Shift: "))
def caesar(plainText, shift):
cipherText = ""
for ch in plainText:
if ch.isalpha():
stayInAlphabet = ord(ch) + shift
if stayInAlphabet > ord('z'):
stayInAlphabet -= 26
finalLetter = chr(stayInAlphabet)
cipherText += finalLetter
elif ch is " ":
cipherText += " "
print(cipherText)
return cipherText
caesar(plainText, shift)
Example
Secret message: THE IDES OF MARCH
Shift: 1
UIF JEFT PG NBSDI

Python Vigenere Cipher with required functions

I am working on an online course that has us creating a caesar cipher and vigenere cipher, but we first created two functions; one to find the position of a letter in an alphabet variable, and one to rotate one given character a given amount of times (I have seen that ord() and chr() work better, but the assignment wants us to focus on simpler concepts for now, I guess).
I was able to get the caesar function working, but am unsure as how to go forward with the vigenere cipher. I have watched many videos and looked around this site, but have not found any that allow for the preservation of spaces and non alphabetical characters. Can anyone point me in the right direction of how to start the vigenere function?
#Create function alphabet_position(letter) to turn letter into number
#such as a=0 or e=4, using lowercase to make sure case doesnt matter.
def alphabet_position(letter):
alphabet ="abcdefghijklmnopqrstuvwxyz" #Lists alphabet for a key
lower_letter = letter.lower() #Makes any input lowercase.
return alphabet.index(lower_letter) #Returns the position of input as a number.
def rotate_character(char, rot):
alphabet = "abcdefghijklmnopqrstuvwxyz"
if char.isalpha():
a = alphabet_position(char)
a = (a + rot) % 26 #needs modulo
a = (alphabet[a])
if char.isupper():
a = a.title()
return a
else:
return char
def encrypt(text, rot):
list1 = ""
for char in text:
list1 += rotate_character(char, rot)
return list1
def main():
x = input("Type a message: ")
y = input("Rotate by: ")
#result = rotate_character(x, y) #Not needed once encrypt function works.
result = encrypt(x, y)
print (result)
if __name__ == '__main__':
main()
following my comments; using all printables as alphabet:
from string import ascii_letters, digits, punctuation, whitespace
ALPHABET = ascii_letters + digits
STATIC_ALPHABET = punctuation + whitespace
# minor speedup
ALPHA_INDEX = {a: i for i, a in enumerate(ALPHABET)}
STATIC_ALPHABET_SET = set(STATIC_ALPHABET)
MOD = len(ALPHABET)
def encrypt(char, key):
if char in STATIC_ALPHABET_SET:
return char
else:
return ALPHABET[(ALPHA_INDEX[char] + key) % MOD]
def decrypt(char, key):
if char in STATIC_ALPHABET_SET:
return char
else:
return ALPHABET[(ALPHA_INDEX[char] + MOD - key) % MOD]
key = 17
plain = 'Hello World!'
enc = ''.join(encrypt(char, key) for char in plain)
print(enc) # YvCCF dFICu!
dec = ''.join(decrypt(char, key) for char in enc)
print(dec) # Hello World!

Categories