Recursive behavior - python

Why does the following executes such that the print statement is called as often as it recursed but the count variable, count, when x == 1 is never reached.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
count_bits(int(n/2), count)
print("counter")
return count
why is it necessary to recurse with the return statement? Because if the recursive call is above the return statement the code
returns the wrong output but with the recursive call called with
return keyword, everything works well. Typically, the print statement
prints 'counter' as often as it recursed showing that the recursive call
works.
On the other hand, if "return" follows after the recursive call, it returns the count from the base condition, correctly.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1
return count_bits(int(n/2), count)

You have to return recursion result, as reccurent function meaning is to count current step you have to get result of previous step
F_k = F_k-1 * a + b # simple example
Means that you have to get result of F_k-1 from F_k and count current result using it.

I advised you to use snoop package to debug your code more efficient , by this package you can track the executing step by step.
to install it run:
Pip install snoop
Import snoop
add snoop decorator to count_bits()
for about the package see this link
https://pypi.org/project/snoop/

the difference in the output between the two methods is because of the way in which python handles fundamental data types. Fundamental data types such as float, ints, strings etc are passed by value, whereas complex data types such as dict, list, tuple etc are passed by reference. changes made to fundamental data types within a function will therefore only be changed within the local scope of the function, however changes made to a complex data type will change the original object. See below for an example:
x = 5
def test_sum(i : int):
print(i)
i += 5
print(i)
# the integer value in the global scope is not changed, changes in test_sum() are only applied within the function scope
test_sum(x)
print(x)
y = [1,2,3,4]
def test_append(l : list):
print(l)
l.append(10)
print(l)
# the list in the global scope has '10' appended after being passed to test_append by reference
test_append(y)
print(y)
This happens because it's far cheaper computationally to pass a reference to a large object in memory than to copy it into the local scope of the function. For more information on the difference, there are thousands of resources available by searching "what is the difference between by reference and by value in programming".
As for your code, it seems to me the only difference is you should alter your first snippet as follows:
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
# assign the returned count to the count variable in this function scope
count = count_bits(int(n/2), count)
print("counter")
return count
The second code snippet you wrote is almost identical, it just doesn't assign the new value to the 'count' variable. Does this answer your question?

Related

Error: variable not storing modified value on recursion

I am finding count of all the ways a target is reached. In base case, i am updating the value but when returning, it is taking the initial value only. How to change it to updated value, Kindly help me making changes in this code only and let me know how can i store so that it can return the modified value.
Input list:[1,2,3]
target:3
Output: 2 as [1,2] and [3] will make it 3
def counter(ind,grid,target,count):
if target==0: #if target becomes 0(achieved)
count+=1
return count
if ind==0: #if ind=0 is reached and target=value at that index(achieved)
if target==grid[ind]:
count+=1
return count
else:
return
nottake=counter(ind-1,grid,target,count) #not taking the index's value
take=0
if target-grid[ind]>=0: #only if value at index is smaller that target
take=counter(ind-1,grid,target-grid[ind],count) #taking the index's value
return count
grid=[1,2,3]
target=3
ind=len(grid)-1
print(counter(ind,grid,target,0)) #output should be 2 but i am getting 0
For starters, please format your code with Black. It's difficult to understand code that's scrunched together. Also, use default values to avoid burdening the caller with fussy indices and extra parameters.
This approach exhibits a common mistake with recursion: trying to pass the result downward through the recursive calls as well as upward. Just pass the result upward only, and pass parameters/state downward.
Doing so gives:
from functools import cache
#cache
def count_ways(available_numbers, target, i=0):
if target == 0:
return 1
elif target < 0:
return 0
elif i >= len(available_numbers):
return 0
take = count_ways(available_numbers, target - available_numbers[i], i + 1)
dont_take = count_ways(available_numbers, target, i + 1)
return take + dont_take
if __name__ == "__main__":
print(count_ways(available_numbers=(1, 2, 2, 1, 3, 4) * 70, target=7))
This is clearly exponential since each recursive call spawns 2 child calls. But adding a cache (formerly lru_cache(maxsize=None) prior to CPython 3.9) avoids repeated calls, giving a linear time complexity as long as the list fits within the stack size. Use a bottom-up dynamic programming approach if it doesn't

why does the counter variable reset to old value within the same function?

I am trying to get the count of all items in the nested lists. while the code traverse through all items, the counter gets set to only non-nested list items. what is happenning and how to I correct this code?
def countarr(arr,count):
if len(arr) == 0:
return count
else:
if type(arr[0]) != list:
count += 1
print(arr[0],count)
else:
print ("inner")
countarr(arr[0],count)
return countarr(arr[1:],count)
count=0
arr=[1,2,[[4,5]],8,[7,4,5,6],12,34]
print("Answer: ",countarr(arr, count))
count is a variable of type int. When passing an int as parameter to a function in python, only the value is passed, not the actual variable. So, when the recursive call "modifies count", it actually modifies a copy of the variable, not the original. So, the original is not updated.
Furthermore, when you do the recursive call: countarr(arr[0]), you're not using the return value of that call. You can simply add count = in front of the recursive call, to retrieve the returned value of the recursive call:
def countarr0(arr,count):
if len(arr) == 0:
return count
else:
if not isinstance(arr[0], list):
count += 1
else:
count = countarr0(arr[0], count)
return countarr0(arr[1:], count)
arr=[1,2,[[4,5]],8,[7,4,5,6],12,34]
print("Answer: ",countarr0(arr, 0))
# Answer: 11
However, passing an argument count to the recursive call is not really useful. The recursive call doesn't need to know the current value of count. The recursive call should just count how many elements are in the nested sublist, and we'll add its return value to our current count:
def countarr1(arr):
if len(arr) == 0:
return 0
else:
count = 0
if isinstance(arr[0], list):
count += countarr1(arr[0])
else:
count += 1
count += countarr1(arr[1:])
return count
arr=[1,2,[[4,5]],8,[7,4,5,6],12,34]
print("Answer: ",countarr1(arr))
# Answer: 11
Note that using recursion to navigate the sublists is a good idea; but using recursion to iterate on the list is a bad idea in python. In python, recursion is much slower than for-loops; in addition, when you write arr[1:], a whole new copy of the array is made! That's terribly inefficient. Instead, we can use a loop to iterate on the list:
def countarr2(arr):
count = 0
for x in arr:
if isinstance(x, list):
count += countarr2(x)
else:
count += 1
return count
arr=[1,2,[[4,5]],8,[7,4,5,6],12,34]
print("Answer: ",countarr2(arr))
# Answer: 11
Variables don't magically reset. They only are set to the value you give it. Modifying the value of a local variable does not affect the value of another variable in another function, even if it has the same name. Take the following example:
def foo(count):
print(count)
count += 1
print(count)
def bar()
count = 0
print(count)
foo(count)
print(count)
bar()
Recursion is the same. Each recursive function call has its own copies of local variables just in the same way foo() and bar() each have a variable named count that are independent of each other.
To fix the recursive function, just return the value of count from every possible if...else branch. You already do this in two of the branches, but you only print the value in two other branches and don't return it.

Why does my code only work when running the first function but the second function gives me a referenced before assignment variable?

I've tried changing the name of the variable thinkig that was the issue but that didn't fix it. I'm trying to calculate the additive persistence and multiplicative persistence and the additive and multiplicative roots of a number entered into the get_numbers function. For the additive_calculator and multiplicative_calculator functions whichever one I call first works, but the second one gives me an error at the print statement saying that the value of of the root, which I called total and total2 in this case, gives me a referenced before assignment error. I have no idea what to do to fix this error.`enter code here
from functools import reduce
def get_numbers():
num = (int(input("Please enter an integer(negative integer to quit):")))
nums = [int(a) for a in str(num)]
return(nums)
nums = get_numbers()
print(nums)
def additive_calculator(nums):
print("Additive loop")
counter = 0
while len(nums) > 1:
for num in nums:
len(nums)
total = 0
total = sum(nums)
list.clear(nums)
nums = [int(a) for a in str(total)]
print("sum:", total)
print(len(nums))
counter = counter + 1
print("Additive persistence", counter,",", "Additive Root:", total)
print("DONE")
def multiplicative_calculator(nums):
print("multiplicative loop")
counter = 0
while len(nums) > 1:
for num in nums:
len(nums)
total2 = 0
total2 = reduce(lambda x, y: x*y,(nums))
list.clear(nums)
nums = [int(a) for a in str(total2)]
print("sum:", total2)
print(len(nums))
counter = counter + 1
print("multiplicative persistence", counter,",", "multiplicative Root:", total2)
print("DONE")
multiplicative_calculator(nums)
additive_calculator(nums)
If you assign to a variable anywhere in a function, that variable is considered a local variable (unless declared global or nonlocal). It doesn't matter if you actually do the assignment when the function runs; it suffices that there is some statement in the function that assigns to it.
For example, in the function below, you assign to the variable a only if the argument b is true, but a is still a local variable if you don't assign to it (when b is false).
def f(b):
if b:
a = "foo"
return a
f(True) # -> "foo"
f(False) # error
It's the same with loops. If you assign to a variable in the body of a loop, or the variable in a for loop, that variable is a local variable even if you don't execute the body of the loop. If you execute the body, the variable is assigned to, and it is safe to read it, but if you do not execute the body, the variable isn't initialised, and it is an error to read it.
def f(x):
for a in x:
pass
return a
f([1, 2, 3]) # -> 3
f([]) # error
In your code, if nums is empty, total2 is never assigned to. It is still a local variable because there is an assignment to it in the code--it doesn't matter that the statement is never executed--but it is an error to read the variable if you haven't assigned to it.
The fix is simple: make sure you initialise total2 regardless of whether you enter the loop body or not.
def multiplicative_calculator(nums):
print("multiplicative loop")
counter = 0
total2 = 0 # move initialisation of total2 here
while len(nums) > 1:
# same code as before...
# when you get here, total2 is initialised whether you
# entered the loop or not.
print("multiplicative persistence", counter,
",", "multiplicative Root:", total2)

Inconsistent return value Pylint

I have used pylint to check my code and I am receiving the following suggestion.
'Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements)'. The code block is below. I am passing in a value and need to return which value range it belongs to. If I move the return value outside of the if it will return the incorrect count value. Any suggestions?
def findRangeValue(ranges, number):
count = -1
n = 2
for x in range(len(ranges)-n+1):
count += 1
batch = range[x:x + n]
if batch[0] < number <= batch[1]:
return count
You need confirm the function always have a non-empty (not None) return value in most common situation.
Your code will return None when all if statement in the loop failed, need add a final return value outside loop.
example code:
def findRangeValue(ranges, number):
count = -1
n = 2
for x in range(len(ranges)-n+1):
count += 1
batch = ranges[x:x + n]
if batch[0] < number <= batch[1]:
return count
return -1
print(findRangeValue([1,3,5,7,9], 4))
print(findRangeValue([1,3,5,7,9], 10))
result:
1
-1
Your function is written in a way, that it returns a value only if a condition is met i.e .
if batch[0] < number <= batch[1]:
Assume a situation you are consuming the function something like
range_val = findRangeValue([1,2,3], 3)
If the condition was met the return value will be assigned to range_val. But if the condition was not met, then the function returns nothing and your range_val becomes None.
But your code expects it to be a number and not None. So, now you need to check the return value to avoid errors - an extra line of code.
Assume you are calling this function from various parts of your code, every where you need to bring in this check - more and more code.
To avoid this, a standard to follow is that, a function always returns what was promised. If it was not able to do so, raise an Exception so that, the caller is notified without an additional check.
In your particular scenario, you can use a break statement and always return a count outside (beware, if break was not met, the value could be not what you expect) Or use an additional flag variable with break, and raise an error if the flag variable was not set.

Variable Scope Issue in Python

I am new to Python and I have been working with it for a bit, but I am stuck on a problem. Here is my code:
def collatz(num,ctr):
if(num != 1):
ctr+=1
if(num%2==0):
collatz(num/2,ctr)
else:
collatz(num*3+1,ctr)
return ctr
test=collatz(9,0)
For any number I put in for num, let's say 9 for instance, and 0 for ctr, ctr always comes out as 1. Am I using the ctr variable wrong?
EDIT:
I am trying to print out how many times the function is recursed. So ctr would be a counter for each recursion.
The variable ctr in your example will always be 1 because of the order of the recursive call stack. As one value of ctr is returned, then the call stack will start returning the previous values of ctr. Basically, at the very last recursive call, the highest value of ctr will be returned. But since the method call at the bottom of the call stack returns the very last value aka the value that will be stored in test, test will always be 1. Let's say I input parameters into collatz that would result in five total calls of the method. The call stack would look like this coming down,
collatz returns ctr --> 5
collatz returns ctr --> 4
collatz returns ctr --> 3
collatz returns ctr --> 2
collatz returns ctr --> 1 //what matters because ctr is being returned with every method call
As you can see, no matter how many times collatz is called, 1 will always be returned because the call at the bottom of the call stack has ctr equaling 1.
The solution can be a lot of things, but it really depends on the purpose of what you're trying to accomplish which isn't clearly stated in your question.
EDIT: If you want ctr to end up being the number of times a recursive call is made, then just assign ctr to the value of the method call. It should look like this,
def collatz(num,ctr):
if(num != 1):
ctr+=1
if(num%2==0):
ctr = collatz(num/2,ctr)
else:
ttr = collatz(num*3+1,ctr)
return ctr
test=collatz(9,0)
I changed your recursive calls to set the value received back from the recursive calls into ctr. The way you wrote it, you were discarding the values you got back from recursing.
def collatz(num,ctr):
if(num != 1):
ctr+=1
if(num%2==0):
ctr=collatz(num/2,ctr)
else:
ctr=collatz(num*3+1,ctr)
return ctr
test=collatz(9,0)
An example:
def collatz(number):
if number % 2 == 0:
print(number // 2)
return number // 2
elif number % 2 == 1:
result = 3 * number + 1
print(result)
return result
n = input("Give me a number: ")
while n != 1:
n = collatz(int(n))
Function parameters in Python are passed by value, not by reference. If you pass a number to a function, the function receives a copy of that number. If the function modifies its parameter, that change will not be visible outside the function:
def foo(y):
y += 1
print("y=", y) # prints 11
x = 10
foo(x)
print("x=", x) # Still 10
In your case, the most direct fix is to make ctr into a global variable. Its very ugly because you need to reset the global back to 0 if you want to call the collatz function again but I'm showing this alternative just to show that your logic is correct except for the pass-by-reference bit. (Note that the collatz function doesn't return anything now, the answer is in the global variable).
ctr = 0
def collatz(num):
global ctr
if(num != 1):
ctr+=1
if(num%2==0):
collatz(num/2)
else:
collatz(num*3+1)
ctr = 0
collatz(9)
print(ctr)
Since Python doesn't have tail-call-optimization, your current recursive code will crash with a stack overflow if the collatz sequence is longer than 1000 steps (this is Pythons default stack limit). You can avoid this problem by using a loop instead of recursion. This also lets use get rid of that troublesome global variable. The final result is a bit more idiomatic Python, in my opinion:
def collats(num):
ctr = 0
while num != 1:
ctr += 1
if num % 2 == 0:
num = num/2
else:
num = 3*num + 1
return ctr
print(collatz(9))
If you want to stick with using recursive functions, its usually cleaner to avoid using mutable assignment like you are trying to do. Instead of functions being "subroutines" that modify state, make them into something closer to mathematical functions, which receive a value and return a result that depends only on the inputs. It can be much easier to reason about recursion if you do this. I will leave this as an exercise but the typical "skeleton" of a recursive function is to have an if statement that checks for the base case and the recursive cases:
def collatz(n):
if n == 1:
return 0
else if n % 2 == 0:
# tip: something involving collatz(n/2)
return #???
else:
# tip: something involving collatz(3*n+1)
return #???
The variable will return the final number of the collatz sequence starting from whatever number you put in. The collatz conjecture says this will always be 1

Categories

Resources