How do I find the Space Complexity of this code? - python

I've was writing some code in Python to solve the following problem:
"Implement the function fib(n), which returns the n^th number in the Fibonacci sequence,using only O(1) space."
Below is the code I wrote, but I'm unsure how to find its space complexity.
def fib(n):
x=0
y=1
z=0
for num in range(0,n):
if (num % 2) == 0:
z = x
x = x+y
print(x)
else:
y = x
x = x+z
print(x)
return x

To find space complexity, you look at any collections / data-structures you've made.
Any primitive values are constant space.
A range() is a generator function that can be considered as constant space since it does not need to maintain more state than its current value and a step, as compared to a list(range()), which allocates memory for all values between start and end.

Related

How do I optimize my Python code to perform my calculation using less memory?

I have put together the following code in order to determine if a number has an odd or even number of divisors. The code works well with relatively small numbers but once a larger number like 9 digits is entered it hangs up.
def divisors(n):
num = len(set([x for x in range(1,n+1) if not divmod(n,x)[1]]))
if (num != 0 and num % 2 == 0):
return 'even'
else:
return 'odd'
what can I do to make this more efficient?
Here's your problem:
num = len(set([x for x in range(1,n+1) if not divmod(n,x)[1]]))
This constructs a list, then constructs a set out of that list, then takes the length of the set. You don't need to do any of that work (range(), or xrange() for that matter, does not produce repeated objects, so we don't need the set, and sum() works on any iterable object, so you don't need the list either). While we're on the subject, divmod(n, x)[1] is just a very elaborate way of writing n % x, and consumes a little bit of extra memory to construct a tuple (which is immediately reclaimed because you throw the tuple away). Here's the fixed version:
num = sum(1 for x in xrange(1,n+1) if not n % x)
You do not need to test every possible divisor, testing up to sqrt(n) is enough. This will make your function O(sqrt(n)) instead of O(n).
import math
def num_divisors(n):
sqrt = math.sqrt(n)
upper = int(sqrt)
num = sum(1 for x in range(1, upper + 1) if not n % x)
num *= 2
if upper == sqrt and num != 0:
num -= 1
return num
In my benchmarks using python2 this is 1000 times faster than sum(1 for x in range(1, n + 1) if not n % x) with n = int(1e6) and 10000 times faster for 1e8. For 1e9 the latter code gave me a memory error, suggesting that the whole sequence is stored in memory before doing the sum because in python 2 range() returns a list and I should be using xrange() instead. For python3 range() is fine.

Fibonacci in python - Can some explain how this Fibonacci number between range works?

I am learning python programming and i have run into a bit of jumble. I found this code to compute Fibonacci sequence by user inputted start and end number. Can someone explain how this code works?
def fib(lowerbound, upperbound):
x = 0
y = 1
while x <= upperbound:
if (x >= lowerbound):
yield x
x, y = y, x + y
startNumber = 10
endNumber = 100
for fib_sequence in fib(startNumber, endNumber):
print "And the next number is... %d!" % fib_sequence
The function def fib( ... returns a list on which you can iterate using for i in <return val by fib>
I guess your main confusion is around yield part. What it does is it remembers the past values (ie val of x and y) and next time it continues from previous values and whenever it sees yield it adds that yielded value (x here) to the returned list.
https://www.jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/
This article shall clear all your doubts.
Cheers!

How can I prove the correctness of my greatest common divisor progam?

I need to write a program that prints the greatest common divisor of two entered integers, and also prove the correctness of it. I wrote the following:
def main():
x = int(input("Enter the first integer: "))
y = int(input("Enter the second integer: "))
print(gcd(x,y))
def gcd(x,y):
if x > y:
smaller = y
else:
smaller = x
for i in range(1, smaller + 1):
if ((x % i == 0) and (y % i == 0)):
gcd = i
return gcd
main()
The precondition is clearly that x and y should be two integers. The postcondition is the greatest common divisor of x and y. I need to identify the loop invariant, and show that it is ok after initialization, that it remains true after every iteration. Also, I'm suppose to prove the postcondition by using the loop invariant and show that the loop is finite.
I think the loop invariant is that i is always smaller or equal to x and y, and that x % i = r and y % i = s for some integers r and s. This clearly holds for every iteration. The loop continues until r = 0 and s = 0, and then it terminates. Finiteness is guaranteed because the iteration has an upper bound.
Is my reasoning correct here? Does this proves the correctness of the loop?
I think you can state a loop invariant a bit more clearly if you initialize the gcd variable to 1 before entering the loop (which be over a range starting at 2 if you want). Your invariant will be:
gcd is the largest integer less than or equal to i such that x % gcd == y % gcd == 0.
Your loop ends because i iterates over a finite range (ending with i == min(x, y)). The postcondition is that gcd is the greatest common divisor of x and y, which is proved by the loop invariant (as long as you can prove the GCD of two numbers is not larger than the smaller of them).

While loops with multiple condtions. Python

I'm writing a function that, given a random int, will return the next int that is both a prime number and a palindrome. i.e getint(13) will return 101.
One of the conditions of this function is that recursion is not allowed.
Where am i going wrong here?
def golf(number):
x = number +1
for i in range(2, x):
while str(x) != str(x)[::-1] and x % i == 0:
x += 1
return x
Divide the problem into smaller pieces. It will be easier for you to understand what's going on:
def golf(number):
x = number + 1
while True:
if is_palindrome(x) and is_prime(x):
return x
x += 1
Now all you have to do is implement is_palindrome(x) and is_prime(x):
import math
def is_prime(x):
for i in xrange(2, math.ceil(math.sqrt(x))+1):
if x % i == 0:
return False
return True
def is_palindrome(x):
x = str(x)
return x == x[::-1]
Side note: the math.ceil(math.sqrt(x))+1 might be an overkill (perhaps int(...)+1 is enough) if math.sqrt works correctly for squares (i.e. does not depend on floating point calculations so math.sqrt(a*a)==a is always true). Well in worst case the loop will do one more iteration then necessary. Better safe then sorry.
Your for loop will only go as far as x's initial value: the range(2,x) is computed only once, right after x = number + 1. So increasing x inside the loop doesn't make a difference. Consider using a while loop, something like:
i = 2
while i <= x:
...
i += 1
And you should probably first check is x is a palindrome (a "cheap" operation) and then, if it is, check weather it is prime (an "expensive" operation), not try do it all in a couple of nested loops that make little sense.
In your code:
def golf(number):
x = number +1
for i in range(2, x):
while str(x) != str(x)[::-1] and x % i == 0:
x += 1
return x
The program execution will not satisfy the while loop half of the time since any even number will not be divisible by any odd number and vice-versa. Also in a single iteration of the for loop, x will be incremented every time the while loop is satisfied.
You need to separate the prime checking and palindrome checking. The approach given by freakish is the simplest.

Project Euler 2 python3

I've got, what I think is a valid solution to problem 2 of Project Euler (finding all even numbers in the Fibonacci sequence up to 4,000,000). This works for lower numbers, but crashes when I run it with 4,000,000. I understand that this is computationally difficult, but shouldn't it just take a long time to compute rather than crash? Or is there an issue in my code?
import functools
def fib(limit):
sequence = []
for i in range(limit):
if(i < 3):
sequence.append(i)
else:
sequence.append(sequence[i-1] + sequence[i-2])
return sequence
def add_even(x, y):
if(y % 2 == 0):
return x + y
return x + 0
print(functools.reduce(add_even,fib(4000000)))
The problem is about getting the Fibonacci numbers that are smaller than 4000000. Your code tries to find the first 4000000 Fibonacci values instead. Since Fibonacci numbers grow exponentially, this will reach numbers too large to fit in memory.
You need to change your function to stop when the last calculated value is more than 4000000.
Another possible improvement is to add the numbers as you are calculating them instead of storing them in a list, but this won't be necessary if you stop at the appropriate time.

Categories