Reputation: 11
I am trying to make a program to break a Hill Cipher with a known Key Matrix, and when I enter the ciphertext, and convert the letters into numbers, I use this for loop:
letters = 'abcdefghijklmnopqrstuvwxyz'
ciphertext = input('Enter your ciphertext')
ciphertext = list(ciphertext.lower())
for symbol in ciphertext:
num = int(letters.find(symbol))
print(num)
and I am wondering if there is a way to transfer the numbers that are printed into an array, which I am using to represent the matrices that are involved in Hill ciphers, as I need to insert every block of 2 letters into a 2x1 matrix, in order to perform a matrix multiplication on each, in order to get the plaintext.
Basically, I am trying to get the numbers from the for loop into arrays, in the most efficient way possible, but I am very new to coding in python, so I have no idea how I would do it.
Also, is there just an easier way altogether to use matrices in python that I don't know about that would make this whole thing easier?
Upvotes: 1
Views: 92
Reputation: 402593
It looks like you want to return the character's ASCII values in a list. You can do this in linear time with a list comprehension.
symbols = 'axy12c'
lst = [ord(c) - ord('a') if c.isalpha() and c.islower() else -1 for c in symbols]
print(lst)
[0, 23, 24, -1, -1, 2]
Since you mention arrays, there's also a numpy way of doing this, lo and behold.
arr = np.array(list(symbols), dtype='|S1').view(np.int8) - 97
arr[(0 > arr) | (25 < arr)] = -1
print(arr)
array([ 0, 23, 24, -1, -1, 2], dtype=int8)
Upvotes: 2
Reputation: 15905
Yes, you should use a list comprehension for this:
letters = 'abcdefghijklmnopqrstuvwxyz'
ciphertext = input().lower()
indices = [letters.find(c) for c in ciphertext]
This will append letters.find(c)
to indices for each letter (called c
here) in ciphertext
.
But if letters
is always the alphabet (and it seems like according to the wikipedia it is), you can make this faster by using ord
which gives you the ASCII index of each letter. To make it so 'a'
is 0
you can just subtract ord('a')
:
ciphertext = input().lower()
indices = [ord(c) - ord('a') for c in ciphertext]
Looking at the Wikipedia for a Hill Cipher, it looks like what you've described isn't exactly what you need.
My understanding is your key must be a perfect square in length (n^2). You then shape the key into a n
by n
matrix and multiply it by n
chunks of the plaintext. You should look into numpy. It has tons of tools for working with matrices and I'm pretty sure you can express this cipher in a few lines:
import numpy as np
ciphertext = np.array(map(ord, input().lowercase())) - ord('a')
n = int(sqrt(len(ciphertext)))
assert n**2 == len(ciphertext), 'ciphertext must be perfect square in length'
ciphertext.reshape((n, n)) # Make ciphertext a nxn matrix
# Matrix multiply plaintext chunks of n by ciphertext
plaintext.reshape((n, -1)) @ ciphertext
It also seems like ciphertext must be invertible. numpy allows you to check that the ciphertext is invertible (and compute its inverse) without having to code out the linalg stuff yourself.
Upvotes: 1
Reputation: 71
Here's a way to get them into a list.
letters = 'abcdefghijklmnopqrstuvwxyz'
ciphertext = input('Enter your ciphertext')
ciphertext = list(ciphertext.lower())
desiredArray = list(map(lambda x: int(letters.find(x)), ciphertext))
This defines your lookup and conversion to an integer as a "lambda" function; the "map" function maps the lambda function to the elements of your ciphertext, and the "list" function casts the result as a list. These are all bits of python worth learning about.
Upvotes: 0