How does this while-loop compute? (x = function(x)) - python

I'm doing a quiz from "Automate the Boring Stuff with Python", and after tinkering with the problem for a bit, I finally found a solution that worked (with a little help from a comp-sci buddy of mine). The quiz asks me to make program that executes a Collatz-sequence.
I understand the logic behind all of the code, EXCEPT for the final line.
Here's my code with a few comments:
def collatz(number):
if number % 2 == 0:
print(number // 2)
return number // 2
elif number % 2 == 1:
print(3 * number + 1)
return 3 * number + 1
guess = input("Your guess please ")
while guess != 1:
guess = collatz(int(guess))
The output of the program is a sequence of numbers, as the while-loop somehow re-iterates over the returned value of the function, and uses that for another computation.
My problem is with the last line. Here's how I understand it:
Once I enter the while-loop, my function "collatz" is called, using my input-value
The function is run, and my input is computed, and based on the input, I get either the even or odd calculation in return
Here's where my brain hurts!
Is the line "guess = collatz(...)" now constantly updating "guess" to be equal to the retuned value from the function? If this is the case, then I completely understand the flow. If it's not the case, then I don't understand how the returned value is constantly being used for new calculations.
Also, is this what is called "recursion"?

Short answer:
Yes.
Longer answer: (still short)
The collatz function is returning a value which is assigned to guess.
Also, this is not called recursion, recursion is a function which calls itself.

First, no, this is not a recursion. Recursion is a function that calls itself.
For instance this is a recursion:
def fibonacci(n):
if n == 0:
return 0
if n == 1:
return 1
return fibonacci(n-1) + fibonacci(n-2)
As you can see here fibonacci function will call fibonacci function ... But it also has an exit condition (n == 0, and n == 1). Without that, this would cause runtime error with message that maximum recursion depth exceeded. But if I am not mistaken, you can check what is the maximum depth of recursion with following command:
import sys
print(sys.getrecursionlimit())
On my computer, this number is 1000. If this number is to small for you, you can also set it with this command:
sys.setrecursionlimit(n)
About other thing. Your function is returning some calculated value and in your main loop, this is assigned to variable guess. So everytime, that main loop will go through, value of guess will also update

Related

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));

Difficulty with Fizz Buzz

I'm a high highschool student taking an online course for python, and one of the assignments is to create a function similar to fizz buzz, except instead of "fizz" and "buzz", they simply use "three" and "five". I wrote a function but unfortunately it completely failed, and doesn't do its job at all. I've been having a hard time to figure out what to input instead of "for x in range(101):", I think that's where my problem is. Any pointers/guidance would be very helpful, I'm completely lost.
The instructions are here and here is my code:
def student_func(x):
for x in range(101):
if x % 3 == 0 and x % 5 == 0:
return('threefive')
elif x % 3 == 0:
return('three')
elif x % 5 == 0:
return('five')
else:
return x
Edit:A couple of people were recommending me to delete the loop, since it was starting off the function with 0(I should've realized that). However when I took it out the output became None. Am I doing something else wrong, or am I misunderstanding? Thanks for the speedy answers however, I'm still very inexperienced
Code with new error:
def student_func(x):
if x % 3 == 0 and x % 5 == 0:
return('threefive')
elif x % 3 == 0:
return('three')
elif x % 5 == 0:
return('five')
else:
print(x)
So, a few things to note. First, the range(n) function returns an iterator that will go through 0, 1, 2, ..., n-1. Thus, the first value you will iterate over in your code will be 0. Think about how your for loop will handle that.
Second, because you're assigning x to be the values iterated over from the range function, you're never actually testing the x being passed into the function.
Third, note that every branch of the if-elif-else tree inside the loop has a return statement, thus the loop is only going to go around once before returning a value. That sort of defeats the purpose of using a loop.
Lastly, make sure you are aware of how 0 % n evaluates. Think about things and see if you can reach a conclusion about why your program is returning student_func(1) = 'threefive'.
The issue is that the "tester" which tests your function gives the function the number/argument - x. However, what you are doing is testing the function from 0 - 100. The question does not ask you to test your function with numbers from 0 - 100. Therefore, you should remove the for loop.
Why is it giving the output "threefive"? Well, the function first tests in the loop the number 0. It satisfies the first if statement, and returns (so it gets out of the function).
Right now your code is looping through every value from 0 to 100 and returning if the first value (0) is divisible by both three and five, which of course it is. The function is only asking you to check if a single value is divisible by three, or five, or both. Why would you need a loop?
Your function isn't supposed to loop; it's supposed to examine only the single value passed in as the argument. The teacher's function will call your function with a variety of inputs.
Take out the for loop statement and it should work.

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.

Trying to find the 1000th prime number

I am trying to write a script in python to find the 1000th prime number. I don't understand why this isn't working here. Basically while the mod is less than the square root of the number and still has a remainder, the mod goes up by one. This should continue until the mod equals the square root of the number. Then the check should remain at 0 and the number should be prime. Every time I try to run the script it tells me theres a system error.
import math
b=2
count=2
next_odd=3
next_prime=1
check = 0
while count<=10:
while b<float(math.sqrt(next_odd)):
if next_odd%b>0:
b+=1
if next_odd%b == 0:
check+=1
if check > 0:
next_prime=next_odd
next_odd+=2
print(next_prime)
b=2
count+=1`
I understand what you are trying to do, but unfortunately there were too many things wrong with your program. Here is a working version. I made minimal changes. Hopefully you can compare the below version with your own and see where you went wrong.
import math
count=2
next_odd=3
next_prime=1
while count<=1000:
b=1
check = 0
while b<float(math.sqrt(next_odd)):
b+=1
if next_odd%b == 0:
check+=1
if check == 0:
next_prime=next_odd
print(next_prime)
count+=1
next_odd+=2
With the above program, 1000th prime can be successfully determined to be 7919.
(first, I assume the tick on the end of your code is a typo in your stack overflow post, not the code itself)
Consider what happens when next_odd is prime. This block:
while b<float(math.sqrt(next_odd)):
if next_odd%b>0:
b+=1
if next_odd%b == 0:
check+=1
will increment b up until the square root of next_odd without ever incrementing check. That means that if check > 0: won't pass, and thus count never increments, and you then you just spin around in the
while count<=10:, skipping both if blocks because their conditions are false.
In other words, you don't actually say what to do when next_odd is prime. This is also an example of why while shouldn't really be used when all you want to do is increment through numbers (which is what you're using it for here). Try something like this:
max_num = 10000 # or whatever
for odd in range(3, max_num, 2):
factor_count = 0
for factor in range(2, math.floor(math.sqrt(max_num)) + 1):
if odd % factor == 0:
factor_count += 1
if factor_count == 0:
print(odd)
A couple points about this code:
There's no (non-constant) variables in the global scope. That makes it much easier to reason about how the script's state changes over time.
The use of for-loops over while-loops guarantees that our script won't get caught in an infinite loop due to an erroneous (or unaccounted for) condition.
The use of for-loops means that we don't have to worry about incrementing all of the variables ourselves, which dramatically reduces the amount of state that we have to manage.
Hope that helps!
Oh, note that there are much more efficient ways to compute primes also. See https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

Code skips the loop even though the condition evaluates to true

I cannot get this portion of my program to enter the while loop. After plugging in values for poly, x_0, and epsilon, the program calculates a value for ans using the function evaluate_poly() which works correctly and in my specific instance gives the answer -13.2119. My epsilon value used is 0.0001. Since the abs(ans) is in fact greater than epsilon why is it skipping the loop?
I put a print ans statement right below the x_01 = 0 line to make sure it was calculating correctly before the while loop and also a print epsilon statement to make sure it was taking in my epsilon value correctly (which it does).
def compute_root(poly, x_0, epsilon):
"""uses newton's method to find a root of a polynomial function"""
ans = evaluate_poly(poly, x_0)
x_01 = 0
while abs(ans) > epsilon:
Dpoly = compute_deriv(poly)
Fprime = evaluate_poly(Dpoly, x_01)
return ans
x_01 = x_0 - (ans/Fprime)
print x_01
return x_01
print ans
You put two return statements inside your loop. When the first one is hit, the function exits instantly only. So, not only is the while loop being exited on the first loop, but the print statements are never being reached.
You have return ans inside your loop. So at that point the function will exit, and the print will never be reached.
If ans is really -13.2119 then it does enter the loop, it just doesn't perform everything in it because of the return.
Even if you remove the return ans line inside the loop, there is also return x_01 at its end. So in that case the loop will run at most once, which will make it the same as an if statement.

Categories

Resources