Write recursive pseudocode problem in python code - python

I am having trouble to solve the problem below:
I am supposed to implement the pseudocode in python. h is just some given list. I have tried all kinds of stuff, most recently for example:
def _p_recursion(n,i):
if n == 0:
return h[n+i]
for i in range(1,n+1):
s = 0
s = h[i] + _p_recursion(n-i,i)
v.append(s)
return s
v=[]
h =[0,3,11,56,4]
_p_recursion(2,0)
I know why it does not work but I am not able to come up with a solution. It feels like a pretty simple problem but I have been stuck with it for hours. I am not very experienced with recursive functions only really basic ones. I can't come up with one. Feels like the simplest way to come up with a solution must be to append all possible outputs of p(n) into an array and then one can easily find the maximum. For the values in the code above 11 is missing from the list v. Can anybody give me a hint how to fix this problem using python statements for, if, while...
Thanks in advance!

Code
def p(n):
" Implements function shown in image "
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p(n-i) for i in range(1, n+1))
More explicit recursive function
def p(n):
" Implements function shown in image "
if n == 0:
return 0
# Stores previous results in an array for formula
# then computes max
previous = []
for i in range(1, n+1):
previous.add(h[i] + p(n-i))
return max(previous)
Test
h = range(10)
for i in range(len(h)):
print(i, p(i))
Output
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
Performance
darrylg solution
def p_dg(n):
" Implements function shown in image "
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p_dg(n-i) for i in range(1, n+1))
Poster (Karl) solution
def p(n,m):
if n == 0:
return 0
for i in range(1,n+1):
s = h[i] + p(n-i,m)
m[n-1].append(s)
return max(m[n-1])
def p_caller(n):
if type(n) != int:
return
m =[]
for g in range(n):
m.append([])
return p(n,m)
darrylg solution with caching (memorization)
def p_cache(n, cache = {}):
if n in cache:
return cache[n]
if n == 0:
return 0
cache[n] = max(h[i] + p_cache(n-i) for i in range(1, n+1))
return cache[n]
Timing (seconds)
darrylg method
time taken: 0.20136669999965306
Poster method (Karl)
time taken: 26.77383000000009
darrylg with memoization
time taken: 0.00013790000002700253
Thus Caching greatly improves performance.
Timing Code
import time
import random
# timing software allows timing recursive functions
# Source: https://stackoverflow.com/questions/29560643/python-counting-executing-time-of-a-recursion-function-with-decorator
def profile(f):
is_evaluating = False
def g(x):
nonlocal is_evaluating
if is_evaluating:
return f(x)
else:
start_time = time.perf_counter()
is_evaluating = True
try:
value = f(x)
finally:
is_evaluating = False
end_time = time.perf_counter()
print('time taken: {time}'.format(time=end_time-start_time))
return
return g
# darrylg method
#profile
def p_dg(n):
" Implements function shown in image "
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p_dg(n-i) for i in range(1, n+1))
# Poster (Karl) method
def p(n,m):
if n == 0:
return 0
for i in range(1,n+1):
s = h[i] + p(n-i,m)
m[n-1].append(s)
return max(m[n-1])
#profile
def p_caller(n):
if type(n) != int:
return
m =[]
for g in range(n):
m.append([])
return p(n,m)
# darrylg with caching (Memoization)
#profile
def p_cache(n, cache = {}):
if n in cache:
return cache[n]
if n == 0:
return 0
cache[n] = max(h[i] + p_cache(n-i) for i in range(1, n+1))
return cache[n]
h = [random.randint(0, 100) for _ in range(18)]
l = 17
print('darrylg method')
p_dg(l)
print('Poster method (Karl)')
p_caller(l)
print('darrylg with memoization')
p_cache(l)

I was not able to comment my code in the previous post so I am writing it here instead:
(my Solution to problem)
def p(n,m):
if n == 0:
return 0
for i in range(1,n+1):
s = h[i] + p(n-i,m)
m[n-1].append(s)
return max(m[n-1])
def p_caller(n):
if type(n) != int:
return
m =[]
for g in range(n):
m.append([])
return p(n,m)
I would never actually call p() only p_caller()
DarrylG solution to problem:
def p(n):
if n == 0:
return 0
# Finds the max of h[i] + p(n-i)
# with p(n-i) found recursively
# Gets access to h from the global namespace
return max(h[i] + p(n-i) for i in range(1, n+1))
What would the difference in worst case time complexity be between these methods and why?

Related

What should I do? I have a recursive function that returns the mth item of a n-bonacci sequence. It has for loop. I am banned from using loops

I have the following recursive function to get the mth term of a n-bonacci sequence as shown below this question. My problem is that the use of for and while loops is totally banned from the code, so I need to get rid off
for i in range(1, n+1):
temp += n_bonacci(n,m-i)
and convert the code into something that is not a loop but nevertheless achieves the same effect. Among the things I can use, but this is not an exclusive enumeration, is: (1) use built-in functions like sum() and .join() and (2) use list comprehensions.
The full code is as follows:
def n_bonacci(n,m): #n is the number of n initial terms; m is the mth term.
if (m < n-1):
return 0
elif (m == n-1):
return 1
else:
temp = 0
#[temp += n_bonacci(n,m-i) for i in range(n)] #this is an attempt at using list comprehension
for i in range(1, n+1):
temp += n_bonacci(n,m-i)
return temp
print("n_bonacci:",n_bonacci(2,10))
print("n_bonacci:",n_bonacci(7,20))
Here's a solution that avoids any type of loop, including loops hidden inside comprehensions:
def n_bonacci_loopless(n, m):
def inner(i, c):
if i == m:
return sum(c)
else:
return inner(i+1, c[-(n-1):] + [sum(c)])
if m < n-1:
return 0
elif (m == n-1):
return 1
else:
return inner(n, [0] * (n-1) + [1])
The base cases are the same, but for recursive cases it initialises a list of collected results c with n-1 zeroes, followed by a one, the sum of which would be the correct answer for m == n.
For m > n, the inner function is called again as long as i < m, summing the list and appending the result to the end of the last n-1 elements of the list so far.
If you are allowed to use comprehensions, the answer is trivial:
def n_bonacci(n,m):
if (m < n-1):
return 0
elif (m == n-1):
return 1
else:
return sum(n_bonacci(n, m-i) for i in range(1, n+1))
You can rewrite the code as follows using list comprehensions:
def n_bonacci(n,m): #n is the number of n initial terms; m is the mth term.
if (m < n-1):
return 0
elif (m == n-1):
return 1
else:
return sum(n_bonacci(n, m-i) for i in range(1, n + 1))
print("n_bonacci:",n_bonacci(2,10))
print("n_bonacci:",n_bonacci(7,20))
To go beyond #Grismar 's answer you can write your own version of sum which doesn't use loops.
def n_bonacci_loopless(n, m):
def recsum(l, s=0):
return recsum(l[1:], s + l[0])
def inner(i, c):
if i == m:
return recsum(c)
else:
return inner(i+1, c[-(n-1):] + [recsum(c)])
if m < n-1:
return 0
elif (m == n-1):
return 1
else:
return inner(n, [0] * (n-1) + [1])

Can recursive method calls share a variable of dictionary type in Python 3

I got the following error message No value for argument 'd' in method call (no-value-for-parameter)when I tried to solve LeetCode 364. Nested List Weight Sum II (link) with the following code.
class Solution:
"""
#param nestedList: a list of NestedInteger
#return: the sum
"""
def depthSumInverse(self, nestedList):
# Write your code here.
d = collections.defaultdict(int)
self.dfs(nestedList, 1, d)
n = len(d)
sum = 0
for i in range(1, n + 1):
sum += d[i] * (n - i + 1)
return sum
def dfs(self, nestedList, depth, d):
ans = 0
for e in nestedList:
if e.isInteger():
ans += e.getInteger()
else:
self.dfs(e.getList(), depth + 1)
d[depth] += ans
I attempted to put keyword nonlocal to let the dfs method have the assess to the outer variable d. However, this modification doesn't work either:
class Solution:
"""
#param nestedList: a list of NestedInteger
#return: the sum
"""
def depthSumInverse(self, nestedList):
# Write your code here.
d = collections.defaultdict(int)
self.dfs(nestedList, 1, d)
n = len(d)
sum = 0
for i in range(1, n + 1):
sum += d[i] * (n - i + 1)
return sum
def dfs(self, nestedList, depth, d):
nonlocal d
ans = 0
for e in nestedList:
if e.isInteger():
ans += e.getInteger()
else:
self.dfs(e.getList(), depth + 1)
d[depth] += ans
Finally, I resolved the problem with a self dictionary variable:
class Solution:
"""
#param nestedList: a list of NestedInteger
#return: the sum
"""
def depthSumInverse(self, nestedList):
# Write your code here.
self.d = collections.defaultdict(int)
self.dfs(nestedList, 1)
n = len(self.d)
sum = 0
for i in range(1, n + 1):
sum += self.d[i] * (n - i + 1)
return sum
def dfs(self, nestedList, depth):
ans = 0
for e in nestedList:
if e.isInteger():
ans += e.getInteger()
else:
self.dfs(e.getList(), depth + 1)
self.d[depth] += ans
I am wondering about the reason why the first two methods do not work. Thanks.
I might be very wrong , but it looks like your error is just a typo.
In your first code example, you do this:
self.dfs(e.getList(), depth + 1)
However. your function dfs() takes 3 arguments(I don't count self here). You should call it like this:
self.dfs(e.getList(), depth + 1,d)
You second code example doesn't work for the same reason.

Changing a generator with another function - Python 3.x

I want to change the behavior of the generator below so that it only yields even numbers. How can I do this?
I'm aware that there simpler, clever ways to do this. This is a contrived HR challenge, where the
The change_generator function that I wrote does not yield the desired output. I can only change change_generator.
I cannot change positive_integers_generator() nor the for loop below.
Can I solve this with a decorator?
#can't change the body of this function
def positive_integers_generator():
n = 1
while True:
x = yield n
if x is not None:
n = x
else:
n += 1
# can only change this function
def change_generator(generator, n):
for i in generator:
if i%2 == 0:
yield(i)
# can't change this code either
# should print 1, 2, 4, 6, 8
g = positive_integers_generator()
for _ in range(5):
n = next(g)
print(n)
change_generator(g, n)
You can use the built in function filter
even_numbers_generator = filter(lambda n: n % 2 == 0, positive_integers_generator())
Or a generator expression.
even_numbers_generator = (n for n in positive_integers_generator() if n % 2 == 0)
Or itertools.count from the standard library:
even_numbers_generator = itertools.count(start=2, step=2)
But if you only can change the change_generator function, the "correct answer" to the challenge probably involves using generator.send()
# can only change this function
def change_generator(generator, n):
if n % 2 == 0:
generator.send(n + 1)
You don't need the parens on generator in your loop, and you don't seem to be printing the output of the right generator. Updated version that works for me:
def positive_integers_generator():
n = 1
while True:
x = yield n
if x is not None:
n = x
else:
n += 1
def change_generator(generator):
for i in generator:
if i%2 == 0:
yield i
g = positive_integers_generator()
# should print 1, 2, 4
for _ in range(5):
n = next(change_generator(g))
print(n)
In your very specific problem, if you can't change the print(n) part then you are pretty cornered because you can't change the instance of generator g that was created for positive_integers_generator().
In what may be a frowned upon answer, in this particular case you might want to update the global g to be reassigned to a new generator after that:
def change_generator(generator, n):
def even_gen():
n = 2
while True:
if n % 2 == 0:
yield n
else:
yield
n += 1
global g # directly change the g referenced in main code
if not g.__name__ == 'even_gen': # change g if it is not even_gen
g = even_gen()
# output:
# 1
# 2
# None
# 4
# None

How to count the number of times a value recurs in a recursive function?

If you have a recursive function (e.g. the Fibonacci sequence):
def fib(n):
"""Return Fibonacci of n; assumes n is an int >= 0."""
if n == 0 or n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
How would one count, for example, the number of times fib(2) occurs when fib(20) is called?
You can use a decorator:
import functools
def count(f):
"""Count function calls."""
#functools.wraps(f)
def counted(*args, **kwargs):
counted.count_calls += 1
return f(*args, **kwargs)
counted.count_calls = 0
return counted
fib = count(fib)
fib(5)
fib.count_calls
# 15
Alternatively, you can now prepend any function definition using this decorator and the # symbol:
#count
def fib(n):
...
fib(5)
fib.count_calls
# 15
Note, this decorator accumulates function calls:
fib(5)
fib(5)
fib.count_calls
# 30
This is a clever implementation that takes advantage of lesser known function attributes. Note, the original decorator is modified from John DiNero's count function discussed in his lecture on Memoization where he addresses this specific issue.
You can use a dictionary to count all invocations of fib. The dictionary has to be cleared before the first call to fib.
calls = defaultdict(int)
In the function, update the corresponding entry in the dictionary before doing anything else:
def fib(n):
global calls
"""Assumes n an int >= 0
Returns Fibonacci of n"""
calls[n] += 1
if n == 0 or n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
This is what i tried ... thinks like work fine
def fib(n):
global counter
if (n == 0 or n == 1):
counter=counter+1
return 1
else:
counter=counter+1
return fib(n-1) + fib(n-2)
def countfib(n):
global counter
counter = 0
fib(5);
global count
count=counter
counter = 0
return count
counter=0
count=0
print fib(5)
count=countfib(5)
print count
Output:
8
15
It's not clear to me exactly what the recurring values you want to count are, so I'm guessing it's the number of times the (recursive) function was called with the same argument (or group of them, if there's more than one).
In the code below, a decorator named tally_recurring_args() is used to wrap the function in some code to do this. To simplify things and avoid reinventing the wheel, a collections.Counter is used to tally the number of calls of every unique combination of arguments to the function. This is made a attribute of the function so it can be easily referenced in the wrapper every call to the decorated function.
import collections
import functools
def tally_recurring_args(func):
func._args_counter = collections.Counter() # add attribute to func
#functools.wraps(func)
def wrapper(*args):
key = ', '.join(str(arg) for arg in args)
func._args_counter[key] += 1
return func(*args)
return wrapper
#tally_recurring_args
def fib(n):
"""Return Fibonacci of n; assumes n is an int >= 0."""
if n == 0 or n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
print('fib(5) -> {}'.format(fib(5)))
for args, count in sorted(fib._args_counter.items()):
print('fib({}): called {} time{}'.format(args, count,
's' if count > 1 else ''))
Output:
fib(5) -> 8
fib(0): called 3 times
fib(1): called 5 times
fib(2): called 3 times
fib(3): called 2 times
fib(4): called 1 time
fib(5): called 1 time
How about:
def fib(n, counts_dict):
counts_dict[n] += 1
if n == 0 or n == 1:
return 1
else:
return fib(n-1, counts_dict) + fib(n-2), counts_dict
Where counts_dict = collections.defaultdict(int)
def fib(n, x):
c = 1 if n == x else 0
if n == 0 or n == 1:
return 1, c
else:
fA, cA = fib(n - 1, x)
fB, cB = fib(n - 2, x)
return fA + fB, cA + cB + c
If I've not messed up the logic, this function accepts n and x and returns a tuple (y, c) s.t. fib(n, _)=y and fib(x, _) was invoked c times during the calculation.
This is a purer alternative to the other proposed solutions that involve updating a dictionary. It rests on the assumption that OP only requires the number of times f(k, _) was invoked for one specific k, and so avoids filling a dictionary from which only one value is needed.
Without global variables:
from collections import defaultdict
def fib(n, count=None):
if count is None:
count = defaultdict(int)
count[n] += 1
if n in (0, 1):
return 1, count
return fib(n - 1)[0] + fib(n - 2)[0], count
The fib function now returns a tuple: the first element is the desired value and the second element is a dictionary containing the information of how many times each value the fib function was called on.
How about using an function attribute. Just like a static variable.
def fib(n):
"""Return Fibonacci of n; assumes n is an int >= 0."""
If hasattr(fib, 'cnt'):
fib.cnt += 1
else:
fib.cnt = 1
if n == 0 or n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
fib.cnt =0
Using global:
c = 0
def fib(n):
global c
c += 1
if n == 1:
return 0
elif n == 2:
return 1
else:
return fib(n-1) + fib(n-2)
This is another solution without global variable, but done through value passing:
def fibo(n, c):
if n <= 2 and n > 0:
return n-1, c
else:
n1, c1 = fibo(n-1,c+1)
n2, c2 = fibo(n-2,1)
return n1+n2, c1+c2

Python Pure recursion - Divisor - One input

What is the recursive call (or inductive steps) for a function that returns the number of integers from 1 to N, which evenly divide N. The idea is to concieve a pure recursive code in python for this function. No 'for' or 'while' loops, neither modules can be used. The function num_of_divisors(42) returns 8, representing 1, 2, 3, 6, 7, 14, 21, and 42 as divisors of 42.
def num_of_divisors(n):
return sum(1 if n % i==0 else 0 for i in range(((n+1)**0.5)//1)
Good luck explaining it to your teacher!
If you really can't use for loops (?????????) then this is impossible without simulating one.
def stupid_num_of_divisors_assigned_by_shortsighted_teacher(n, loop_num=1):
"""I had to copy this from Stack Overflow because it's such an
inane restriction it's actually harmful to learning the language
"""
if loop_num <= (n+1) ** 0.5:
if n % loop_num == 0:
return 2 + \
stupid_num_of_divisors_assigned_by_shortsighted_teacher(n, loop_num+1)
else:
return stupid_num_of_divisors_assigned_by_shortsighted_teacher(n, loop_num+1)
else:
if n % loop_num == 0:
return 1
Bonus points: explain why you're adding 2 in the first conditional, but only 1 in the second conditional!
Here you go buddy your teacher'll be happy.
def _num_of_divisors(n, k):
if (k == 0):
return 0
return _num_of_divisors(n, k-1) + (n % k == 0)
def num_of_divisors(n):
return _num_of_divisors(n, n)
It's easier than you think to convert such a simple problem from a loop to a recursive function.
Start with a loop implementation:
n = 42
result = []
for i in range(n+1):
if n % i == 0:
result.append(i)
then write a function
def num_of_divisors_helper(i, n, result):
if <condition when a number should be added to result>:
result.append(n)
# Termination condition
if <when should it stop>:
return
# Recursion
num_of_divisors_helper(i+1, n, result)
Then you define a wrapper function num_of_divisors that calls num_of_divisors_helper. You should be able to fill the gaps in the recursive function and write the wrapper function yourself.
It's a simple, inefficient solution, but it matches your terms.
Without using %
def is_divisible(n, i, k):
if k > n:
return False
if n - i*k == 0:
return True
else:
return is_divisible(n, i, k+1)
def num_of_divisors(n, i=1):
if i > n/2:
return 1
if is_divisible(n, i, 1):
return 1 + num_of_divisors(n, i+1)
else:
return num_of_divisors(n, i+1)
num_of_divisors(42) -> 8
def n_divisors(n,t=1):
return (not n%t)+(n_divisors(n,t+1) if t < n else 0)
good luck on the test later ... better hit those books for real, go to class and take notes...
with just one input i guess
t=0
def n_divisors(n):
global t
t += 1
return (not n%t)+(n_divisors(n) if t < n else 0)

Categories