Python: base case of a recursive function - python

Currently I'm experimenting a little bit with recursive functions in Python. I've read some things on the internet about them and I also built some simple functioning recursive functions myself. Although, I'm still not sure how to use the base case.
I know that a well-designed recursive function satisfies the following rules:
There is a base case.
The recursive steps work towards the base case.
The solutions of the subproblems provide a solution for the original problem.
Now I want to come down to the question that I have: Is it allowed to make up a base case from multiple statements?
In other words is the base case of the following self written script, valid?
def checkstring(n, string):
if len(string) == 1:
if string == n:
return 1
else:
return 0
if string[-1:] == n:
return 1 + checkstring(n, string[0:len(string) - 1])
else:
return checkstring(n, string[0:len(string) - 1])
print(checkstring('l', 'hello'))

Yes, of course it is: the only requirement on the base case is that it does not call the function recursively. Apart from that it can do anything it wants.

That is absolutely fine and valid function. Just remember that for any scenario that you can call a recursion function from, there should be a base case reachable by recursion flow.
For example, take a look at the following (stupid) recursive function:
def f(n):
if n == 0:
return True
return f(n - 2)
This function will never reach its base case (n == 0) if it was called for odd number, like 5. You want to avoid scenarios like that and think about all possible base cases the function can get to (in the example above, that would be 0 and 1). So you would do something like
def f(n):
if n == 0:
return True
if n == 1:
return False
if n < 0:
return f(-n)
return f(n - 2)
Now, that is correct function (with several ifs that checks if number is even).
Also note that your function will be quite slow. The reason for it is that Python string slices are slow and work for O(n) where n is length of sliced string. Thus, it is recommended to try non-recursive solution so that you can not re-slice string each time.
Also note that sometimes the function do not have strictly base case. For example, consider following brute-force function that prints all existing combinations of 4 digits:
def brute_force(a, current_digit):
if current_digit == 4:
# This means that we already chosen all 4 digits and
# we can just print the result
print a
else:
# Try to put each digit on the current_digit place and launch
# recursively
for i in range(10):
a[current_digit] = i
brute_force(a, current_digit + 1)
a = [0] * 4
brute_force(a, 0)
Here, because function does not return anything but just considers different options, we do not have a base case.

In simple terms Yes, as long as it does not require the need to call the function recursively to arrive at the base case. Everything else is allowed.

Related

Counting zeroes using Recursion and if-else in python

def countz(n):
if n<10:
if n==0:
return 1
else:
return 0
small=countz(n//10)
if n%10==0:
return small+1
else:
return small
from sys import setrecursionlimit
setrecursionlimit(11000)
n = int(input())
print(countz(n))
Someone helped me write this code, I did not understand why he used the condition n<10 in the base case of the recursion. I though the code would work without that condition but it didn't. Can anyone help me understand why the code is not working without that condition/ what is the real purpose or reason for that condition?
The base case that we want here is indeed n<10, or in words, n is a single digit number.
You might be tempted to choose n==0 as the base case and simply return 1 because it has one zero:
def countz(n):
if n==0:
return 1
small=countz(n//10)
if n%10==0:
return small+1
else:
return small
But that wouldn't work! Why? Consider the input 9, which has no zeroes. This isn't the base case, so we enter the recursive call: small=countz(n//10). n//10 gives 0, so we call countz(0) which returns 1. This is then returned as the answer, even though it should be 0.
The underlying problem is that, by convention, we denote the number zero with one more digit than is actually needed. Ideally it would consist of no digits at all, but that would be a bit impractical in daily life!

When to use while loops vs recursion

I am a beginner and I am beginning to learn about 'while' statement loops to perform iteration. However, earlier on I learned about 'if/else' statements and how one can perform recursion using if/else by returning a variable as well. Here's a simple countdown function using while statement:
def countdown(n):
while n > 0:
print(n)
n = n-1
print('Blast off!')
And for comparison, here is a simple countdown function using if/else and recursion.
def countdown(n):
if n > 0:
print(n)
return countdown(n-1)
elif n < 0:
return None
else:
print('Blast off!')
return
As you can see, the two functions do almost exactly the same thing, with the only difference being that the if/else statement accounts for a case where n < 0 and returns a None value, while the 'while' statement simply omits the loop and prints 'Blast off!' even if n < 0 anyway. (If there is a way to factor this in the while statement, I would love to learn, do suggest!)
My question is, seeing how the same thing can be done in if/else statements and while statements and vice versa, I would like to know a case where they are clearly differentiated and one is clearly preferable to the other. Is there a subtle conceptual difference between the two types of statements that I am missing, or are they interchangeable in usage?
They are fundamentally different. The way you have described them, both are the same thing as for loops. (I don't mean to be rude) While is used when you want something to happen as long as or until something else happens. Recursion is used for functions that are based on themselves. (common examples being factorial or the Fibonacci sequence) Often, they behave similarly in the manner you described on small scale problems. When scaled up however, both have their advantages and drawbacks.
TLDR: the functions are inherently different in how they iterate. While they each iterate, they do so based on different conditions and with different use cases.
You are actually right, the logic behind is the same and you can also apply while loop for e.g. Fibonacci sequence. Just the notation of recursion is usually shorter and more elegant.
Your code can be even simplified a bit and then you see that your solutions are kind of similar:
While loop:
def countdown(n):
while n > 0: # Step 1 -> check
print(n)
n = n-1 # Step 2 -> recursive call in while loop
print('Blast off!') # Step 3 -> end ensured by Step 1
Recursion:
def countdown(n):
if n > 0: # Step 1 -> check
print(n)
return countdown(n-1) # Step 2 -> recursive call of the countdown()
else:
print('Blast off!') # Step 3 -> end ensured by Step 1
Significant is that action n-1. You recursively act on a variable or object (Step 2) as long as your condition related to that variable/object is valid (Step 1).

Are both of these proper use of recursion? Is one better than the other?

I am trying to better understand recursion.
As an example, here are two definitions for calculating n! :
def factorial(num, p=1):
if num == 0:
return p
else:
p *= num
return factorial(num-1,p)
and
def factorial(num):
if num == 1:
return num
return num * factorial(num - 1)
Is one better than the other? I understand that the second one builds a call stack, while the first is calculating p at each step and just passing it to another iteration of the function. Are there separate names for these types of functions? Is the first one not recursion?
Just looking for some guidance on understanding the differences here. Thanks!
There is little difference between the two. I would say "simpler is better" and the 2nd one involves fewer variables so I would go for that one.
Or something even simpler:
def factorial(N): return max(1,N) if N<3 else N*factorial(N-1)
In real life though, you shouldn't implement it recursively because iterative solutions will be faster.

Finding multiples using recursion

Given 1 to 100 numbers, for multiples of 3 it should print "he" ,for multiples of 5 it should print "llo" ,for both multiples of 3 and 5 it should print "hello".
This is what I have:
for i in range (1,100):
if(i%3==0):
print("he")
elif(i%5==0):
print("llo")
elif(i%3==0 and i%5==0):
print("hello")
How would I do this recursively?
How about the code below?
def find_multiples(current, last_num=100):
# Base Case
if current > last_num:
return
result = ""
if current % 3 == 0:
result += "he"
if current % 5 == 0:
result += "llo"
if result:
print(f"{current}: {result}")
find_multiples(current+1, last_num)
find_multiples(1)
Base case is if current reaches last_num or the maximum number you'd like to check.
Here is a general outline for doing simple recursive things in python:
BASE_CASE = 1 #TODO
def f(current_case):
if current_case == BASE_CASE:
return #TODO: program logic here
new_case = current_case - 2 #TODO: program logic here ("decrement" the current_case somehow)
#TODO: even more program logic here
return f(new_case) + 1 #TODO: program logic here
Of course, this doesn't handle all possible recursive programs. However, it fits your case, and many others. You would call f(100), 100 would be current_value, you check to see if you've gotten to the bottom yet, and if so, return the appropriate value up the call stack. If not, you create a new case, which, in your case, is the "decrement" logic normally handled by the "loop" construct. You then do things for the current case, and then call the function again on the new case. This repeated function calling is what makes it "recursive". If you don't have an "if then" at the beginning of the function to handle the base case, and somewhere in the function recall the function on a "smaller" value, you're probably going to have a bad time with recursion.
This recursive function prints multiples of a number! hope it helps
def multi(n,x):
if x == 12:
print(n*x)
else :
print(n*x,end =",")
multi(n,x+1)
print(multi(4,1));

Python Fibonacci sequence error

this was one of the problems I was assigned in MyProgrammingLab. I've attempted to answer this problem over 45 times, but can't get it right.
Any help will be appreciated
Question:
In the following sequence, each number (except the first two) is the sum of the previous two numbers: 0, 1, 1, 2, 3, 5, 8, 13, .... This sequence is known as the Fibonacci sequence.
We speak of the i'th element of the sequence (starting at 0)-- thus the 0th element is 0, the 1st element is 1, the 2nd element is 1, the 3rd element is 2 and so on. Given the positive integer n, associate the nth value of the fibonacci sequence with the variable result. For example, if n is associated with the value 8 then result would be associated with 21.
My work:
def fib(n):
if n <= 1:
result == n
elif n >= 1:
result = fib(n-1)+fib(n-2)
else:
return result
It's because in all of your cases, you assign the result but don't return it.
So, for example, when fib(1) is called, Python returns None because you never told it to return result in that case. The same thing happens for, say, fib(45).
To correct this, just return result always. (This is a good idea no matter what type of program you are writing - functions should always have an explicit return value).
def fib(n):
if n <= 1:
result = n
elif n > 1:
result = fib(n-1)+fib(n-2)
return result # always return result!
Things to Know
You should be aware that this implementation of the Fibonacci sequence is the least efficient one out there. If you can ditch the recursive calls altogether and just use a while loop to calculate fib(n) - or, if you want recursion, store previously computed values of fib(n) instead of forcing it to compute all the way to fib(n) - you will have a much more efficient implementation.
Your code contained numerous issues, such as
Assigning without returning, which we've already discussed.
Using == instead of =. The first checks if the left and right hand side are equal, and returns True or False. The second actually assigns the value of the right hand side to the variable on the left hand side. Don't confuse checking for equality with assignment.
Using the same base case twice but telling Python to do something different in both cases. This is such a bad idea that I feel jonrsharpe in the comments is justified in saying "Seriously?". The reason for this is because doing this makes no sense and makes it hard to predict behaviour. The whole point of an if-else statement is to do different things in different cases.
Edit based on examples provided by OP. Indentation should only be four spaces, not eight. This is more of a stylistic issue than anything else, but it is the standard.
def fib(n):
if n < 2:
return n
else:
return fib(n-1)+fib(n-2)
You can essentially reduce it to this. You could even leave out the else and say:
def fib(n):
if n < 2:
return n
return fib(n-1)+fib(n-2)
but you said you need to have an else-case for whatever reason.
I wrote this one for my assignment. I know it's a little indirect, but it works and that's what's important :)
n = int(input("Insert a number: "))
i = 0
fib_list = [1, 1, 0]
for i in range (0,2):
if n == 0:
result = fib_list[2]
elif n <= 2:
result = fib_list[0]
for i in range (2,n):
result = fib_list[0] + fib_list[1]
fib_list.insert(0, result)
i += 1
result = fib_list[0]
By the way, you don't need to define an input to use in the myprogramminglab question.
I added the input version here because I used it in my tests.

Categories

Resources