Using recursion to find words in a word search - python

I'm having a little fun with python3 by trying to find words in a word search. I know I could easily do this with loops however, I don't know recursion too well and I really want to know how to do it this way.
I began by creating a 2-D list of the rows in the word search and calling that list "square". I created another list of the individual words I am looking for called "word" (for the sake of simplicity, let's pretend there is only one word).
I am going to use recursive functions for each direction a word can go and run the word in each function, returning True if it is found, and False if it is not.
This is the first function:
def down(word, square):
if (len(word)==0):
return True
elif (len(square)==0):
print(square)
return False
else:
if word[:1]==square[0][:1]:
return down(word[1:], square[1:])
elif (word[:1]!=square[0][:1]):
print(square)
return down(word, square[1:][1:])
else:
return False
This function will try to find the first letter of the word in the 2-D list and then check that same position where the first letter is found in each subsequent line of the square to see if the rest of the word is found.
I cannot get the function to go past the first letter of each 1-D list within the overall 2-D list and any assist would be greatly appreciated.
Thanks!

Related

How to write a function to find positions of substring within a larger string without using the 'find' function?

I have just recently started using python and I am extremely new to python and coding in general and I'm in a class where we have been given the assignment to find a function in Python that takes two strings of DNA sequence (say string1 and string2) as input and returns a list of the position(s) where string2 is present as a substring of string1. And since we're supposed to be becoming familiar with how coding works, we can't use the "find" built-in function. I'm really confused on how to even start this problem. But this is what I have so far:
def linear(seq, sub):
positions = [0]
for i in range(len(sub)):
if seq[i:i+len(sub)] == sub:
positions[0]+=1
return( positions )
I get an error when I put this in but I should get out a list of the positions where the substring occurs. If someone can guide me in which direction I should be going, that would be really helpful.
As an example if the sequence is 'ATTCCATGGACCTAGTCAT' and the substring I want to find is 'CAT', then the output should be [5,17]
Unfortunately, it can't be [5,17] as indexing in python starts at 0, it should be [4,16]. You can use a for loop to go through the indexes till the last index minus the length of the substring plus 1 in order not to get out of range. Then you check if the slice of the string, which is the current index till the current index plus the length of the substring (if it is 'CAT' then you get every slice of the length of 3 of the string), is equal to the substring. If so then append the index to the index list.
def find(st,sub):
list_of_pos=[]
for i in range(len(st)-len(sub)+1):
if st[i:i+len(sub)]==sub:
list_of_pos.append(i)
return list_of_pos
You can make it more compact if you use list comprehension:
def find(st,sub):
return [i for i in range(len(st)-len(sub)+1) if st[i:i+len(sub)]==sub]

Countdown letters solver - Recursive Python

def solver(word):
#trackerCount = len(word
convertedKey = sum(bytearray(word,'utf8'))
if(len(word)>=MIN_WORD_LENGTH):
countdownLetters = wordmap.get(convertedKey)
if(convertedKey in wordmap):
for str in countdownLetters:
if sorted(word)==sorted(str) and str not in result:
result.update({str:len(str)})
if(len(word)==9 and len(result)>0):
return result
tempList = list(word)
for i in range(len(tempList)):
charTmp = tempList.pop()
wordStr =''.join(tempList)
tempList.insert(0,charTmp)
solver(wordStr)
return result
I am writing a countdown letters solver using a recursive function. I want to completely STOP calling recursive
function when i find the longest letters. For example, say I passed a word "education" to a solver function.
Let us assume, we don't have any anagrams of education, now I would like to check if there are any words of length (8).
If there are words of length 8, I want to return out of the function but if there are no 8 letters words I want to
check of 7 and so on. Im only interested to find the longest words. Off course If I have more than one word of same (length)
as the longest word, I want to get them all. The above loop finds all the words from max length(9) to min length(5).
Explanation of the above code:
Basically, If I have no 9 letters(max) words then I am popping the last element, created a tempWord (wordStr) inserted charTemp(last element of the list), and called solver function with a letter removed. The above loop finds all the words from max length(9) to min length(4).
The output of above code is here. http://postimg.org/image/pgfixbglv/ . please have a look, it might make more sense. In the image, you can see 9 letters words. I want my recursive function to return at that point, but if there are no 9 letters, I wanna look for 8, and again no 8 letters words found, move on to 7 and so on.At the moment to prevent StackOverflow exception I have specified the min word length. i.e 5.
The problem here is in your termination clause;
if(len(word)==9 and len(result)>0):
return result
This very explicitly says that you stop only when you've found a result from a 9-letter word. Unless len(word) is 9, you drop to the recursion code.
As the old gag goes, if that's not what you want, then don't do that. Just check that you have some result; if so, return it. I can't tell if this, alone, is sufficient, since you haven't shown how you manage collecting all the results for the various runs through the lower loop (since you need them all), nor how you drop each of the 9 letters in turn, leaving the other 8.

Python recursive function seems to lose some variable values

I have 4x4 table of letters and I want to find all possible paths there. They are candidates for being words. I have problems with the variable "used" It is a list that includes all the places where the path has been already so it doesn't go there again. There should be one used-list for every path. But it doesn't work correctly. For example I had a test print that printed the current word and the used-list. Sometimes the word had only one letter, but path had gone through all 16 cells/indices.
The for-loop of size 8 is there for all possible directions. And main-function executes the chase-function 16 times - once for every possible starting point. Move function returns the indice after moving to a specific direction. And is_allowed tests for whether it is allowed to move to a certain division.
sample input: oakaoastsniuttot. (4x4 table, where first 4 letters are first row etc.)
sample output: all the real words that can be found in dictionary of some word
In my case it might output one or two words but not nearly all, because it thinks some cells are used eventhough they are not.
def chase(current_place, used, word):
used.append(current_place) #used === list of indices that have been used
word += letter_list[current_place]
if len(word)>=11:
return 0
for i in range(3,9):
if len(word) == i and word in right_list[i-3]: #right_list === list of all words
print word
break
for i in range(8):
if is_allowed(current_place, i) and (move(current_place, i) not in used):
chase(move(current_place, i), used, word)
The problem is that there's only one used list that gets passed around. You have two options for fixing this in chase():
Make a copy of used and work with that copy.
Before you return from the function, undo the append() that was done at the start.

modify program of generation for saving words in list and choosing them with random.choice()

How to modify program of generation for saving words in list and choosing them with random.choice() previously do import random?
where is mistake? its not working correct
def generate_model(cfdist,word,num=15):
for i in range(num):
word=random.choice.im_class(cfdist
[word].keys())
>>> generate_model(cfd,'living')
There is all kinds of weird stuff going on with that code:
def generate_model(cfdist,word,num=15):
You use word as the key to look up in the dictionary.
word=
Then you change it? Are you intentionally chaining the result of one random lookup as the key for the next lookup?
random.choice
If you're intentionally chaining, this is right, but if you want a bunch of words from the same dict you want random.sample.
.im_class(
This is completely unnecessary. Just call it like random.choice(. Look at the examples in the random docs
cfdist[word]
You're getting the value in cfdist with the key equal to the value of word passed in (in this case, living) the first time, then the key is equal to the result of the choice after that. Is that what you intended?
.keys())
This will work, if each value in cfdist is a dict.
Now, you say you want
for saving words in list
But since I'm not sure what exactly you want, I'll give two examples. The first, I'll just aggregate the words, but not change anything else:
def generate_model(cfdist,word,num=15):
words = []
# you can add the starting word with words.append(word) here if you want
for i in range(num):
word=random.choice(cfdist[word].keys())
words.append(word)
return words
The second, I'll assume you just want 15 random words from the dict with no repetitions:
def generate_model(cfdist,word,num=15):
return random.sample(cfdist[word].keys(), num)
Then either way call it as
>>> words = generate_model(cfd,'living')
to get the list of words.

multilevel caesar cipher

Hey, I'm trying to decode a multilevel Caesar cipher. By that I mean a string of letters could have been shifted several times, so if I say apply_shifts[(2,3),(4,5)], that means I shift everything from the 2nd letter by 3 followed by everything from the 4th letter by 5. Here's my code so far.
def find_best_shifts_rec(wordlist, text, start):
"""
Given a scrambled string and a starting position from which
to decode, returns a shift key that will decode the text to
words in wordlist, or None if there is no such key.
Hint: You will find this function much easier to implement
if you use recursion.
wordlist: list of words
text: scambled text to try to find the words for
start: where to start looking at shifts
returns: list of tuples. each tuple is (position in text, amount of shift)
"""
for shift in range(27):
text=apply_shifts(text, [(start,-shift)])
#first word is text.split()[0]
#test if first word is valid. if not, go to next shift
if is_word(wordlist,text.split()[0])==False:
continue
#enter the while loop if word is valid, otherwise never enter and go to the next shift
i=0
next_index=0
shifts={}
while is_word(wordlist,text.split()[i])==True:
next_index+= len(text.split()[i])
i=i+1
#once a word isn't valid, then try again, starting from the new index.
if is_word(wordlist,text.split()[i])==False:
shifts[next_index]=i
find_best_shifts_rec(wordlist, text, next_index)
return shifts
My problems are
1) my code isn't running properly and I don't understand why it is messing up (it's not entering my while loop)
and
2) I don't know how to test whether none of my "final shifts" (e.g. the last part of my string) are valid words and I also don't know how to go from there to the very beginning of my loop again.
Help would be much appreciated.
I think the problem is that you always work on the whole text, but apply the (new) shifting at some start inside of the text. So your check is_word(wordlist,text.split()[0]) will always check the first word, which is - of course - a word after your first shift.
What you need to do instead is to get the first word after your new starting point, so check the actually unhandled parts of the text.
edit
Another problem I noticed is the way you are trying out to find the correct shift:
for shift in range(27):
text=apply_shifts(text, [(start,-shift)])
So you basically want to try all shifts from 0 to 26 until the first word is accepted. It is okay to do it like that, but note that after the first tried shifting, the text has changed. As such you are not shifting it by 1, 2, 3, ... but by 1, 3, 6, 10, ... which is of course not what you want, and you will of course miss some shifts while doing some identical ones multiple times.
So you need to temporarily shift your text and check the status of that temporary text, before you continue to work with the text. Or alternatively, you always shift by 1 instead.
edit²
And another problem I noticed is with the way you are trying to use recursion to get your final result. Usually recursion (with a result) works the way that you keep calling the function itself and pass the return values along, or collect the results. In your case, as you want to have multiple values, and not just a single value from somewhere inside, you need to collect each of the shifting results.
But right now, you are throwing away the return values of the recursive calls and just return the last value. So store all the values and make sure you don't lose them.
Pseudo-code for recursive function:
coded_text = text from start-index to end of string
if length of coded_text is 0, return "valid solution (no shifts)"
for shift in possible_shifts:
decoded_text = apply shift of (-shift) to coded_text
first_word = split decoded_text and take first piece
if first_word is a valid word:
rest_of_solution = recurse on (text preceding start-index)+decoded_text, starting at start+(length of first_word)+1
if rest_of_solution is a valid solution
if shift is 0
return rest_of_solution
else
return (start, -shift mod alphabet_size) + rest_of_solution
# no valid solution found
return "not a valid solution"
Note that this is guaranteed to give an answer composed of valid words - not necessarily the original string. One specific example: 'a add hat' can be decoded in place of 'a look at'.

Categories