How to determine the complexity of python code? - python

I need to understand the complexity of the following code. I am familiar with the concepts of all the Big O() notation and also have read a lot of blogs but I cant figure how to apply to big programs.
Following is the code for largest pallindrome number from 100 to 999:
def isPaindrome(number):
stringNum = str(number)
firstDigit_index,lastDigit_index=0,len(stringNum)-1
isPalindrome = False
while(lastDigit_index > 0 and firstDigit_index < lastDigit_index):
#print(stringNum[f],"==",stringNum[l],"......")
if(stringNum[firstDigit_index]==stringNum[lastDigit_index]):
isPalindrome = True
else:
isPalindrome = False
break
firstDigit_index = firstDigit_index + 1
lastDigit_index = lastDigit_index - 1
if(isPalindrome):
return number
else:
return 0
max = 0
startRange = 100
endRange = 999
for i in range(endRange*endRange,startRange*startRange,-1):
factors = []
result = isPaindrome(i)
if(result!=0):
for i in range(startRange,endRange+1):
if(result%i==0):
factors.append(i)
if(len(factors)>1):
sumFactor = factors[(len(factors))-1] + factors[(len(factors))-2]
mul = factors[(len(factors))-1] * factors[(len(factors))-2]
if(sumFactor>max and mul==result):
max = sumFactor
print("Largest Palindrome made from product of two 3 digit numbers(",factors[(len(factors))-1],",",factors[(len(factors))-2] ,") is", result,".")
If anyone could just make me understant step by step how to calculate I'd be grateful.

As I mentioned, your isPalindrome function is literally incorrect, as you're not changing the indexes. I changed a bit of your also, so this is the version I'm analysing. (I'm only analysing the isPalindrome function, since I actually couldn't understand what the main function is doing), sorry!
def isPalindrome(n):
num = str(n)
head, tail = 0, len(num) - 1
while tail > head:
if num[head] != num[tail]: # Not symmetrical!! WARNING!
return False
head += 1 # move position
tail -= 1 # move position
return True
This code on average is O(|n|) i.e. O(log N) where N is the number. This is because on average, the if comparison has 50% chance of breaking (returning False) and 50% of continuing. Therefore the expected number of comparisons would be |n|/4 which is O(|n|).

Related

Codechef - Monkey Power(Problem Code: MPOW)

I am looking at this challenge on CodeChef:
There are n trees in a row and you have been given the height of these
trees. On each tree there is one monkey and each monkey have (sic)
particular power to climb the tree. Power of each monkey can be
calculated as π(x). Where x is the total number of maximum consecutive
trees (I.e. to the left of the current tree including itself also)
which has height less than or equal to the height of the current tree.
Print the maximum power that can be obtained among n monkeys. Here
π(x) can be stated as: - π(x)=(x)∗(x−1)∗(x−2)................1.
You have to answer t independent test cases.
Output the answer modulo 10^9+7.
[...]
Sample input: -
1
6
6 4 12 3 6 7
Output: -
6
EXPLANATION:-
For the first tree smaller or equal to itself to the
left is only none. So, for the first monkey power will be π(1) =1. For
the second tree smaller or equal to its left is none so π(1)=1.For the
third tree the tree which is smaller or equal to its left is
π(3)=6.And similarly for the rest of the trees.
My Code:
def power(x):
if x==1:
return 1
else:
return x*power(x-1)
for _ in range(int(input())):
mod = 10**9 + 7
number = int(input())
trees = list(map(int,input().split()))
stack = []
list1 = []
for i in range(number):
if len(stack)==0:
list1.append(-1)
elif stack[-1]>trees[i]:
list1.append(trees.index(stack[-1]))
elif stack[-1]<=trees[i]:
while len(stack)>0 and stack[-1]<=trees[i]:
stack.pop()
if len(stack)==0:
list1.append(-1)
else:
list1.append(trees.index(stack[-1]))
stack.append(trees[i])
list2 = [0]*number
for i in range(number):
list2[i]= i- list1[i]
x = max(list2)
print(power(x)%mod)
CodeChef is giving me a runtime error. Can anyone tell me why?
The prime reason you got the runtime error was due to the recursion. Have a look at the constraints on the number of elements n as provided in the question: 1 <= n <= 10^5.
Therefore, for relatively larger values of x, the power(x) function consumed more than allowed memory and caused the system stack to overflow.
Remember, always avoid recursion if you can solve the problems using loops.
I replaced the recursive call with a loop in the function and I got Time Limit Exceeded.
Time Limit Exceeded means, we have successfully avoided the runtime error, but now, we need to work on optimising the algorithm further.
Have a look at the following code with correct implementation of modulo arithmatic for large values of x:
def power(x, mod):
if x<=1:
return 1
else:
ans = 1
for i in range(1, x+1):
ans = (ans * i)%mod
return ans
for _ in range(int(input())):
mod = 10**9 + 7
number = int(input())
trees = list(map(int,input().split()))
stack = []
list1 = []
for i in range(number):
if len(stack)==0:
list1.append(-1)
elif stack[-1]>trees[i]:
list1.append(trees.index(stack[-1]))
elif stack[-1]<=trees[i]:
while len(stack)>0 and stack[-1]<=trees[i]:
stack.pop()
if len(stack)==0:
list1.append(-1)
else:
list1.append(trees.index(stack[-1]))
stack.append(trees[i])
list2 = [0]*number
for i in range(number):
list2[i]= i- list1[i]
x = max(list2)
print(power(x, mod)%mod)
PS: As the runtime error is avoided, I suggest you to optimise your code further.

Happy Number String Python function

I am pretty new to Python and just got started in Leet and I am doing the Happy Number question, only half of the test cases have been passed. I would appreciate any help. Thanks
A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.
My test cases were 19, 100 where it output True correctly but when I do 7, it is wrong
def isHappy(self, n: int) -> bool:
if (n == 1):
return True
sum = 0
flag = False
for j in range(1,100):
x = str(n)
a = list(x)
for i in range(0,len(a)):
sum += int(a[i])*int(a[i])
if sum == 1:
return True
break
else:
x = sum
return False
Here is an implementation using a set to keep track of numbers that we have already seen. (I've removed the self argument here for sake of something that can be run outside of your test class.)
def isHappy(n: int) -> bool:
seen = set()
while True:
if n == 1:
return True
if n in seen:
return False
seen.add(n)
n = sum(int(c) ** 2 for c in str(n))
Your code has various issues aside from the fact that the number 100 is arbitrary.
Mainly that you never update n in your loop. Also you do not wait for the completion of the for loop before testing sum (in fact your break is never reached), you initialise sum only once, and you return False prematurely. Here is a minimally corrected version of your code, although still subject to the fact that there is no rule about 100 maximum iterations.
def isHappy(n: int) -> bool:
if (n == 1):
return True
for j in range(1,100):
x = str(n)
a = list(x)
sum = 0
for i in range(0,len(a)):
sum += int(a[i])*int(a[i])
if sum == 1:
return True
else:
n = sum
return False

Palindromic numbers divisible by any given number

I have the following problem to solve:
The numbers 545, 5995 and 15151 are the three smallest palindromes divisible by 109. There are nine palindromes less than 100000 which are divisible by 109.
How many palindromes less than 10**32 are divisible by 10000019 ?
So my code is shown below.
In theory my code will work, but to count all the way from 0 to 10**32 would take my computer literally YEARS.
Is there anyway to improve this code?
Python code:
listPalindroms=[]
for i in range (0,10**32):
strI = str(i)
printTrue = 1
if len(strI) == 1:
listPalindroms.append(i)
else:
if len(strI)%2 ==0:
FinalVal = int(len(strI)/2)
for count in range (0,FinalVal):
if strI[count]!=strI[-count-1]:
printTrue = 0
if printTrue==1: listPalindroms.append(i)
else:
FinalVal = int(round(len(strI)/2))-1
for count in range (0,FinalVal):
if strI[count]!=strI[-count-1]:
printTrue = 0
if printTrue ==1: listPalindroms.append(i)
i=0
for item in listPalindroms:
if item%10000019 ==0:
i = i + 1
print (i)
The problem is presented as Project Euler 655
You are getting all palindromes between 0 and 10**32 then filtering using divisibility. But you can do it the other way around also. Just find the multiples of 10000019 that are less than 10**32 then check if each multiple is a palindrome.
This way you can avoid checking palindromes for numbers that are not required.
i = 1
number = 10000019
list_palindromes = []
element = number * i
while element < (10**32):
e = str(element)
for j in range(len(e)):
if e[j] != e[len(e)-j-1]:
break
if len(e)-1 == j:
list_palindromes.append(e)
print(e)
i += 1
element = number * i
Given that MathJax doesn't work here it's gonna be tough to present my solution.
When you look at a number, eg. 1991, you can write this as 1000*1+100*9+10*9+1*1.
If you look at the remainder when dividing by 19 we have that:
(1000*1+100*9+10*9+1*1)%19 = ((1000%19)*1+(100%19)*9+(10%19)*9+(1%19)*1)%19 = (12*1+5*9+10*9+1*1)%19 = 10.
Therefore 19 doesn't divide 1991
For a palindrome, abcba, we can use this property of modular arithmetic to see that 19 divides abcba if and only if:
(7*a + 3*b + 5*c)%19 = 0
Because
(10000*a+1000*b+100*c+10*b+a)%19 = (10001*a+1010*b+100*c)%19 = (10001*a%19+1010*b%19+100*c%19)%19 = (7*a + 3*b + 5*c)%19
By using this method we can cut the number of iterations down to the square root of the max value. A routine for calculating the sum of ever palindrome less than 10**10 that is divisible by 109 will look some thing like this.
maxValue = 10**5
divisor = 109
moduli = []
for i in range(0,33):
moduli.append((10**i)%divisor)
def test(n,div):
n = str(n)
sum_odd = 0
sum_even = 0
for i in range(len(n)):
sum_even = sum_even + int(n[i])*(moduli[i]+moduli[2*len(n)-i-1])
if i != len(n)-1:
sum_odd = sum_odd + int(n[i])*(moduli[i]+moduli[2*len(n)-i-2])
else:
sum_odd = sum_odd + int(n[i])*(moduli[i])
if sum_odd%div==0 and sum_even%div==0:
return 2
if sum_odd%div==0 or sum_even%div==0:
return 1
else:
return 0
# The sum starts at -1 because 0 is counted twice
sum = -1
for a in range(maxValue):
sum = sum + test(a,divisor)
print(sum)
Running the calculation for every palindrome less than 10**32, still requires 10**16 iterations, so it's not efficient enough for your problem, it's however better than previous answers (requiring about 10**24 iterations).
Well, you just need to check for numbers divisible by your divisor, so why check numbers before that, and while incrementing, why not just increment the divisor amount?
def is_palin(num):
num_str = str(num)
for index in range(len(num_str)/2):
if num_str[index]==num_str[len(num_str)-1-index]:
continue
return False
return True
def multiple(divisor, end):
count=0
index = divisor*2
while index<end:
if is_palin(index):
count+=1
index+=divisor
return count
if __name__=="__main__":
print(multiple(109, 100000))
# print(multiple(10000019, 10**32))
This approach still takes a lot of time, and I'd recommend finding a better method.

Hofstadter equation related code in python

There was this question in a university assignment related to Hofstadter sequence. It basically say that it is a integer sequence blah blah and there are two values for a given index n. A male value [M(n)] and a female value [F(n)].
They are defined as:
M(0)=0
F(0)=1
M(n)=n-F(M(n-1))
F(n)=n-M(F(n-1))
And we were asked to write a program in python to find the Male and Female values of the sequence of a given integer.
So the code I wrote was:
def female(num):
if num == 0:
return 1
elif num >0:
return num - male(female(num-1))
def male(num):
if num==0:
return 0
elif num >0:
return num - female(male(num-1))
And when executed with a command like
print male(5)
It works without a fuss. But when I try to find the value of n = 300, the program gives no output.
So I added a print method inside one of the functions to find out what happens to the num value
[ elif num>0:
print num ...]
And it shows that the num value is decreasing until 1 and keeps hopping between 1 and 2 at times reaching values like 6.
I can’t figure out why it happens. Any insight would be nice. Also what should I do to find the values relevant to bigger integers. Thanks in advance. Cheers.
The code is theoretically fine. What you underestimate is the complexity of the computation. Formula
M(n)=n-F(M(n-1))
actually means
tmp = M(n-1)
M(n) = n - F(tmp)
So if you represent this calculation as a tree of necessary calls, you might see that its a binary tree and you should go through all its nodes to calculate the results. Given that M(n) and F(n) are about n/2 I'd estimate the total number of the nodes to be of order 2^(n/2) which becomes a huge number once you put n = 300 there. So the code works but it just will take a very very long time to finish.
The one way to work this around is to employ caching in a form of memoization. A code like this:
femCache = dict()
def female(num):
#print("female " + str(num))
global femCache
if num in femCache:
return femCache[num];
else:
res = 1 # value for num = 0
if num > 0:
res = num - male(female(num-1))
femCache[num] = res
return res
maleCache = dict()
def male(num):
#print("male " + str(num))
global maleCache
if num in maleCache:
return maleCache[num];
else:
res = 0 # value for num = 0
if num > 0:
res = num - female(male(num-1))
maleCache[num] = res
return res
print(male(300))
should be able to compute male(300) in no time.

Find the smallest equally divisible in a range of numbers in Python, puzzle

I'm trying to solve a projecteuler puzzle detailed below. My current function works for the numbers 1 to 10, but when I try 1 to 20 it just loops forever without a result.
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
def calculate():
results = dict()
target = 20
num_to_test = 1
while len(results) < target:
for j in range(1, target+1):
results[num_to_test] = True
if num_to_test % j != 0:
# current num_to_test failed in the 1-10, move on
del results[num_to_test]
break
num_to_test += 1
return min(results)
Can anyone see any issues in the logic, and especially I'd like to know why it is working for a target of 10, but not 20. Thanks
Your algorithm is pretty inefficient, but the core of your problem is that your results dictionary is accumulating 1 value for each integer that's evenly divisible by the numbers from 1-20, and your while loop is trying to keep going until it has 20 such numbers.
This is one correct way to implement this inefficient algorithm:
def calculate():
target = 20
candidate = 1
success = False
divisors = range(1, target+1)
while not success:
for divisor in divisors:
if candidate % divisor != 0:
candidate += 1
break
else:
success = True
return candidate
Note that the else clause really is on the for loop, not the if. From the tutorial on flow control:
Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement.
A somewhat more concise expression would be:
candidate = 0
while not success:
candidate += 1
success = all((candidate % divisor == 0 for divisor in divisors))
That uses a generator expression so all can short-circuit and avoid doing unnecessary calculation.
Since this is a puzzle I'll pass on suggesting better algorithms.
actually I have very efficient algorithm for that problem.
I'll not give you the code, but I could show you the way
For N = 10
1.Calculate all factors of all numbers from 5 to 10:
[[2, 3], [7], [2, 2, 2], [3, 3], [2, 5]]
2.calculate maximum number of each prime in the list
{2: 3, 3: 2, 5: 1, 7: 1}
3.get product of key power value
2^3 * 3^2 * 5 * 7 = 2520
A lot of the other answers mention the original code being inefficient, but they still loop through almost every number. Wouldn't it be more efficient to utilize an lcm function?
def calculate(num, current_lcm = 1):
if (num == 1): return current_lcm
return calculate(num - 1, lcm(num, current_lcm))
def lcm(a, b):
return a * b // gcd(a, b)
def gcd(a, b):
while b:
a, b = b, a % b
return a
print calculate(20)
While your algorithm is very inefficient, it may help a little to make this small change
if num_to_test % j = 0:
results[num_to_test] = True
else:
# current num_to_test failed in the 1-10, move on
break
Not sure why you are storing them all though? For debugging perhaps?
Hint. It would be better to calculate the prime factors of the result and simply multiply those together.
# spoiler
def calculate(target):
n = 1
for i in range(1, target+1):
for j in range(1, target+1):
if (n * j) % i == 0:
n *= j
break
return n
Dont store em all, instead just return early when you find it, get rid of that result dictionary, this is not optimal at all by the way, just a clean up
def calculate():
target = 20
num_to_test = 0
while True:
num_to_test += target
if all((num_to_test % j == 0) for j in range(1,target+1)):
return num_to_test
return -1
Also you dont need to test numbers that aren't multiples of your maximum. It'll run 20 times faster.
I switched to using a generator to test to see if the number was divisible by all() of the nubmers from 1 to 20
Props for writing your own algorithm and not copying one :)

Categories

Resources