Why does 'while' cause the program to run longer in Python - python

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)

Related

Python Non-recursive Permutation

Does anyone understand the following iterative algorithm for producing all permutations of a list of numbers?
I do not understand the logic within the while len(stack) loop. Can someone please explain how it works?
# Non-Recursion
#param nums: A list of Integers.
#return: A list of permutations.
def permute(self, nums):
if nums is None:
return []
nums = sorted(nums)
permutation = []
stack = [-1]
permutations = []
while len(stack):
index = stack.pop()
index += 1
while index < len(nums):
if nums[index] not in permutation:
break
index += 1
else:
if len(permutation):
permutation.pop()
continue
stack.append(index)
stack.append(-1)
permutation.append(nums[index])
if len(permutation) == len(nums):
permutations.append(list(permutation))
return permutations
I'm just trying to understand the code above.
As mentioned in the comments section to your question, debugging may provide a helpful way to understand what the code does. However, let me provide a high-level perspective of what your code does.
First of all, although there are no recursive calls to the function permute, the code your provided is effectively recursive, as all it does is keeping its own stack, instead of using the one provided by the memory manager of your OS. Specifically, the variable stack is keeping the recursive state, so to speak, that is passed from one recursive call to another. You could, and perhaps should, consider each iteration of the outer while loop in the permute function as a recursive call. If you do so, you will see that the outer while loop helps 'recursively' traverse each permutation of nums in a depth-first manner.
Noticing this, it's fairly easy to figure out what each 'recursive call' does. Basically, the variable permutation keeps the current permutation of nums which is being formed as while loop progresses. Variable permutations store all the permutations of nums that are found. As you may observe, permutations are updated only when len(permutation) is equal to len(nums) which can be considered as the base case of the recurrence relation that is being implemented using a custom stack. Finally, the inner while loop picks which element of nums to add to the current permutation(i.e. stored in variable permutation) being formed.
So that is about it, really. You can figure out what is exactly being done on the lines relevant to the maintenance of stack using a debugger, as suggested. As a final note, let me repeat that I, personally, would not consider this implementation to be non-recursive. It just so happens that, instead of using the abstraction provided by the OS, this recursive solution keeps its own stack. To provide a better understanding of how a proper non-recursive solution would be, you may observe the difference in recursive and iterative solutions to the problem of finding nth Fibonacci number provided below. As you can see, the non-recursive solution keeps no stack, and instead of dividing the problem into smaller instances of it(recursion) it builds up the solution from smaller solutions. (dynamic programming)
def recursive_fib(n):
if n == 0:
return 0
elif n == 1:
return 1
return recursive_fib(n-1) + recursive_fib(n-2)
def iterative_fib(n):
f_0 = 0
f_1 = 1
for i in range(3, n):
f_2 = f_1 + f_0
f_0 = f_1
f_1 = f_2
return f_1
The answer from #ilim is correct and should be the accepted answer but I just wanted to add another point that wouldn't fit as a comment. Whilst I imagine you are studying this algorithm as an exercise it should be pointed out that a better way to proceed, depending on the size of the list, may be to user itertools's permutations() function:
print [x for x in itertools.permutations([1, 2, 3])]
Testing on my machine with a list of 11 items (39m permutations) took 1.7secs with itertools.permutations(x) but took 76secs using the custom solution above. Note however that with 12 items (479m permutations) the itertools solution blows up with a memory error. If you need to generate permutations of such size efficiently you may be better dropping to native code.

RecursionError in Project Euler #5

I'm receiving a "maximum recursion depth exceeded" error when executing my program to solve this problem. Project Euler's question #5 asks to find:
The smallest positive number that is evenly divisible by all of the numbers from 1 to 10.
I've tried to write a program that recursively checks if x is divisible by each integer 1-10, and if it doesn't then we call it again with x incremented by 1 and repeat until x is found. (In this case the answer is 2520, which is why I added the if statement.)
def euler5(x):
if x < 2521:
for i in range(1, 11):
if x % i == 0:
print(x)
else:
euler5(x+1)
else:
print(x)
x = 2
print(euler5(x))
The reason for this is that Python (or CPython, at least) has a limited stack size and no tail call optimization. So you cannot use unbounded recursion in Python, unlike Scheme (for example).
The solution is to use a regular loop:
x = 0
while True:
x += 1
# Put loop body here
In you defined function, you could you a loop even if you do not know math functions. However, your code is not efficient as program has to keep checking the values to see if it matches the condition. Recursion is not recommended as it should be flexible to be used with other values not just for this question. Euler questions are meant to train your coding practices.
A better method can be used to simplify your code:
from functools import reduce
from fractions import gcd
def lcm(a,b):
return a*b//gcd(a,b) #gcd is greatest common divisor AKA HCF
print (reduce(lcm, range(1, 20+1)))

Project Euler #4 with python. What;s wrong with my code?

I am trying to do project euler problem 4 using python. The problem statement goes like this:
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.
I wrote down a solution for it:
s=0
x=100
y=100
list=[]
z=x*y
def palindrome():
while z>=1:
s=s*10+z%10
z=z/10
if z==s:
list.append(z)
while x<=999:
while y<=999:
palindrome()
y=y+1
x=x+1
y=100
print list
It ended up giving an error along the lines of 'z referenced beyond assignment'.
I searched for a solution to this error before finally deciding to use the syntax 'global' to bypass this error.
s=0
x=100
y=100
list=[]
z=x*y
def palindrome():
global z
global s
global x
global y
global list
while z>=1:
s=s*10+z%10
z=z/10
if z==s:
list.append(z)
while x<=999:
while y<=999:
palindrome()
y=y+1
x=x+1
y=100
print list
Now it doesn't give an error, but it gives an empty list as output. I tried to debug the code by inserting print statements in between. The loops appear to work fine, as 'x' and 'y' print all the values they are supposed to. However, I get an empty list as an output to the print list command and 'z' does not apparently change values and is stuck at 100000 despite me using while loops to change the values of x and y.
I am at a loss on how to proceed from here.
The error you got was probably:
UnboundLocalError: local variable 'z' referenced before assignment
This means that z was not defined, at least not within the palindrome() function. Your solution of adding the global keyword is technically correct. However, as others have pointed out already, use of globals makes the code hard to follow.
It's not clear to me what palindrome() is supposed to do. Is it supposed to check if a number is a palindrome? Generate palindrome numbers? To fix this problem, you should think about structuring your code. There are many ways to do this, of course, and with time you will find your own style.
My advice, then, is to think about how you would solve this in general. If you don't know the solution, coding won't help you. Sometimes, when solving problems like this one, I write functions without declaring their bodies. You can do this top-down or bottom-up, both work. For example:
def is_palindrome(n):
""" Check if n is a palindrome number. """
pass
def multiples_of_3_digits():
""" Return all numbers that are the product of two 3-digit numbers ."""
pass
def main():
print max(n for n in multiples_of_3_digits() if is_palindrome(n))
This way you can focus on solving the problem, then on the actual coding. Maybe you will add helper functions or realize you can solve the problem in a more efficient way, but it's a start. Good luck!
min=100
max=999
max_palindrome = 0
for a in range(min,max + 1):
for b in range(a + 1, max + 1):
prod = a*b
if prod > max_palindrome and str(prod)==(str(prod)[::-1]):
max_palindrome = prod
print max_palindrome
Here we are only concerned with the maximum palindrome, and so we don’t spend any time storing other palindromes once they are known to be non-maximum. Also, the if statement first checks if the given product is larger than the maximum known palindrome before using the string cast and list slice to check whether or not the number is even a palindrome. This should speed up our code a bit since the greater than comparison will often fail, regardless of whether the product in question is a palindrome. When we run this, we get the following.
906609
Alternate Way:
I would discourage you to use the global variables because of the reasons pointed out by others. I would also like you to refer to Andre's approach as it will teach you to organize yourself. In this approach too I will be using 2 functions is_palindrome(num) [to check if the number is palindrome or not] and find_max_palindrome [to find the largest palindrome]
def is_palindrome(num):
reversed = 0
original = num
if num < 10:
return True
if num % 10 == 0:
return False
while num >= 1:
reversed = (reversed * 10) + (num % 10)
num = num/10
if original == reversed:
return True
else:
return False
def find_max_palindrome():
max_palindrome = 0
a = 999
b = 999
prod = 0
while a > 99:
b = 999
while b >= a:
prod = a*b
if prod > max_palindrome and is_palindrome(prod):
max_palindrome = prod
b = b -1
a = a - 1
return max_palindrome
print find_max_palindrome()

How do I double my step variable for each for-loop iteration in Python 3?

I'm new to Python, and I'm playing around with recursive functions just for practice.
I made the following algorithm which takes a number as x and halves it until it is equal to 1. n is the number of times x has been halved.
def binary(x, n = 0):
print(n,":",x)
x = x // 2
n += 1
if x > 0:
binary(x, n)
return x
return x
I'm trying to make a loop that will call binary() with multiple values for x. I want my step to be doubled each iteration. I have it working with a while loop like the one below.
i = 1
while i < 1000000000:
print("when x is", i, ":")
binary(i)
i += i
For some reason though, I can't seem to achieve the same thing with a For loop. Here's what I have now.
for i in range(1,1000):
print("when x is", i, ":")
binary(i)
i += i
In the code above, i += i does not seem to effect the i in my header. I know that range() takes a third parameter called step, but I've tried this:
for i in range(1,1000, i += i):
# statements
This gives me a name error, and says "i is not defined".
Most of my experience with programming is in JavaScript and C#. In both of those languages I wouldn't of had any trouble doing this.
How would I get this to work in a For loop using Python 3?
The third parameter of range is indeed step. But you should use it with a computed value like:
for i in range(1,1000,2):
#statements
The reason why your i += i didn't worked is because, under the hood, the for-loop is executing something similar to i = next(...) at the end of an iteration, overiding your last increment.
Edit
You can achieve the desired effect using a generator, but it kinda kills the "trying to avoid while-loops" thing:
def myrange(start, stop):
i = start
while i < stop:
yield i
i += i
for i in myrange(1, 1000):
print(i)
Anyway, while-loops are perfectly valid constructs and I’d personnally go with one in this case. Do not forget that the for-loop has a rather different semantic in python than in both languages you’re used to. So trying to use a for-loop because you are able to do so with javascript seems like a bad idea if all what you need is a while-loop.
range can step by a fixed amount, but not a variable amount. Use a while-loop to increment i by i:
i += i
You could replace the while-loop with an iterator, such as:
import itertools as IT
for i in (2**i for i in IT.count()):
if i >= 1000000000: break
print("when x is", i, ":")
binary(i)
but I don't think this has any advantage over a simple while-loop.
If all you're doing is doubling i, then why not just raise it to the power?
for p in range(int(1000000000**0.5)):
print(binary(2**p)

python list generation/saving bug

I am trying to make program that prints all the possible combinations for a to zzz. I tried to add a save state feature, and it works fine but there is this bug.
Let's say I interrupted the program when it printed something like e. When I execute the program again, it works fine until z but after z instead of printing aa it prints ba and continues from ba. This happens right after it prints zz too. it prints baa instead of aaa. How can I fix this?
Here is what I did so far:
import pickle,os,time
alphabet="abcdefghijklmnopqrstuvwxyz"
try:
if os.path.isfile("save.pickle")==True:
with open("save.pickle","rb") as f:
tryn=pickle.load(f)
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
b=a[tryn:]
for k in b:
print(k)
time.sleep(0.01)
tryn+=1
else:
tryn=0
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
for k in a:
print(k)
tryn+=1
time.sleep(0.01)
except KeyboardInterrupt:
with open("save.pickle","wb") as f:
pickle.dump(tryn,f)
If you're using python2, or python3 as the tag suggests, this exists in the standard library already. See itertools, product py2, and product py3, for a simple way to solve this problem.
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
b=a[tryn:]
Here's your bug. You skip the first tryn strings of every length, rather than just the first tryn strings. This would be easier to recognize in the output if it weren't for the following:
for k in b:
print(k)
time.sleep(0.01)
tryn+=1
You modify tryn, the number of things you're skipping. When you print out length-2 strings, you skip a number of them equal to the number of length-1 strings. When you print out length-3 strings, you skip a number of them equal to the number of length-2 strings. If tryn were bigger than the number of length-1 strings, you would skip even more.
your problem is almost certainly here:
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
Perhaps you shouldn't assign the in-loop value to a, but instead use a different name? Otherwise, you are changing what you use every time through the loop....
Edit: More detail. So, technically user2357112's answer is more correct, but I'm amending mine. The initial answer was just from a quick reading, so the other answer is close to the original intent. But, the original version is inefficient (for more reasons than not using product :), since you are generating the inner loops more than once. So let's walk through why this is a bad idea, as an educational exercise:
Initial algorithm:
for i in range(n):
assign a to alphabet
for j in range(i):
i times, we rewrite a to be all combinations of the current set against the alphabet.
Note that for this algorithm, to generate the length(n) product, we have to generate all previous products length(n-1), length(n-2), ..., length(1). But you aren't saving those.
You'd be better off doing something like this:
sum_list = alphabet[:]
#get a copy
product_list = alphabet[:]
#Are we starting at 0, or 1? In any case, skip the first, since we preloaded it
for i in range(1, n):
# Your existing list comprehension was equivalent here, and could still be used
# it MIGHT be faster to do '%s%s'%(x,y) instead of x+y... but maybe not
# with these short strings
# This comprehension takes the result of the last iteration, and makes the next iteration
product_list = [x+y for x,y in product(product_list, alphabet)]
# So product list is JUST the list for range (n) - i.e. if we are on loop 2, this
# is aaa...zzz. But you want all lengths together. So, as you go, add these
# sublists to a main list.
sum_list.extend(product_list)
Overall, you are doing a lot less work.
Couple other things:
You're using i as a loop variable, then re-using it in the loop comprehension. This is conflicting, and probably not working the way you'd expect.
If this is to learn how to write save/restore type apps... it's not a good one. Note that the restore function is re-calculating every value to be able to get back where it left off - if you could rewrite this algorithm to write more information out to the file (such as the current value of product_list) and make it more generator-like, then it will actually work more like a real-world example.
Here is how I would suggest solving this problem in Python. I didn't implement the save state feature; this sequence is not a really long one and your computer should be able to produce this sequence pretty fast, so I don't think it is worth the effort to try to make it cleanly interruptable.
import itertools as it
def seq(alphabet, length):
for c in range(1, length+1):
for p in it.product(alphabet, repeat=c):
yield ''.join(p)
alphabet="abcdefghijklmnopqrstuvwxyz"
for x in seq(alphabet, 3):
print(x)
If you really wanted to, you could make a one-liner using itertools. I think this is too hard to read and understand; I prefer the above version. But this does work and will be somewhat faster, due to the use of itertools.chain and itertools.imap() rather than a Python for loops.
import itertools as it
def seq(alphabet, length):
return it.imap(''.join, it.chain.from_iterable(it.product(alphabet, repeat=c) for c in range(1, length+1)))
alphabet="abcdefghijklmnopqrstuvwxyz"
for x in seq(alphabet, 3):
print(x)
In Python 3.x you could just use map() rather than itertools.imap().

Categories

Resources