Codechef - Monkey Power(Problem Code: MPOW) - python

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.

Related

Eulers problem №3, code stucks on big numbers

I'm currently learning Python, and practice with euler's problem's.
I'm stuck on 3rd problem, my code didn't working on the big numbers, but with other number's it's working.
n = 600851475143
x = 0
for i in range(2,n):
if(n%i == 0):
if(x < i): x = i
print(x)
The console just didn't get any results and stucks.
P.S https://projecteuler.net/problem=3
(sorry for my bad english)
It's running, but just the time it takes is huge.
You can check by the following code, it keeps going to print x's.
n = 600851475143
x = 0
for i in range(2, n):
if n % i == 0:
if x < i:
x = i
print(x)
To save time, you can try the following code instead of your code:
n = 600851475143
x = 0
for i in range(2, n):
if n % i == 0:
x = n // i
break
print(x)
which prints 8462696833 instantly. But as #seesharper stated on the comment, it is merely the largest factor and not a prime factor. So it is not the correct answer to the Project Euler Problem #3.
Check this code you will save a lot of time
Using sqrt for optimization
from math import sqrt
def Euler3(n):
x=int(sqrt(n))
for i in range(2,x+1):
while n % i == 0:
n //= i
if n == 1 or n == i:
return i
return n
n = int(input())
print(Euler3(n))
Also, check my Euler git repo
There are only 6 solutions in python2 and it's pretty old but they are very well optimized.
from tqdm.auto import tqdm
n = 600851475143
x = 0
for i in tqdm(range(2,n)):
if(n%i == 0):
x = i
print(x)
If using classic python, your program will last for at least 40 min, that's why you had no output. I suggest you either use numpy to go through, or add a step because I think even numbers won't work.
I used tqdm to estimate the time needed for the for loop to run, you can download it using pip install tqdm
What would be more efficient is to only divide your number by actual primes. You also only need to check divisors up to the square root of the number given that any valid (integer) result will be also be a divisor (which you will already have checked if your divisor goes beyond the square root).
# generator to obtain primes up to N
# (sieve of Eratosthenes)
def primes(N):
isPrime = [True]*(N+1)
p = 2
while p<=N:
if isPrime[p]:
yield p
isPrime[p::p] = (False for _ in isPrime[p::p])
p += 1 + p>2
# use the max function on primes up to the square root
# filtered to only include those that divide the number
n = 600851475143
result = max(p for p in primes(int(n**0.5)+1) if n%p == 0)
print(result) # 6857
Alternatively, you can use sequential divisions to find factors while reducing the number as you go along. This will cause each new factor you find to be a prime number because divisions by all smaller numbers will already have been done and removed from the remaining number. It will also greatly reduce the number of iterations when your number has many prime factors.
def primeFactors(N): # prime factors generator
d,d2 = 2,4 # start divisors at first prime (2)
while d2<=N: # no need to go beyond √N
if N%d:
d += 1 + (d&1) # progress by 2 from 3 onward
d2 = d*d # use square of d as limiter
else:
yield d # return prime factor
N //= d # and reduce number
if N>1: yield N # final number could be last factor
print(*primeFactors(600851475143)) # 71 839 1471 6857
print(max(primeFactors(600851475143))) # 6857

How to determine the complexity of python code?

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|).

how to optimize the python code of staircase problem to work with larger values?

I had to create a program for a staircase problem where i had to design staircase with n number of bricks. The complexity is that the number of bricks in every step must be unique, and less than the previous step.
for example, using 6 bricks, i can make a staircase with step height (5,1) , (4,2) and (3,2,1) but not (3,3) or (1,2,3) or (2,4) or any other permutation.
I have designed the code but the problem is that it runs fine till n as nearly 100 or 120, but freezes if the inputs are larger than these values. I am a beginner in Python programming and learning concepts on the go.
I tried memoization, but to no avail. I need to know if there is something else i can do to make my code more optimized to run over n as 200-250?
import cProfile
def solution(n):
memory = {0: [], 1: [1], 2: [2]}
def rec(max_val, i):
t = []
r = []
for j in range(1,i):
y = i - j
if y < max_val:
if y > j:
t = [y, j]
r.append(t)
if n / 2 >= j >= 3 and j in memory:
mem = memory[j]
[r.append([y, item]) for item in mem]
else:
if y >= 3 and n / 2 >= j >= 3 and j in memory:
mem = memory[j]
for item in mem:
if y > item[0]:
r.append([y, item])
else:
v = rec(y, j)
if v:
for item in v:
t = [y, item]
r.append(t)
if r:
if i in memory:
if len(memory[i]) < len(r):
memory[i] = r
else:
memory[i] = r
return r
def main_func(n):
stair = []
max_val = 201
total = 0
for i in range (1,n):
x = n - i
if x > i:
s = [x, i]
total += 1
if i >= 3:
u = rec(max_val, i)
total += len(u)
elif x == i and i >= 3:
u = rec(max_val, i)
total += len(u)
elif x < i and i >= 3:
u = rec(x, i)
total += len(u)
return total
stairs = main_func(n)
return (stairs)
print(solution(100))
You can approach the problem recursively from the perspective of the base of the stairs. The strategy is to add up the pattern counts of the next step levels for each base size.
For example, with 6 bricks, the first call would go through base sizes n = 5,4,3,2 and make a recursive calls to know how many combinations are possible for the next step levels using the remaining bricks and with a maximum base of n-1. The sum of next levels counts will constitute the total count of possible stair patterns.
At the top of the stairs, you need at least 3 bricks to add more than one level so you can stop the recursion with a count of 1 when there are fewer than 3 bricks left. This will roll up the recursive calls to form larger totals and produce the right answer once the original call completes.
In order to optimize this process, you can use memoization and you can also short circuit the calculation using the base size provided at each recursion.
For a given base, the largest number of bricks that can be used will be the sum of numbers from 1 to that base. This can be computed using Gauss's formula: base*(base+1)/2. If you have more bricks than the maximum brick count for the base, then you can stop the recursion and return a count of zero (since you have too many remaining bricks and will not be able to fit them all over the previous level's base)
Another way to optimize the calculation is to loop through the base sizes in decreasing order. This will allow you to stop the loop as soon as you get a count of zero for the next levels, which means that there are too many remaining bricks for that base size (or any smaller base size)
Here's an example (using lru_cache for memoization):
from functools import lru_cache
#lru_cache(None)
def stairCount(N,base=None):
base = min(base or N-1,N)
if N > base*(base+1)//2: return 0
if N < 3: return 1
count = 0
while True:
nextLevels = stairCount(N-base,base-1)
if nextLevels == 0: break
count += nextLevels
base = base - 1
return count
With these optimizations, the function will respond in less than a second for up to 600 bricks (depending on the speed of your computer).
With Python's list comprehensions, you could write this function more concisely (though it would lose the decreasing base order optimization ≈ 10%):
#lru_cache(None)
def stairCount(N,base=None):
base = min(base or N-1,N)
if N > base*(base+1)//2: return 0
if N < 3: return 1
return sum(stairCount(N-b,b-1) for b in range(2,base+1))
EDIT Here's a version with a "manual" memoization (i.e. without using functools):
def stairCount(N,base=None,memo=dict()):
memoKey = (N,base)
if memoKey in memo: return memo[memoKey]
base = min(base or N-1,N)
if N > base*(base+1)//2: return 0
if N < 3: return 1
count = 0
while True:
nextLevels = stairCount(N-base,base-1,memo)
if nextLevels == 0: break
count += nextLevels
base = base - 1
memo[memoKey] = count
return count

How to create a piece of code which checks a number for its greatest prime factor?

I was trying to make a program which would check a number for its greatest prime factor. I was almost done when this error message came up. list index out of range.
What does this mean and what is wrong with my code?
Here is my code.
def is_prime(n):
for i in range(3, n):
if n % i == 0:
return False
return True
def Problem3():
x = 144
n = 2
not_a_factor = []
z = []
prime = []
not_a_prime = []
while n < x:
if x%n == 0:
z.append(n)
else:
not_a_factor.append(n)
n = n + 1
for i in z:
if is_prime(z[i]) == True:
prime.append(z[i])
else:
not_a_prime.append(z[i])
print(prime)
Problem3()
You're just a bit off. for-loops in Python iterate an object and return it's entities, not a pointer/ index.
So just use the thing you get from each iteration of 'z'
(Side note: might want to check out this post, it'll help you make your is_prime function more performant)
def is_prime(n):
for i in range(3, n):
if n % i == 0:
return False
return True
def Problem3():
x = 144
n = 2
not_a_factor = []
z = []
prime = []
not_a_prime = []
while n < x:
if x%n == 0:
z.append(n)
else:
not_a_factor.append(n)
n =+ 1 # Python version of n++
for i in z: # Python for-loop is more like a say "for each", no need for the indexing
if is_prime(i): # no need for '=='; Python will 'truthify' your object
prime.append(i)
else:
not_a_prime.append(i)
print(prime)
Problem3()
"list index out of range - what does this mean?"
The message list index out of range refers to an IndexError. Basically, this means that you are attempting to refer to an index in a list that doesn't exist.
Using your code as an example: you generate a list, z, containing the factors of the number 144. You then iterate through each element in this list (for i in z:). This means that for the:
1st iteration: i is the 1st element in z, which is 2;
2nd iteration: i is the 2nd element in z, which is 3;
and so on.
Then, you attempt if isprime(z[i]) == True:. So, as written, your program works like this:
1st iteration: if isprime(z[2]) == True:;
2nd iteration: if isprime(z[3]) == True:;
...
8th iteration: if isprime(z[16]) == True:
At this point, your code prompts an IndexError, because there are only 13 elements in z.
"what is wrong with my code?"
One way to get the result that you want is to iterate through range(len(z)) instead of each element of z. So, adjust the line for i in z to for i in range(len(z)).
Additionally, since prime is a list, and you want to return the greatest prime factor, change print(prime) to print(max(prime)).
These two changes will give you the result you are looking for.
Additional Learnings
Overall, your program could be written much more efficiently. If you want a simple algorithm to determine the greatest prime factor of a number, here is one possibility:
def greatest_prime_factor(n):
greatest_prime = 1
for i in range(n + 1):
# iterate through range(n). We skip index 0.
if i == 0:
continue
# determine if the number i is a factor of n.
if n % i == 0:
# determine if the number i is prime.
for i_ in range(2,i):
if i % i_ == 0:
break
else:
# update greatest_prime.
greatest_prime = max(greatest_prime, i)
return greatest_prime
print (greatest_prime_factor(144))
This algorithm saves a lot of memory space when compared with your original program by not initializing lists to store numbers that are primes, that aren't primes, etc. If you want to store those values, that's up to you; there are just far more efficient possibilities for what you appear to want to achieve.
Check this link for some more info on algorithmic efficiency and how to think about time and space complexity.

given and integer, return the next integer that is a prime number and a palindrome . Python

Given any random integer, create a function to find the next number that is a prime number and also a palindrome.
My attempt
def golf(number):
x = number + 1
for i in range(2, x):
if x % i == 0 or str(x) != str(x)[::-1]:
golf(number + 1)
return x
E.g golf(13) = 101
I'm actually looking for an alternative option than the recursion method i used. How can this best be accomplished without using recursion?
Thanks
Here's a variation on byron he's answer which adds several optimizations:
We can eliminate all even x values (other than 2) before doing any elaborate tests, since we can trivially tell they are not prime.
A small improvement is to only call str(x) once, and reuse the value later.
We can take advantage of the fact that all even-length palindromes are multiples of 11, which means that (except for 11 itself) they're not prime. We can jump ahead to the next odd-length x value.
Since we've already eliminated even numbers, our prime test only needs to test odd divisors. Further we can stop our loop when we reach sqrt(x), rather than going all the way to x itself.
Finally, there's no need to use a Boolean flag variable to carry the primeness out of the loop. If we don't break, the else block attached to the loop will be run.
The code:
import math
def next_prime_palindrome(x):
while True:
x += 1
if x > 2 and x % 2 == 0: # even numbers greater than 2 are non-prime
continue
s = str(x) # compute str(x) just once
if x > 11 and len(s) % 2 == 0: # all even-length palindromes are multiples of 11
x = 10 ** len(s) # so jump to the next odd-length integer
continue
if s != s[::-1]: # palindrome test
continue
for i in xrange(3, round(math.sqrt(x))+1, 2): # loop over odd potential divisors
if x % i == 0: # prime test
break
else: # this else block runs only if no break happened in the loop, so x is prime
return x
Here are some tests runs, showing a few cases where the optimizations save significant time:
>>> next_prime_palindrome(1)
2
>>> next_prime_palindrome(3)
5
>>> next_prime_palindrome(9)
11
>>> next_prime_palindrome(11)
101
>>> next_prime_palindrome(99999)
1003001
>>> next_prime_palindrome(999999999)
10000500001
A further improvement might be to directly generate palindromes, rather than working with integers to start with, and doing a palindrome test to filter them. That would get quite a bit further from your original design, so I'll leave that for someone else.
Palindrome are a sparser set of numbers than primes, and you can generate palindromes directly.
Consider the sequence 98.102
These are palidrome numbers you can base on these
989, 9889, 999, 9999, 10001, 100001, 10101, 101101, 10201, 102201
ADDED
Not also that all of the palidromes with an odd number of digits will come before the palidromes with an even number of digits.
If you write this as a generator (ie using yield) get get a straightforward algorithm for generating palindromic numbers in order.
For 1..9 you generate either 9 or 18 palindromes depending upon whether you consider 1 digit numbers palindromic.
For 10..99 you generate 90 even digit and 90 odd digit palindromes.
For 100..999 you generate 900 even digit and 900 odd digit palindromes.
You have just generated all 1989 (or 1997 if including single digit numbers) of the palindromic numbers less than 1 million. There are 78,498 primes less than 1 million
Any algorithm that is based on generating primes then testing for a palindrome will be much slower that generating palindromes and then testing for primes
def golf(number):
primes = []
i = 2
while i <= number:
if isPrime(i, primes):
primes.append(i)
i += 1
answer = primes[-1] + 1
while True:
if isPrime(answer, primes):
primes.append(answer)
if str(answer) == str(answer)[::-1]:
return answer
answer += 1
def isPrime(n, primes):
for (p for p in primes if p<=n**0.5):
if n%p == 0:
return False
return True
Your solution can be slightly modified in order to create an iterative solution:
def golf(number):
x = number + 1
while True:
is_golf = True
for i in range(2, x):
if x % i == 0 or str(x) != str(x)[::-1]:
is_golf = False
break
if is_golf:
return x
x += 1
improved according to Blckknght's advice, thanks.
def golf(number):
x = number
while True:
x += 1
if str(x) != str(x)[::-1]:
continue
for i in xrange(2, x):
if x % i == 0 :
break
else:
return x

Categories

Resources