Carol Liu
Carol Liu

Reputation: 335

using remainder to help find index?

Can anyone explain why "%26" is needed in the following code? The result seems the same without "%26". Code source enter link description here

def caesar_encrypt(realText, step):
    outText = []
    cryptText = []

    uppercase = ['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']
    lowercase = ['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']

    for eachLetter in realText:
        if eachLetter in uppercase:
            index = uppercase.index(eachLetter)
            crypting = (index + step) % 26
            cryptText.append(crypting)
            newLetter = uppercase[crypting]
            outText.append(newLetter)
        elif eachLetter in lowercase:
            index = lowercase.index(eachLetter)
            crypting = (index + step) % 26
            cryptText.append(crypting)
            newLetter = lowercase[crypting]
            outText.append(newLetter)
    return outText

code = caesar_encrypt('abc', 2)
print(code)

Upvotes: 0

Views: 68

Answers (2)

Patrick Artner
Patrick Artner

Reputation: 51683

You need the %26 to avoid overshooting your list of mappings if the position of the original characters +step would go out of bounds. This starts on position 0 again if you land after the last character.


The code you posted uses lots of searches over text, which takes quite some time.

It would be better to just "look up" what pos a character has, add step to that and then look up what character is assigned to this sum. You do not need a full upper- and lowercase mapping: you can test the input character and use .upper() to create an upper case character from the lower case one.

# lowercase and uppercase ascii can be taken from constants out of string module
import string

# create a dict that comntains character -> info, only for lowercase
cryptDict = {ch:pos for pos,ch in  enumerate(string.ascii_lowercase)} 

# add the inverse mapping (pos ->character)    
for k,v in cryptDict.items():
    cryptDict[v] = k

def caesar_encrypt(realText, step):
    outText = []
    cryptText = []

    for letter in realText:
        # get what pos this letter is at
        pos = cryptDict.get(letter,None) # gets None if character not in, f.e. 8

        if pos is not None:
            # you need % 26 here, in case your pos + step goes bigger then 26
            # f.e. z = 25,step = 2 => 27 , you do not have any character thats
            # mapped to 27,so you % 26 and use b which is mapped to 1
            crypt = cryptDict[(pos + step)%26]

            # fix casing if input was uppercase
            if letter.isupper():
                crypt = crypt.upper()

            outText.append(crypt)
        else:
            outText.append(letter) # do not forget unmapped values

    return outText

code = caesar_encrypt('abc', 2)
print(code) # ['c','d','e']

The cryptDict looks like so:

{0: 'a',  1: 'b',  2: 'c',  3: 'd',  4: 'e',  5: 'f',  6: 'g',  7: 'h',  8: 'i', 
 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 
18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 

'a':  0, 'c':  2, 'b':  1, 'e':  4, 'd':  3, 'g':  6, 'f':  5, 'i':  8, 'h':  7, 
'k': 10, 'j':  9, 'm': 12, 'l': 11, 'o': 14, 'n': 13, 'q': 16, 'p': 15, 's': 18, 
'r': 17, 'u': 20, 't': 19, 'w': 22, 'v': 21, 'y': 24, 'x': 23, 'z': 25}

Reference: - string.ascii_lowercase

Upvotes: 1

Dylan
Dylan

Reputation: 1405

Its because, its encrypting the information with a ROT style encryption technique. Its using that remainder, to determine which letter to replace the real letter with.

Upvotes: 1

Related Questions