Python Vigenère cipher output doesn't match correct answer

77 Views Asked by At

I wrote some code for decoding Vigenère cipher. But it always outputs a wrong decoded message.

And here's my code:

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"]

v_message= "txm srom vkda gl lzlgzr qpdb? fepb ejac! ubr imn tapludwy mhfbz cza ruxzal wg zztylktoikqq!"

keyword = "friends"

def vigenere_cipher(message, keyword):
  decoded = []
  for i in range(len(message)):
    char = message[i]
    if char.isalpha():
      char_index = alphabet.index(char)
      key_index = alphabet.index(keyword[i % len(keyword)])
      letter_index = (char_index - key_index) % len(alphabet)
      decoded.append(alphabet[letter_index])
    else:
      decoded.append(char)
  return "".join(decoded)

print(vigenere_cipher(v_message, keyword))

The output it gave me is:

oge fowh ngqx bu hmioua mcaj? xacy zssy! cwa ezk ojhhhaet edsyh lrw ocsish to irplisoxagdn!

And I don't know what's wrong with it. Can anyone help and fix me my code please?

I have asked chatGPT as well but it cannot find the actual error.

2

There are 2 best solutions below

3
e-motta On
  1. You're shifting letters backwards in the alphabet, when you should shift them forwards. This is the opposite of the Vigenere cipher, as pointed out in the comments. So replace letter_index = (char_index - key_index) % len(alphabet) with letter_index = (char_index + key_index) % len(alphabet)

  2. i should not be incremented for spaces.

def vigenere_cipher(message, keyword):
    decoded = []
    i = 0
    for char in message:
        if char.isalpha():
            char_index = alphabet.index(char)
            key_index = alphabet.index(keyword[i % len(keyword)])
            letter_index = (char_index + key_index) % len(alphabet)
            decoded.append(alphabet[letter_index])
            i += 1
        else:
            decoded.append(char)
    return "".join(decoded)

This will print:

you were able to decode this? nice work! you are becoming quite the expert at cryptography!

0
SIGHUP On

The only difference between Vigenère encoding and decoding is one piece of arithmetic. Thus both activities can be carried out in a single function that gets passed a Callable that will handle this variation.

from string import ascii_lowercase
from itertools import cycle
from operator import add, sub
from collections.abc import Callable

AMAP = {c: o for o, c in enumerate(ascii_lowercase)}


def crypto(msg: str, key: str, func: Callable) -> str:
    assert msg.lower() == msg and key.lower() == key
    rv = ""
    k = cycle(key)
    for c in msg:
        if c in AMAP:
            a = AMAP[c]
            b = AMAP[next(k)]
            p = func(a, b)
            rv += ascii_lowercase[p % 26]
        else:
            rv += c
    return rv


def encrypt(msg: str, key: str) -> str:
    return crypto(msg, key, add)


def decrypt(msg: str, key: str) -> str:
    return crypto(msg, key, sub)


KEY = "friends"
msg = "you were able to decode this? nice work! you are becoming quite the expert at cryptography!"

enc = encrypt(msg, KEY)
print(enc)
print(decrypt(enc, KEY))

Output:

dfc aruw fsti gr vjtwhr wznj? vmph otis! cbx swv jipreneo uhllj kpi rahjib eg fjdgbstusuyg!
you were able to decode this? nice work! you are becoming quite the expert at cryptography!

Note:

This code only works for lowercase ASCII