Optimizing Python 3n + 1 Programming Challenge - python

I am trying to find a efficient solution for the 3n + 1 problem on uvaonlinejudge. The code I have uses memoization using a dictionary. Can anyone suggest an improvement(s) that will help with the execution time of this code? At the moment I am getting a 'Time limit Exceeded' error when I submit the code. If anyone has a working solution to the problem please share it with me. PLEASE DON'T mark this post as DUPLICATE. I have already seen this post and others on stackoverflow but they don't answer the question posted here. My code is as below:
import sys
def recCycleLength(n,cycLenDict):
if n==1:
return 1
if n not in cycLenDict:
if n%2==0:
cycLen = recCycleLength(n//2, cycLenDict)
cycLenDict[n] = cycLen + 1
return cycLen+1
else:
cycLen = recCycleLength(3*n+1, cycLenDict)
cycLenDict[n] = cycLen + 1
return cycLen+1
else:
return cycLenDict[n]
def maxCycle(a, b):
i = a
mydict = {}
maxLength = 1
while i <= b:
m = recCycleLength(i, mydict)
if m > maxLength:
maxLength = m
i = i + 1
return maxLength
for line in sys.stdin:
curr_line=line.split()
num1 = int(curr_line[0])
num2 = int(curr_line[1])
if num1>num2:
num1, num2 = num2, num1
m = maxCycle(num1, num2)
print("{} {} {}".format(num1, num2, m))

I found the problem in your code. Actually, you are not saving cycLenDict generated on the previous interval for next one. And this is why your code is so "slow" because it will generate all possible endings over and over again. Just move it in global scope or make something like this:
import sys
def rec(n, cache):
if n in cache:
return cache[n]
if n % 2 == 0:
cycle = rec(n//2, cache)
else:
cycle = rec(3*n+1, cache)
cache[n] = cycle + 1
return cache[n]
def cycle(a, b, cache):
return max(rec(i, cache) for i in range(a, b+1))
if __name__ == '__main__':
cache = {1: 1}
for line in sys.stdin:
a, b = map(int, line.split())
a, b = min(a, b), max(a, b)
m = cycle(a, b, cache)
print("{} {} {}".format(a, b, m))

The code seems to do the right thing to execute maxCycle optimally by caching all calculated results in mydict.
However, the input to the application consists of many pairs of values to be processed and maxCycle will reset mydict = {} and calculate everything from scratch.
I suggest remembering the results globally instead. A simple modification of the original code would be:
cycLenDict = {} # global dictionary
def recCycleLength(n): # no cycLenDict argument
if n==1:
return 1
if n not in cycLenDict:
# ...
def maxCycle(a, b):
# ...
while i <= b:
m = recCycleLength(i) # no myDict argument
To make everything a little bit nicer looking (without any difference performance-wise, compared to the solution above), make a decorator which remembers the results, so that the remainder of the code does not have to take care of that:
def memoize(func):
"""decorate any function which takes positional arguments to cache its results"""
func.results = {} # results are stored globally as the funtion's attribute
def memoized(*a): # memoized version of func
if a not in func.results: # if not cached
func.results[a] = func(*a) # save to cache
return func.results[a] # return from cache
return memoized
#memoize # with this, recCycleLength is called only once for every n value
def recCycleLength(n):
if n==1:
return 1
elif n%2==0:
return recCycleLength(n//2) + 1
else:
return recCycleLength(3*n+1) + 1
def maxCycle(a, b):
return max(recCycleLength(i) for i in range(a, b+1))

import sys
def cycle(current, count=1):
if current == 1:
return count
if current % 2 == 0:
return cycle(current / 2, count + 1)
return cycle(current * 3 + 1, count + 1)
def max_cycles(lower, upper):
max = 0
for current in range(lower, upper):
result = cycle(current, 1)
if result > max:
max = result
return max
def main(i, j):
if i > j:
(i, j) = (j, i)
print (i, j, max_cycles(i, j))
if __name__ == "__main__":
if len(sys.argv) < 3:
print 'usage: python filename argv1 argv2'
print 'exiting'
exit(0)
main(int(sys.argv[1]), int(sys.argv[2]))

Related

Invalid function return

This is my code, why when i print(is_happy(13)) i got True, but in the result of happy_numbers i don't get 13 and many correct numbers?
def is_happy(n, result = []):
next_number = sum(map(lambda x: int(x) ** 2, str(n)))
if next_number == 1:
return True
elif next_number in result:
return False
else:
result.append(next_number)
return is_happy(next_number)
def happy_numbers(n):
result = []
for i in range(1, n + 1):
if is_happy(i):
result.append(i)
return result
The issue is that you are using a mutable default argument in is_happy(n, result=[]). This means that the result won't start as an empty list every time it's run but rather will keep its old results.
To counter this, I'd remove the default argument and explicitly pass the array:
def is_happy(n, result):
# all the stuff you already had
return is_happy(next_number, result)
and in the other function
if is_happy(i, []):
Generators make your programs easier to write -
def is_happy(n, seen = set()):
if n == 1:
return True
elif n in seen:
return False
else:
return is_happy(sum(x ** 2 for x in digits(n)), seen | {n})
def digits(n):
if n < 10:
yield n
else:
yield from digits(n // 10)
yield n % 10
def happy_numbers(n):
for x in range(n):
if is_happy(x):
yield x
print(list(happy_numbers(100)))
is_happy can be optimized with lru_cache -
from functools import lru_cache
seen = set()
#lru_cache
def is_happy(n):
if n == 1:
return True
elif n in seen:
return False
else:
seen.add(n)
answer = is_happy(sum(x ** 2 for x in digits(n)))
seen.remove(n)
return answer
print(list(happy_numbers(10000)))
before optimization
with LRU cache
real
1,303 ms
284 ms
user
470 ms
118 ms
sys
19 ms
13 ms

Repeat a recursion of a function "x" times

I need to repeat a function "x" amount of times by recursion. I can do that, but the output needs to carry over for each one.
num is the amount of times to be called
def incr(n):
print(n+1)
return n+1
def foo(fn, num):
def op(n):
fn(n)
print(num)
if(num > 0):
foo(fn, num-1)
return op
f = foo(incr, 3)
f(5)
Right now I get 6. The intended result is 6,7,8
Here is one way to do it:
def incr(n):
print(n+1)
return n+1
def foo(fn, num):
def op(n):
if num > 0:
n = fn(n)
foo(fn, num-1)(n)
return op
f = foo(incr, 3)
f(5)
A recursive function should call itself, see https://www.geeksforgeeks.org/recursive-functions/ for more.
I also added a parameter that keeps track of the end result.
def incr(n, num):
if (n != num):
print(n + 1)
return incr(n + 1, num)
def foo(fn, num):
def op(n):
fn(n, n + num)
return op
f = foo(incr, 3)
f(5)

Recursive Hexadecimal Conversion Python 3

I've made a hexadecimal converter to practice recursion/recursive thinking. I, however, The recurssion doesn't appear to be happening as the functions seems to just output the result of 9 as of current.The code is as follows:
import math
curr=0
def convert(x):
L=len(x)
L-=1
sol=0
if L == 0:
return 0
else:
if x[curr]==["A","a"]:
v=10
elif x[curr]==["B","b"]:
v=11
elif x[curr]==["C","c"]:
v=12
elif x[curr]==["D","d"]:
v=13
elif x[curr]==["E","e"]:
v=14
elif x[curr]==["F","f"]:
v=15
else:
v=int(x[curr])
sol+=((v)*(16**(L-1)))
return sol + convert(x[curr+1])
def main():
print(convert('98A'))
main()
You were setting L = len(x) everytime you call the function. Here is one solution:
import math
def convert(x, L):
c = len(x) - 1
sol=0
if L > c:
return 0
else:
if (x[L]=="A" or x[L]=="a"):
v=10
elif (x[L]=="B" or x[L]=="b"):
v=11
elif (x[L]=="C" or x[L]=="c"):
v=12
elif (x[L]=="D" or x[L]=="d"):
v=13
elif (x[L]=="E" or x[L]=="e"):
v=14
elif (x[L]=="F" or x[L]=="f"):
v=15
else:
v=int(x[L])
sol+=((v)*(16**(c - L)))
print(sol)
return sol + convert(x, L + 1)
def main():
print(convert('98A', 0))
main()
You can use something like this:
class HexMap:
# mapping char to int
d = { hex(n)[2:]:n for n in range(16)}
def convert(x):
s = 0
# use reverse string and sum up - no need for recursion
for i,c in enumerate(x.lower()[::-1]):
s += HexMap.d[c]*16**i
return s
def main():
print(convert('98A'))
main()
Output:
2442
Recursive version:
# class HexMap: see above
def convert(x):
def convert(x,fak):
if not x:
return 0
else:
return HexMap.d[x[-1]]*16**fak + convert(x[:-1],fak+1)
return convert(x.lower(),0)
def main():
print(convert('98A'))
main()
Same output.

python3 recursive function of n * (b(a))

I'm trying to write a function that would recursively hash a key for n times, alternating between sha224 and sha256. Each iteration would be hash_256(hash_224)--a hash256 for the hash224 of the key--so that it would yield n * (hash_256(hash_224)). However, I'm new to coding and can't figure out how to write a recursive function with these parameters.
import hashlib
def shasum(key, n):
key = str(key).encode('utf-8')
hash_a = hashlib.sha224(key).hexdigest().encode('utf-8'))
hash_b = hashlib.sha256(hash_a).hexdigest()
if n == 0 or 1:
return hash_b #one iteration of 256(224)
else:
return n-1
return hash_b #stuck here
Edited: now it behaves like a number generator. What's wrong?
import hashlib
n = 0
def sha480(seed):
hashed_224 = str(hashlib.sha224(seed)).encode('utf-8')
hashed_256 = hashlib.sha256(hashed_224).hexdigest()
hashed_480 = str(hashed_256)
print("hash: " + hashed_480)
def repeater(key, n):
if n == 0:
return key
seed = str(key).encode('utf-8')
while n > 0:
return sha480(repeater(seed, n-1))
repeater('what', 2)
You have no recursive calls at all. You could change it to:
def hash_a(key):
return hashlib.sha224(key).hexdigest().encode('utf-8')
def hash_b(key):
return hashlib.sha256(key).hexdigest()
def shasum(key, n):
if n == 0: # base case: 0 iterations -> return key itself
return key
key = str(key).encode('utf-8')
return hash_b(hash_a(shasum(key, n - 1))) # recursve call
A side note: n == 0 or 1 is equivalent to (n == 0) or 1 which is always true. For that pattern, use n == 0 or n == 1 or shorter n in (0, 1)
Your code is nearly correct. just some minor issues fixed as below
import hashlib
def shasum(key, n):
print ("n: " + str(n))
key = str(key).encode('utf-8')
hash_a = hashlib.sha224(key).hexdigest().encode('utf-8')
print ("hash_a: " + str(hash_a))
hash_b = hashlib.sha256(hash_a).hexdigest()
print ("hash_b: " + str(hash_b))
if n == 0:
return hash_b #one iteration of 256(224)
else:
return shasum(hash_b, n-1)

Keep getting type error with decorative function

I keep getting a type error for this. I am experimenting with decorative functions. Any help is appreciated
def primer(func):
def primes(n):
print (n)
return None
#primer
def find_prime(n):
while True:
count = 2
if (count == n):
z = ("PRIME")
return z
elif (n % count == 0):
z = n / count
return z
else:
count += 1
continue
prime = find_prime()
prime(10)
def primer(func):
def primes(n):
print(n)
#return None: dont know why this is here, you could do without it
return primes
#The nontype error is occuring because your code is returning none
#so to fix that all you have to do is return the inner function
#primer
def find_prime(n):
while True:
count = 2
if (count == n):
z = ("PRIME")
return z
elif (n % count == 0):
z = n / count
return z
else:
count += 1
continue
prime = find_prime
# if you want to turn a function into a variable you have to make sure it's
# callable, which means no parantheses around it
prime(15) # then you can call it

Categories