I'm starting to learn basic programming with python, and as I encountered permutations, I wanted to see if I could (for fun) brute force a userinput..
It seems to work for short PW's, but my counter seems to have a flaw, or do I miss something about permutations nature? ...the counter shows the same number each time, for any PW of the same length...
Also, there's a memory error for longer PW's, but I thought I ruled that out by using a generator(yield) instead of a list.. or didn't I?
Thanks for your Help
import string
from itertools import permutations
characters = string.ascii_letters
user_pw = input('Set your PW')
attempts = 0
done = False
while True:
def Generator():
guess_storage = [''.join(p) for p in permutations(characters, len(user_pw))]
for current in guess_storage:
yield current
for guess in Generator():
attempts += 1
if guess == user_pw:
print('SUCESS: ')
print('Password: ', guess)
done = True
if done == True:
break
print('Attempts: ', attempts)
I'll start with the memory error. By doing:
guess_storage = [''.join(p) for p in permutations(characters, len(user_pw))]
you are 'gluing' all the generated permutations back into a single list before then iterating over that list. this is where you run out of memory. Instead, try making a generator to be iterated over:
guess_storage = (''.join(p) for p in permutations(characters, len(user_pw)))
Or don't use a generator, and just join each permutation as it is yielded (see below).
Next, you are doing this all in a while loop (including the generator), but this is unnecessary and inefficient. Just build the Generator once, then when you are looping over every guess from the generator, just break out when it finds a match.
A (shorter) way of writing what I think you intended is:
def Generator():
for current in permutations(characters, len(user_pw)):
yield ''.join(current)
for guess in Generator():
print("Guessing", guess) # To show the guesses in action
attempts += 1
if guess == user_pw:
print('SUCCESS: ')
print('Password: ', guess)
print('Attempts: ', attempts)
break
You can also avoid manually tracking the attempts counter by using:
for attempts, guess in enumerate(Generator()):
Related
I am trying to produce a list of odd numbers using a generator (just to get a better insight into generators). I wrote the following code but, it doesn't stop running! While I expect the code stops when the condition i>n meets.
Any help is appreciated.
import sys
def odd(n):
i=0
while True:
if i%2==0:
continue
yield i
i+=1
if i>n:
return
# Here we build a generator
g = odd(10)
while True:
try:
print(next(g),end=' ')
except StopIteration:
sys.exit()
When i is even, you don't increment it, so it stays even for every subsequent iteration of the loop and never gets larger than n.
You want to increment i whether or not it is even.
def odd(n):
i=0
while True:
if i%2 != 0: # yield i only if it is odd
yield i
i+=1 # Increment i in either case
if i>n:
return
In my opinion, you have two style issues in your code that make it hard to see the problem:
The use of continue. A simple if statement would make it easier to see which code might not execute and which code will definitely execute. continue is mainly useful when you have nested if statements making things complicated.
You don't utilize the while condition. This assumes that the while loop will have to execute at least once. When writing a loop, you should generally consider what happens if the loop needs to execute 0 times. What if someone passes an argument of -1? What if you change the initial value of i to 1 to save an iteration?
def odd(n):
i = 0
while i <= n:
if i % 2:
yield i
i += 1
# Automatically return and throw StopIteration.
I wrote a program to find out the primes in a list of numbers, just so practice formatting and all. Here is my code:
from math import *
#Defining range to be checked
a = range(1,10)
#Opening empty list
b = []
#Wilsons method for primes
for n in a:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
This code runs without issue and fairly fast, at least at this stage of use. However, when I include a while statement(see below), it is substantially slower.
from math import *
#Defining range to be checked
a = range(1,10)
#Opening empty list
b = []
#Wilson't method for primes
for n in a:
while n>1:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
Could anyone explain why that would be the case?
n.b: I know there are more efficient method to find primes. I am just practicing formatting and all, although I don't mind improving my code.
edit: mistaken addition of less than symbol rather than the appropriate greater than symbol. Corrected.
As pointed out by several people your code will result in an infinite loop as the value of n does not change within your while-loop.
You are probably not looking for a while loop in the first place. It should be sufficient to use the for loop without the first iteration (n = 1). If you insist on including n=1, using an if statement is a possible workaround:
a=range(1,10)
b=[]
for n in a:
if n>1:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
I'm trying to demonstrate how yield in Python is used. I want to demonstrate that through an example.
The example will ask the user to enter yes or no, and increase the counter n by 1 every time yes is entered.
The part I want to show how yield works is when the user calls the function again, and getting an updated value of the number of times. For instance, if return was used, and the user runs the script again, it will start from scratch and the number of times would be 1. Rather, I want the user to get 1,2,3,...etc, that is the number of times yes was entered.
The issue here is how to use yield to demonstrate such example. In the code I wrote below, I always get a generator object returned rather than the value of n. How can I get the integer value n returned instead?
def yes_times(answer):
n = 0
if answer == 'yes':
n = n + 1
yield n
answer = raw_input('"yes" or "no": ')
times = yes_times(answer)
print 'You answered yes ' + str(times) + ' times'
Thanks.
I'm trying to demonstrate how yield in Python is used.
... example problem ...
The issue here is how to use yield to demonstrate such example.
You're doing this backwards.
Don't demonstrate how yield is used by picking some arbitrary example, which is unrelated to generator behaviour, and try implementing it using yield.
Instead, choose something that yield is good at, and implement that - ideally with a comparable non-yield implementation so you can show the benefits.
Simple example: toy implementation of range
def naive_range(begin, end):
i = begin
result = []
while i < end:
result.append(i)
i = i + 1
return result
def generate_range(begin, end):
i = begin
while i < end:
yield i
i = i + 1
Now, can you name the drawbacks of the naive implementation? When will it be significantly worse than the generator, and why?
For your example, you can try:
def yes_times(answer = None):
count = 0
while True:
if answer=="yes":
count += 1
answer = yield count
else:
answer = yield count
gen = yes_times()
gen.next()
while True:
answer = raw_input('"yes" or "no": ')
print 'You answered yes ' + str(gen.send(answer)) + ' times'
The problem is that you're trying to use the generator as if it were a function. I strongly recommend that you go through a tutorial on line until you grasp the difference -- after all, that's how I quit making exactly this mistake. :-)
Your statement
times = yes_times(answer)
instantiates the generator, which is what you get when you print. Instead, you need to either use the next function ...
times = next(yes_times(answer))
... or properly employ a generator that does what you need. The general use of a generator is to recognize it as a stream of data, something like
for times in yes_times(answer):
However, this requires that you update your generator to get the input itself.
Is that enough to get you moving?
I am trying to write a piece of code that will generate a permutation, or some series of characters that are all different in a recursive fashion.
def getSteps(length, res=[]):
if length == 1:
if res == []:
res.append("l")
res.append("r")
return res
else:
for i in range(0,len(res)):
res.append(res[i] + "l")
res.append(res[i] + "r")
print(res)
return res
else:
if res == []:
res.append("l")
res.append("r")
return getSteps(length-1,res)
else:
for i in range(0,len(res)):
res.append(res[i] + "l")
res.append(res[i] + "r")
print(res)
return getSteps(length-1,res)
def sanitize(length, res):
return [i for i in res if len(str(i)) == length]
print(sanitize(2,getSteps(2)))
So this would return
"LL", "LR", "RR, "RL" or some permutation of the series.
I can see right off the bat that this function probably runs quite slowly, seeing as I have to loop through an entire array. I tried to make the process as efficient as I could, but this is as far as I can get. I know that some unnecessary things happen during the run, but I don't know how to make it much better. So my question is this: what would I do to increase the efficiency and decrease the running time of this code?
edit = I want to be able to port this code to java or some other language in order to understand the concept of recursion rather than use external libraries and have my problem solved without understanding it.
Your design is broken. If you call getSteps again, res won't be an empty list, it will have garbage left over from the last call in it.
I think you want to generate permutations recursively, but I don't understand where you are going with the getSteps function
Here is a simple recursive function
def fn(x):
if x==1:
return 'LR'
return [j+i for i in fn(x-1) for j in "LR"]
Is there a way to combine the binary approach and a recursive approach?
Yes, and #gribbler came very close to that in the post to which that comment was attached. He just put the pieces together in "the other order".
How can you construct all the bitstrings of length n, in increasing order (when viewed as binary integers)? Well, if you already have all the bitstrings of length n-1, you can prefix them all with 0, and then prefix them all again with 1. It's that easy.
def f(n):
if n == 0:
return [""]
return [a + b for a in "RL" for b in f(n-1)]
print(f(3))
prints
['RRR', 'RRL', 'RLR', 'RLL', 'LRR', 'LRL', 'LLR', 'LLL']
Replace R with 0, and L with 1, and you have the 8 binary integers from 0 through 7 in increasing order.
You should look into itertools. There is a function there called permutations which does exactly what you want to achieve here.
I'm having trouble with a program, the program takes one word, and changing one letter at a time, converts that word into the target word. Although, keep in mind that the converted word must be a legal word according to a dictionary of words that I've been given.
I'm having trouble figuring out how to make it recursive. The program has a limit to the amount of steps it must take.
The output needs to be a list. So if the parameters for the function changeling are
changeling("find","lose"), the output should be:
['find','fine','line','lone','lose'].
with my current code:
def changeling(word,target,steps):
holderlist=[]
i=0
if steps<0 and word!=target:
return None
if steps!=-1:
for items in wordList:
if len(items)==len(word):
i=0
if items!=word:
for length in items:
if i==1:
if items[1]==target[1] and items[0]==word[0] and items[2:]==word[2:]:
if items==target:
print "Target Achieved"
holder.list.append(target)
holderlist.append(items)
holderlist.append(changeling(items,target,steps-1))
elif i>0 and i<len(word)-1 and i!=1:
if items[i]==target[i] and items[0:i]==word[0:i] and items[i+1:]==word[i+1:]:
if items==target:
print "Target Achieved"
holderlist.append(items)
holderlist.append(changeling(items,target,steps-1))
elif i==0:
if items[0]==target[0] and items[1:]==word[1:]:
if items==target:
print "Target Achieved"
holderlist.append(items)
holderlist.append(changeling(items,target,steps-1))
elif i==len(word)-1:
if items[len(word)-1]==target[len(word)-1] and items[0:len(word)-1]==word[0:len(word)-1]:
if items==target:
print "Target Achieved"
holderlist.append(items)
holderlist.append(changeling(items,target,steps-1))
else:
return None
i+=1
return holderlist
I receive a messy output:
['fine', ['line', ['lone', ['lose', []]]], 'fond', []]
I get the answer I wanted, but I'm not sure how to a)clean it up, by not having lists within lists. and b)fond appears, because when find is called it gives fine and fond, fine is the one that ends up with the target word, and fond fails, but I'm not sure how to get rid of it once I've appended it to the holderlist.
Any help would be appreciated.
Cheers.
I'm not completely convinced that using extend instead of append will solve all your problems, because it seems like that may not account for making a change that does not lead to solving the word and requires backtracking.
If it turns out I am correct and the other answers don't end up working, here is a recursive function that will convert your current result into what you are looking for:
def flatten_result(nested_list, target):
if not nested_list:
return None
for word, children in zip(nested_list[::2], nested_list[1::2]):
if word == target:
return [word]
children_result = flatten_result(children, target)
if children_result:
return [word] + children_result
return None
>>> result = ['fine', ['line', ['lone', ['lose', []]]], 'fond', []]
>>> flatten_result(result, 'lose')
['fine', 'line', 'lone', 'lose']
If you're trying to add a list to a list, you probably want extend rather than append.
http://docs.python.org/library/stdtypes.html#mutable-sequence-types
Here's an alternative implementation. It doesn't use recursion, but instead permutations. It's been rewritten to pass the wordlist rather than rely on the global wordlist, which should make it more portable. This implementation relies strictly on generators, too, which ensures a smaller memory footprint than expanding lists (as in the extend/append solution)
import itertools
somelists = [['find','fine','line','lone','lose'],
['bank','hank','hark','lark','lurk'],
['tank','sank','sink','sing','ding']]
def changeling(word, target, wordlist):
def difference(word, target):
return len([i for i in xrange(len(word)) if word[i] != target[i]])
for length in xrange(1, len(wordlist) + 1):
for possibilities in [j for j in itertools.permutations(wordlist, length) if j[0] is word and j[-1] is target]:
#computes all permutations and discards those whose initial word and target word don't match parameters
if all(difference(possibilities[i], possibilities[i+1]) == 1 for i in xrange(0, len(possibilities) - 1)):
#checks that all words are exactly one character different from previous link
return possibilities
#returns first result that is valid; this can be changed to yield if you wish to have all results
for w in somelists:
print "from '%s' to '%s' using only %s" % (w[-2], w[0], w)
print changeling(w[-2], w[0], w)
print
w[-2], w[0] can be modified/replaced to match any words you choose