How to create an encryption/decryption function using indexes? - python

I have been trying to solve this problem for hours now and I have no idea how. I am not going to write down the problem, only the part I am struggling with.
Let's say we have:
letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
key = 'guwyrmqpsaeicbnozlfhdkjxtvGUWYRMQPSAEICBNOZLFHDKJXTV'
message = 'wncodhrlf'
As you can see, w in the message is equivalent to c, n is to o etc so the message spells out 'computer'. I need to define functions that can decode/encrypt a message using a random key, and I have been told that this is solved through indexes.
If you have any idea how to solve this please tell me. I do not need you to write a code, I simply need to be directed to a solution because I keep getting lost. Thank you for your time.

Simple translation:
>>> message.translate(str.maketrans(key, letters))
'computers'

If I understand correctly the actual letter and the encrypted letter have the same index in the letters and key variables respectively.
So to parse an encrypted string, you can just loop through the letters of the string, find each character's index in the key string, then get the letter at that index in the letters string.
For example, calling key.index('w') should return 2. Calling letters[2] will then return 'c'.
To encrypt an unencrypted string, you just do the opposite. E.g. to encrypt the 'c', call letters.index('c'), which returns 2. Then calling key[2] should return 'w'.

First off you need to access each individual letter of the message. One possible way is with indexes:
value = message[0]
From there, you need to see where in the key this letter is. String objects in Python have several helper methods.
index = key.index("a") # Finds 9 as the index
And now you have an index, finding the corresponding value in the list of letters is as simple as using an index:
decoded = letters[9] # Finds "j" in the list of letters
I'll leave it to you to piece this together.
I will note that many of these could have been found in the documentation, it's quite approachable, I recommend trying to read through it.

Explicit Python code using dictionary:
letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
key = 'guwyrmqpsaeicbnozlfhdkjxtvGUWYRMQPSAEICBNOZLFHDKJXTV'
message = 'wncodhrlf'
assert len(letters) == len(key)
key_letter_mapping = {}
for i in range(len(key)):
key_letter_mapping[key[i]] = letters[i]
result = ''
for c in message:
result += key_letter_mapping[c]
print(result)

Related

Trying to sort two combined strings alphabetically without duplicates

Challenge: Take 2 strings s1 and s2 including only letters from a to z. Return a new sorted string, the longest possible, containing distinct letters - each taken only once - coming from s1 or s2.
# Examples
a = "xyaabbbccccdefww"
b = "xxxxyyyyabklmopq"
assert longest(a, b) == "abcdefklmopqwxy"
a = "abcdefghijklmnopqrstuvwxyz"
assert longest(a, a) == "abcdefghijklmnopqrstuvwxyz"
So I am just starting to learn, but so far I have this:
def longest(a1, a2):
for letter in max(a1, a2):
return ''.join(sorted(a1+a2))
which returns all the letters but I am trying to filter out the duplicates.
This is my first time on stack overflow so please forgive anything I did wrong. I am trying to figure all this out.
I also do not know how to indent in the code section if anyone could help with that.
You have two options here. The first is the answer you want and the second is an alternative method
To filter out duplicates, you can make a blank string, and then go through the returned string. For each character, if the character is already in the string, move onto the next otherwise add it
out = ""
for i in returned_string:
if i not in out:
out += i
return out
This would be empedded inside a function
The second option you have is to use Pythons sets. For what you want to do you can consider them as lists with no dulicate elements in them. You could simplify your function to
def longest(a: str, b: str):
return "".join(set(a).union(set(b)))
This makes a set from all the characters in a, and then another one with all the characters in b. It then "joins" them together (union) and you get another set. You can them join all the characters together in this final set to get your string. Hope this helps

In Python: How can i get my code to print out all the possible words I can spell based on my input?

I think I was close to figuring out how to print out all the possible words based on user input from my set dictionary. it's based on the assumption that the user input is 'ART' so the possible words I have in my dictionary are ART, RAT, TART, and TAR but only the three letter combinations are printing out. can anyone tell me where I am going wrong? Thanks!
Dictionary = ["tar","art","tart","rat"] #creates dictionary of set words
StoredLetters = input('input your word here: ') #allows the user to input any word
list(StoredLetters)
def characters(word):
Dictionary = {}
for i in word:
Dictionary[i] = Dictionary.get(i, 0) + 1
return Dictionary
def all_words(StoredLetters, wordSet):
for word in StoredLetters:
flag = 1
words = characters(word)
for key in words:
if key not in wordSet:
flag = 0
else:
if wordSet.count(key) != words[key]:
flag = 0
if flag == 1:
print(word)
if __name__ == "__main__":
print(all_words(Dictionary, StoredLetters))
It appears there are a few things that could contribute to this.
You are swapping the parameters on all words def allwords(Dictionary, StoredLetters): when you call it in main allwords(StoredLetters, Dictionary). Without specifying the name (look up named parameters in python) you would be swapping the input.
In the characters function it would appear you are resetting the dictionary variable. Try using unique names when creating new variables. This is causing the dictionary of words you set at the top to be emptied out when characters(word) is called
First off, you are confusing things by having the name of your variable StoredLetters also being the name of one of the arguments to your all_words function.
Second, you are actually passing in StoredLetters, which is art, as the 2nd argument to the function, so it is wordSet in the function, not StoredLetters!
You should really keep things more clear by using different variable names, and making it obvious what are you using for which argument. words isn't really words, it's a dictionary with letters as keys, and how many times they appear as the values! Making code clear and understandable goes a long way to making it easy to debug. You have word, StoredLetters, wordSet, another StoredLetters argument, words = characters(word) which doesn't do what is expected. This could all use a good cleanup.
As for the functionality, with art, each letter only appears once, so for tart, which has t twice, if wordSet.count(key) != words[key] will evaluate as True, and flag will be set to 0, and the word will not be printed.
Hope that helps, and happy coding!
Based on the follow-up comments, the rule is that we must use all characters in the target word, but we can use each character as many times as we want.
I'd set up the lookup "dictionary" data structure as a Python dict which maps sorted, unique characters as tuples in each dictionary word to a list of the actual words that can be formed from those characters.
Next, I'd handle the lookups as follows:
Sort the unique characters of the user input (target word) and index into the dictionary to get the list of words it could make. Using a set means that we allow repetition and sorting the characters means we normalize for all of the possible permutations of those letters.
The above alone can give false positives, so we filter the resulting word list to remove any actual result words that are shorter than the target word. This ensures that we handle a target word like "artt" correctly and prevent it from matching "art".
Code:
from collections import defaultdict
class Dictionary:
def __init__(self, words):
self.dictionary = defaultdict(list)
for word in words:
self.dictionary[tuple(sorted(set(word)))].append(word)
def search(self, target):
candidates = self.dictionary[tuple(sorted(set(target)))]
return [x for x in candidates if len(x) >= len(target)]
if __name__ == "__main__":
dictionary = Dictionary(["tar", "art", "tart", "rat"])
tests = ["art", "artt", "ar", "arttt", "aret"]
for test in tests:
print(f"{test}\t=> {dictionary.search(test)}")
Output:
art => ['tar', 'art', 'tart', 'rat']
artt => ['tart']
ar => []
arttt => []
aret => []
The issues in the original code have been addressed nicely in the other answers. The logic doesn't seem clear since it's comparing characters to words and variable names often don't match the logic represented by the code.
It's fine to use a frequency counter, but you'll be stuck iterating over the dictionary, and you'll need to check that each count of a character in a dictionary word is greater than the corresponding count in the target word. I doubt the code I'm offering is optimal, but it should be much faster than the counter approach, I think.

enchant.errors.Error: Don't pass bytestrings to pyenchant-python

I am trying to make a program where I can enter some characters in python and the program then goes through all the possible combinations of those letters. Then it compares that to check if it is in the dictionary or is a word. If it is, it gets appended to a list that will be printed out at the end. I had to look up how to do certain things and was doing great until I got this error. I can't find any forum that has this message. Can someone help me and tell me what I need to do to get it to work? Here is my code.
import itertools
import enchant
how_many_letters=True
letters=[]
possible_words=[]
d = enchant.Dict("en_US")
print("Welcome to combination letters helper!")
print("Type the word 'stop' to quit entering letters, other wise do it one at a time.")
while how_many_letters==True:
get_letters=input("Enter the letters you have with not counting spaces:")
if get_letters=='stop':
how_many_letters=False
letters.append(get_letters)
length=len(letters)
length=length-1
del letters[length:]
print(letters)
for i in range(length):
for subset in itertools.combinations(letters, i):#Subset is the combination thing
print(subset)
check=d.check(subset)
print(check)
if check==True:
possible_words.append(check)
print(possible_words)
Thanks in advance.
The answer to you question is this:
for i in range(1, length + 1):
for subset in itertools.combinations(letters, i):#Subset is the combination thing
s = ''.join(subset)
print(s)
check=d.check(s)
print(check)
if check==True:
possible_words.append(s)
print(possible_words)
You need to pass enchant a string not a tuple and your range was not working.
(It's possible you may want to look at itertools.permutations(), I don't know if that is what you want though.)

going from word to number as in keys in a phone using a dictonary

Hello I have already done a through search before asking this question as I always do. I am trying to do use a dictionary to go from a written word to each individual letters corresponding letter on a phone key board using a dictionary in python. This is easy to do without a dictionary, but using a dictionary although faster to code is quite confusing to me. Help would be appreciated. My code so far is
def phone (word):
d = {'A''B''C':2,'D''E''F':3,'G''H''I':4,'J''K''L':5}
for i in range (len(word)):
word.split ()
return d[word]
the word I am trying to use is 'ADGJ' just as a test.
my errors that I am getting:
File "<pyshell#13>", line 1, in <module>
phone('ADGJ')
File "C:\Users\Christopher\Desktop\pratice.py", line 195, in phone
return d[word]
KeyError: 'ADGJ'
I have a key error I thought the word.split would take care of any issues but it doesn't. any suggestions?
thank you
I changed the code up a bit: I now have this:
def phone (word):
d = {'A':2, 'B':3}
word = word.split()
return d[word]
but I get a new error:
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
phone ('ABABBAA')
File "C:\Users\Christopher\Desktop\pratice.py", line 194, in phone
return d[word]
TypeError: unhashable type: 'list'
the word I am using just consits of 'ABBABABA' just because that is all I have defined in my dictionary since this is a test of understanding.
I guess I still need something to make the connection for the dictionary function thing to work but still trying to figure out what that is...
This is not valid Python
d = {'A''B''C':2,'D''E''F':3,'G''H''I':4,'J''K''L':5}
You would need to define each key, value pair
d = {
'A':2, 'B':2, 'C':2,
'D':3, 'E':3, ... etc
}
You can then convert the word into the corresponding digits
def getNums(word):
return ''.join(str(d[i]) for i in word)
>>> getNums('ADGJ')
'2345'
Your syntax is a little off; you can't let assignment in a dictionary fall through like in a switch statement.
Try this:
d = {'A':2,'B':2,'C':2,'D':3,'E':3,'F':3,'G':4,'H':4,'I':4,'J':5,'K':5,'L':5}
OK, so let me try to help you.
This is your current code:
def phone (word):
d = {'A':2, 'B':3}
word = word.split()
return d[word]
The first line defines your function signature, that means you've got a function called phone which takes a parameter called word. My first comment is: chose appropriate names for functions and variables. phone is not a "function", since a function is kind of an instruction or a command like thing, but never mind (letters_to_phonenumber would be better I think).
The second line defines a dictionary, which maps 'A' to 2, 'B' to '3'. That's OK for now.
The third line overwrites your word variable with the return value of the split() function, which is a method of the string class. Let's look up the documentation for this: https://docs.python.org/2/library/stdtypes.html#str.split
str.split([sep[, maxsplit]]):
Return a list of the words in the string, using sep as the delimiter string.
Since you obviously did not define a sep(arator), we have to figure out what the function will do. Reading further says:
If sep is not specified or is None, a different splitting algorithm is
applied: runs of consecutive whitespace are regarded as a single
separator, and the result will contain no empty strings at the start
or end if the string has leading or trailing whitespace. Consequently,
splitting an empty string or a string consisting of just whitespace
with a None separator returns [].
So it will look for whitespace within your string. You don't know what a whitespace is? Let's google: http://en.wikipedia.org/wiki/Whitespace_character
In computer science, whitespace is any character or series of
whitespace characters that represent horizontal or vertical space in
typography. When rendered, a whitespace character does not correspond
to a visible mark, but typically does occupy an area on a page.
OK, now we know, that whitespace is like space or tab etc. A string like "ABABBAA" does not contain any whitespace, so split() will obviously return only a list with exactly one item in it: the input string itself.
Let's fire up the python interpreter to check this (this is a common way of debugging):
>>> 'ABABBAA'.split()
['ABABBAA']
The next line in your code is return d[word]. So the function terminates here and returns an output value, namely d[word]. But what is the value of d[word]? Well, d is a dictionary (with the keys 'A' and 'B') and you try to find the value of the key ['ABABBAA']. But there is no such key in your dictionary d, let alone there is no way to create a key for a dictionary, since a key has to be a hashable object. What is a hashable object? Let's google: https://docs.python.org/2/glossary.html
hashable: An object is hashable if it has a hash value which never
changes during its lifetime (it needs a hash() method), and can be
compared to other objects (it needs an eq() or cmp() method).
Hashable objects which compare equal must have the same hash value.
Hashability makes an object usable as a dictionary key and a set
member, because these data structures use the hash value internally.
All of Python’s immutable built-in objects are hashable, while no
mutable containers (such as lists or dictionaries) are. Objects which
are instances of user-defined classes are hashable by default; they
all compare unequal (except with themselves), and their hash value is
their id().
OK, so 'A' would be hashable ;-) and any kind of number of string etc. but not a list, in this sense.
So what now? You have to find a way to somehow separate the letters in your input word. This can be easily done with slicing, or simply iterating over the string (in Python, strings are iterable):
for letter in word:
# this loop will iterate over word and assign each of its letters to
# the variable `letter`, which you can use in this scope
But how do we actually return the phone number? This will not work:
def phone (word):
d = {'A':2, 'B':3}
for letter in word:
return d[letter]
Why? Because it will stop at the first letter and terminate the function (remember the return statement?).
The way to go is to collect all the numbers and when we're done, simply put all together and return them. This is a common way to handle such problems. We first initialise a list, which we can manipulate in each for-iteration:
def phone (word):
d = {'A':2, 'B':3}
digits = []
for letter in word:
digits.append(d[letter])
return digits
Great! Looks better now:
>>> phone('ABA')
[2, 3, 2]
Now try to figure out how to return a real number instead of a list.
This is btw. kind of a basic workflow of a programmer. A lot of research and look-up in (API) documentation, solving puzzles and looking at few lines of code hours long. If you don't love it, you'll never become a programmer.

Missing letter from encryption

My encryption is almost working correctly but not quite:
d1={'H':'Z','E':'P','l':'O','O':'J'}
def encrypt(table,string):
encrypted=""
for k,v in table.iteritems():
k=string[0:]
encrypted+=v
return encrypted
print encrypt(d1,"HELLO")
This returns "ZPOJ." It needs to return "ZPOOJ." How can I make this work?
Thanks.
The following code works. You should loop on string rather than table. Make sure that all character using in the string are defined as keys in table. l is replaced with L in d1.
d1={'H':'Z','E':'P','L':'O','O':'J'}
def encrypt(table,string):
encrypted=""
for c in string:
encrypted+=table[c]
return encrypted
print encrypt(d1,"HELLO")
It looks a bit like homework so I'll try to only give hints…
Problems in your current code :
k=string[0:]
This gets the whole string every loop, where you probably only want one character from it.
In any cases, you are not using k inside the loop.
encrypted+=v
This is the only place where you add to the result, but you are taking the value directly from the dictionary values. You end up with a string containing all values of the dictionary, disregarding the plaintext you are encrypting.
You want to loop over the plaintext string and for each character, look up the corresponding value in the dictionary. Something like this:
for k in string:
encrypted = encrypted + table[k]
Note that you have a lowercase L in your dictionary.

Categories

Resources