Double recursion to find smallest multiple using only certain digits - python

This is the problem statement my question stems from (from Hackerrank): You are given an integer N. Can you find the least positive integer X made up of only 9's and 0's, such that, X is a multiple of N? X is made up of one or more occurences of 9 and zero or more occurences of 0.
I thought I would use double recursion but I'm having trouble understanding how to make it work. My function takes in "multiple", which is a string (so that I can append either '0' or '9' to it in further function calls and not have to deal with arithmetic), and "divisor".
Basically I wanted to keep calling my function, adding either 9 or 0 to "multiple" at every call, returning the final "multiple" when it's finally divisible by "divisor". I was envisioning it as a tree of function calls splitting every time between function(multiple + '9', divisor) and function(multiple + '0', divisor).
However, it seems once I call return, it doesn't get to the second function call:
#multiple is a string
def rec_nine_zero(multiple, divisor):
if int(multiple) % divisor == 0:
return multiple
else:
return rec_nine_zero(multiple + '9', divisor)
return rec_nine_zero(multiple + '0', divisor)
The below works:
print(rec_nine_zero('9', 1111))
However, if I try this (where the desired multiple is 90):
print(rec_nine_zero('9', 5)
It crashes and tells me basically that the stack blew up, meaning it never got to the second function call.
One problem I can see is that even if I manage to get the return statement to call both functions (with multiple + '9' and multiple + '0'), I feel like all of the branches of the function call tree except one (the one that finally finds the right result) will keep going until the stack says "annnnd...we're done".
EDIT: Based on Prune's answer, here is my new function:
def rec_nine_zero(multiples, divisor):
for multiple in multiples:
if int(multiple) % divisor == 0:
return multiple
new_multiples = []
for multiple in multiples:
new_multiples.append(multiple + '0')
new_multiples.append(multiple + '9')
return rec_nine_zero(new_multiples, divisor)

It blows up because you've done this in the canonical fashion of depth-first search. The first branch you try in a string of 9's, as long as needed to find a solution. Since there isn't any such solution for N=5, you recur until you blow the stack.
Change over to breadth-first. Generate and test your smallest string, '9'. When that fails, recur on the list of strings you want to extend: ["9"].
In the recursion, you append '0' and '9' to each candidate in the list. On the first recursion, this gives you ["90", "99"]. With N=5, you'll return success on this stage. If you have some other number, such as 7, you'll recur with this new list.
On the next step, you'll test the list ["900", "909", "990", "999"], and continue this process until you succeed.
BTW, you can perhaps make this easier if you quit converting between string and int: just start with 9. The next stage will work on 10*x and 10*x+9 for each x in the previous list.
Does that get you moving?

Not the answer but heres another simpler way to do the question -
def rec_nine_zero(divisor):
d = divisor
while True:
if all([True if i in '09' else False for i in `d`]): return d
else: d+=divisor
print rec_nine_zero(111)
Anyways to answer your question -
There are some caveats in your code which might give you some tips about recursion.
First you have a return statement under another return statement in the same construct and because of this the second return statement is never run.
Second thing is you are missing a base case for recursion which is needed to stop the recursion from going on. What your code is doing right now is just appending 9 and 9 and 9 ...., your 1st case works because luckily, 111 has a multiple of 999. In other cases it fails miserably.
And unfortunately, you cannot construct a base case for this problem if you solve it this way. The solution that #Prune gave is the right one.
Hope it helps!
Yeah you got it right! Anyways, I like shortening the code so here you go with the canonical bfs-
def rec_nine_zero(multiples, divisor):
que = [];que.append('9')
while(len(que) != 0):
k = que.pop()
if int(k)%divisor == 0:return k
que.append(k+'9');que.append(k+'0')
return -1
print rec_nine_zero('9', 5)
PS - I'm sure it can be shortened more!

Related

Why does the range(first_num, second_num) not include second_num? [duplicate]

This question already has answers here:
Why are slice and range upper-bound exclusive?
(6 answers)
Closed last month.
>>> range(1,11)
gives you
[1,2,3,4,5,6,7,8,9,10]
Why not 1-11?
Did they just decide to do it like that at random or does it have some value I am not seeing?
Because it's more common to call range(0, 10) which returns [0,1,2,3,4,5,6,7,8,9] which contains 10 elements which equals len(range(0, 10)). Remember that programmers prefer 0-based indexing.
Also, consider the following common code snippet:
for i in range(len(li)):
pass
Could you see that if range() went up to exactly len(li) that this would be problematic? The programmer would need to explicitly subtract 1. This also follows the common trend of programmers preferring for(int i = 0; i < 10; i++) over for(int i = 0; i <= 9; i++).
If you are calling range with a start of 1 frequently, you might want to define your own function:
>>> def range1(start, end):
... return range(start, end+1)
...
>>> range1(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Although there are some useful algorithmic explanations here, I think it may help to add some simple 'real life' reasoning as to why it works this way, which I have found useful when introducing the subject to young newcomers:
With something like 'range(1,10)' confusion can arise from thinking that pair of parameters represents the "start and end".
It is actually start and "stop".
Now, if it were the "end" value then, yes, you might expect that number would be included as the final entry in the sequence. But it is not the "end".
Others mistakenly call that parameter "count" because if you only ever use 'range(n)' then it does, of course, iterate 'n' times. This logic breaks down when you add the start parameter.
So the key point is to remember its name: "stop".
That means it is the point at which, when reached, iteration will stop immediately. Not after that point.
So, while "start" does indeed represent the first value to be included, on reaching the "stop" value it 'breaks' rather than continuing to process 'that one as well' before stopping.
One analogy that I have used in explaining this to kids is that, ironically, it is better behaved than kids! It doesn't stop after it supposed to - it stops immediately without finishing what it was doing. (They get this ;) )
Another analogy - when you drive a car you don't pass a stop/yield/'give way' sign and end up with it sitting somewhere next to, or behind, your car. Technically you still haven't reached it when you do stop. It is not included in the 'things you passed on your journey'.
I hope some of that helps in explaining to Pythonitos/Pythonitas!
Exclusive ranges do have some benefits:
For one thing each item in range(0,n) is a valid index for lists of length n.
Also range(0,n) has a length of n, not n+1 which an inclusive range would.
It works well in combination with zero-based indexing and len(). For example, if you have 10 items in a list x, they are numbered 0-9. range(len(x)) gives you 0-9.
Of course, people will tell you it's more Pythonic to do for item in x or for index, item in enumerate(x) rather than for i in range(len(x)).
Slicing works that way too: foo[1:4] is items 1-3 of foo (keeping in mind that item 1 is actually the second item due to the zero-based indexing). For consistency, they should both work the same way.
I think of it as: "the first number you want, followed by the first number you don't want." If you want 1-10, the first number you don't want is 11, so it's range(1, 11).
If it becomes cumbersome in a particular application, it's easy enough to write a little helper function that adds 1 to the ending index and calls range().
It's also useful for splitting ranges; range(a,b) can be split into range(a, x) and range(x, b), whereas with inclusive range you would write either x-1 or x+1. While you rarely need to split ranges, you do tend to split lists quite often, which is one of the reasons slicing a list l[a:b] includes the a-th element but not the b-th. Then range having the same property makes it nicely consistent.
The length of the range is the top value minus the bottom value.
It's very similar to something like:
for (var i = 1; i < 11; i++) {
//i goes from 1 to 10 in here
}
in a C-style language.
Also like Ruby's range:
1...11 #this is a range from 1 to 10
However, Ruby recognises that many times you'll want to include the terminal value and offers the alternative syntax:
1..10 #this is also a range from 1 to 10
Consider the code
for i in range(10):
print "You'll see this 10 times", i
The idea is that you get a list of length y-x, which you can (as you see above) iterate over.
Read up on the python docs for range - they consider for-loop iteration the primary usecase.
Basically in python range(n) iterates n times, which is of exclusive nature that is why it does not give last value when it is being printed, we can create a function which gives
inclusive value it means it will also print last value mentioned in range.
def main():
for i in inclusive_range(25):
print(i, sep=" ")
def inclusive_range(*args):
numargs = len(args)
if numargs == 0:
raise TypeError("you need to write at least a value")
elif numargs == 1:
stop = args[0]
start = 0
step = 1
elif numargs == 2:
(start, stop) = args
step = 1
elif numargs == 3:
(start, stop, step) = args
else:
raise TypeError("Inclusive range was expected at most 3 arguments,got {}".format(numargs))
i = start
while i <= stop:
yield i
i += step
if __name__ == "__main__":
main()
The range(n) in python returns from 0 to n-1. Respectively, the range(1,n) from 1 to n-1.
So, if you want to omit the first value and get also the last value (n) you can do it very simply using the following code.
for i in range(1, n + 1):
print(i) #prints from 1 to n
It's just more convenient to reason about in many cases.
Basically, we could think of a range as an interval between start and end. If start <= end, the length of the interval between them is end - start. If len was actually defined as the length, you'd have:
len(range(start, end)) == start - end
However, we count the integers included in the range instead of measuring the length of the interval. To keep the above property true, we should include one of the endpoints and exclude the other.
Adding the step parameter is like introducing a unit of length. In that case, you'd expect
len(range(start, end, step)) == (start - end) / step
for length. To get the count, you just use integer division.
Two major uses of ranges in python. All things tend to fall in one or the other
integer. Use built-in: range(start, stop, step). To have stop included would mean that the end step would be assymetric for the general case. Consider range(0,5,3). If default behaviour would output 5 at the end, it would be broken.
floating pont. This is for numerical uses (where sometimes it happens to be integers too). Then use numpy.linspace.

Python: base case of a recursive function

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.

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.

Recursive Function in Python adding Odd Values in List

My task is to create a recursive function in Python that takes a list and a value of 0 as its inputs and then adds up all of the odd numbers on the list and returns that value. Below is the code that I have and it keeps returning that the list index is out of range. No matter what I do I can not get it to work.
def addodds2(x,y):
total=0
a=x[y]
while y<len(x):
if a%2!=0:
total+=a
return(addodds2(x,y+1))
else:
return(addodds2(x,y+1))
return(total)
print(addodds2([3,2,4,7,2,4,1,3,2],0))
Since you are trying to solve this recursively, I don't think you want that while loop.
When you are trying to solve a problem recursively, you need two parts: you need a part that does some of the work, and you need a part that handles reaching the end of the work. This is the "basis case".
Often when solving problems like this, if you have a zero-length list you hit the basis case immediately. What should be the result for a zero-length list? I'd say 0.
So, here's the basic outline of a function to add together all the numbers in a list:
Check the length, and if you are already at the end or after the end, return 0. Otherwise, return the current item added to a recursive call (with the index value incremented).
Get that working, and then modify it so it only adds the odd values.
P.S. This seems like homework, so I didn't want to just give you the code. It's easier to remember this stuff if you actually figure it out yourself. Good luck!
Your code should be (the comments explain my corrections):
def addodds2(x,y):
total=0
if y<len(x): #you don't need a while there
a=x[y] #you have to do this operation if y<len(x), otherwise you would get the index error you are getting
if a%2!=0:
total+=a
return total+addodds2(x,y+1) #you have to sum the current total to the result returned by the addodds2() function (otherwise you would got 0 as the final result)
return total
print(addodds2([3,2,4,7,2,4,1,3,2],0))
while y<len(x)
So the last y which is smaller than len(x) is y = len(x) - 1, so it’s the very last item of the list.
addodds2(x,y+1)
Then you try to access the element after that item, which does not exist, so you get the IndexError.
This code can be very short and elegant:
def add_odds(lst, i=0):
try:
return (lst[i] if lst[i] % 2 == 0 else 0) + add_odds(lst, i+1)
except IndexError:
return 0
Note that, in a truly functional style, you wouldn't keep track of an index either. In Python, it would be rather inefficient, though, but recursion isn't recommended in Python anyway.
def add_odds2(lst):
try:
return (lst[-1] if lst[-1] % 2 == 0 else 0) + add_odds2(lst[:-1])
except IndexError:
return 0
To make it work with any kind of sequence, you can do the following:
def add_odds3(it):
it = iter(it)
try:
value = next(it)
return (value if value % 2 == 0 else 0) + add_odds3(it)
except StopIteration:
return 0
It's much more efficient, though there's not much sense in using an iterator recursively...
I realize that little of this is relevant for your (educational) purposes, but I just wanted to show (all of) you some nice Python. :)

Using python 3 for recursive Boolean check if a list is sorted

I am trying to write a recursive function that does a Boolean check if a list is sorted. return true if list is sorted and false if not sorted. So far I am trying to understand if I have the 'base case' correct (ie, my first 'if' statement):
def isSorted(L, i=[]):
if L[i] > L[i + 1]:
return false
else:
return true
Am I correct with my initial if "L[i] > L[i + 1]:" as base case for recursion?
Assuming my 'base case' is correct I am not sure how to recursively determine if the list is sorted in non-descending order.
here is what I came up with. I designate the default list to 0; first check to see if first item is the last item. if not, should check each item until it reaches the end of the list.
def isSorted(L):
# Base case
if len(L) == 1:
return True
return L[0] <= L[1] and isSorted(L[1:])
This is how I would start. Write a function with the following signature:
function isSorted(currentIndex, collection)
Inside the function, check to see if currentIndex is at the end of the collection. If it is, return true.
Next, check to see if collection[index] and collection[index+1] are sorted correctly.
If they aren't, return false
If they are, return isSorted(currentIndex+1, collection)
Warning: this is a horrible use for recursion
No, the base case will be when you reach the end of the list, in which case you return true.
Otherwise, if the two elements you are looking at are out of order return false.
Otherwise, return the result of a recursive call on the next elements down the list.
I agree with #MStodd: recursion is not the way to solve this problem in Python. For a very long list, Python may overflow its stack! But for short lists it should be okay, and if your teacher gave you this problem, you need to do it this way.
Here is how you should think about this problem. Each recursive call you should do one of three things: 0) return False because you have found that the list is not sorted; 1) return True because you have reached your base case; 2) break the work down and make the remaining problem smaller somehow, until you reach your base case. The base case is the case where the work cannot be broken down any further.
Here is a broad outline:
def recursive_check(lst, i):
# check at the current position "i" in list
# if check at current position fails, return False
# update current position i
# if i is at the end of the string, and we cannot move it any more, we are done checking; return true
# else, if i is not at the end of the string yet, return the value returned by a recursive call to this function
For example, here is a function that checks to see if there is a character '#' in the string. It should return True if there is no # anywhere in the string.
def at_check(s, i):
if s[i] == '#':
return False
i += 1
if i >= len(s):
return True
else:
return at_check(s, i)
I wrote the above exactly like the outline I gave above. Here is a slightly shorter version that does the same things, but not in exactly the same order.
def at_check(s, i=0):
if i >= len(s):
return True
if s[i] == '#':
return False
return at_check(s, i+1)
EDIT: notice that I put i=0 in the arguments to at_check(). This means that the "default" value of i will be 0. The person calling this function can just call at_check(some_string) and not explicitly pass in a 0 for the first call; the default argument will provide that first 0 argument.
The only time we really need to add one to i is when we are recursively calling the function. The part where we add 1 is the important "breaking down the work" part. The part of the string we haven't checked yet is the part after i, and that part gets smaller with each call. I don't know if you have learned about "slicing" yet, but we could use "slicing" to actually make the string get smaller and smaller with each call. Here is a version that works that way; ignore it if you don't know slicing yet.
def at_check(s):
if s == '': # empty string
return True
if s[-1] == '#': # is last character '#'?
return False
return at_check(s[:-1]) # recursive call with string shortened by 1
In this version, an empty string is the basis case. An empty string does not contain #, so we return True. Then if the last character is # we can return False; but otherwise we chop off the last character and recursively call the function. Here, we break the work down by literally making the string get shorter and shorter until we are done. But adding 1 to the index variable, and moving the index through the string, would be the same thing.
Study these examples, until you get the idea of using recursion to break down the work and make some progress on each recursive call. Then see if you can figure out how to apply this idea to the problem of finding whether a list is sorted.
Good luck!

Categories

Resources