Can somebody help me break down and understand this collatz sequence code? - python

I'm a brand new programming graduate student and am having a bit of trouble. I'm working through a textbook problem that wants me to take a positive integer and print the Collatz sequence of that number.
while num !=1:
print(num)
if num%2==0:
num = num//2
else:
num = 3*num+1
print(1)
Now, this code works. And I get the mathematical logic. But there's parts I don't understand.
Firstly, the print(num), this is done so the code prints the input number since that starts the sequence. But, what is the logic/explanation to having this at the front of the loop?
Second, for num = num//2 and num = 3*num+1, why is that all you need for the code to run? In the end, our goal is to print the whole sequence until the input gets to 1. I thought you'd need an accumulator up top. i.e. lst = [] to append each value into that list and then return the list. Why does just re-labeling them num = work? Each time it iterates, wouldn't the prior value be deleted since it isn't stored anywhere?
Lastly, why do we not need to end the loop with a return? How does it know to print the whole sequence of numbers? It ends with print(1) because every input needs to end with that, and it terminates before 1. However, I have not called upon the function (it's a function on my end, not just a while loop) to take action. It just...did it.
I'm sorry if that's a bit confusing, but I'm trying to understand the correct code answer. I've mostly been doing accumulators and stuff, so I'm not sure why this didn't need one, what the num = did, and how you can just end it with print(1) and the whole sequence appears.

Firstly, the print(num), this is done so the code prints the input number since that starts the sequence. But, what is the logic/explanation to having this at the front of the loop?
It's placed where it is because it's not just there to print the input number. It's there to print whatever num happens to be at that point in the code, each time that point in the code is reached. On the first iteration, num is the first element of the sequence. On the second iteration, num is now the second element of the sequence. On the third iteration, num is the third element, and so on. This print prints every element of the sequence, except 1, because the loop breaks at that point, which is why there's a separate print(1) after the loop.
Second, for num = num//2 and num = 3*num+1, why is that all you need for the code to run? In the end, our goal is to print the whole sequence until the input gets to 1. I thought you'd need an accumulator up top. i.e. lst = [] to append each value into that list and then return the list. Why does just re-labeling them num = work? Each time it iterates, wouldn't the prior value be deleted since it isn't stored anywhere?
print(num) prints each sequence element as it's computed, so the program doesn't need to explicitly save the elements. The program does forget the previous values, but they were already printed. Remembering the printed output is some other tool's job - for example, if you run this with stdout directed to a file, the printed output is written into the file.
Lastly, why do we not need to end the loop with a return? How does it know to print the whole sequence of numbers? It ends with print(1) because every input needs to end with that, and it terminates before 1. However, I have not called upon the function (it's a function on my end, not just a while loop) to take action. It just...did it.
return has two jobs: it ends the current function execution, and it sets the return value. If execution reaches the end of a function without executing a return, execution of the function still ends, with a return value of None. return isn't involved in printing the output. That's print's job, as explained above.
As for not calling the function, we can't answer what's going on with that. We would need to see what you actually ran, including the actual function.

this is how the output "magically appears" without a list
see #1
you always return from a function ... in this case you are returning None, instead you are printing the list
it might help to put a time.sleep in there(inside your loop) to visualize whats happening
this site : python tutor - see how it works
is helpful for visualizing code execution

Related

Can't call a subroutine when within a subroutine, if and for loop. Have I done something wrong here?

So here's my code.
def bubblesort(list):
global step
global oldlist
print("""
ORIGINAL LIST""")
print(list)
for i in range(len(list)):
for j in range(len(list)-1):
if float(list[j])>float(list[j+1]):
list[j], list[j+1] = list[j+1], list[j]
if oldlist==list:
**end(list)**
else:
oldlist=list
step=step+1
print("""
STEP""",step)
print(list)
end(list)
def end(list):
global step
step=step+1
print("""
STEP""",step)
print(list)
step=0
oldlist=[]
list=[]
number=int(input("How many numbers do you want to input to sort? : "))
for i in range(1,number+1):
value=float(input("INPUT NUMBER : "))
list.append(value)
bubblesort(list)
The issue is the bit of code which I have is "end(list)" bare in mind I've included the ** to make it easier to see on here it's not actually in the code. It simply won't call the subroutine when I ask it to. I know it definitely runs through the if since if i put a "print" or "break" in they both work it just won't jump to a sub-routine. Furthermore, the time I call that subroutine later on in the code works and does go to the subroutine. So i'm a bit lost as to what I've done wrong here. Any help? Would be much appreciated :)
There are quite a few things wrong with your code, and your question. Let's go through them:
First of all, the formatting was not great (I've suggested an edit). This might not sound like a big deal ("Hey, as long as it works...") but it's actually crucial: I work with python every day, and I had trouble understanding your code
You're talking about subroutines. In python, we usually refer to those as functions, or methods.
You're using globals variables all over the place. This might work in little toy examples like this one, but it will fall apart VERY fast
As David Buck said, list is one of the basic data structures: 1 in an instance of int and [1,2,3] is an instance of list). It's a really bad idea to override it.
I'm not sure what you're trying to do with the end() function. It seems do very little, and what it does is not related to its name...
You create an oldlist list but never really do anything with it. What is it supposed to to ?
With that in mind, here is my proposition:
def main():
# I like to put the main code in a "main" function, so that it can be at the top
# of the file. We'll call main() from the bottom of the file
# Make our program pretty, with a little branding
print("===== Number Sorter 9000 =====")
# 'numbers' is not the name of anything yet, so we can use it safely
numbers = []
# This will loop forever, unless we tell it to stop. It allows us to skip the
# part where you ask the user for a number of values: Simply enter the values
# one by one, and press enter once last time when done.
while True:
value = input(f"Number {len(numbers)+1} (leave empty to continue): ")
# Decide what to do with the value we received. If it's a number,
# add it to our list, if it's empty, stop collecting numbers
if value:
# Make sure the value is a number ()
value = float(value)
# Add the number to the list
numbers.append(value)
else:
# This will get us out of the number-asking loop
break
print("Number Sorter 9000 will now sort your numbers")
sorted_numbers = bubblesort(numbers)
print("Number Sorter 9000 has sorted your numbers:")
print(sorted_numbers)
def bubblesort(numbers):
# Here I've removed things that were not doing much.
# There are some ways to improve the algorithm itself, but I'll let you do that
for i in range(len(numbers)):
for j in range(0, len(numbers)-i-1):
if numbers[j] > numbers[j+1]:
numbers[j], numbers[j+1] = numbers[j+1], numbers[j]
# We 'return' the result from the function, so that code that calls the
# bubblesort() function can use the result
return numbers
# Lastly, we call the "main" function to get everything started. Without this
# line, nothing happens: We've just explained to the computer how to do some
# stuff, but we haven't asked it to *DO* anything
main()
Well, I hope this was not too much information. I've tried to keep things simple, but feel free to ask if something is not clear

I am trying to write a function xyz(a) that loops through the no.s 0 through a and returns "Jackpot" if it finds the number 777 .or else "Try again

def xyz(a):
i=0
while i>=a:
if i==777:
return"Jackpot"
else:
return"Try again"
i+=1
The above when i am running gives me no output. unable to figure out what's missing
There are many errors!
If you return before increment, you'll never have an increment.
If you return after increment , no point of while loop.
Solution
Simple if else
def xyz(a):
if a==777:
return "Jackpot"
else:
# i+=1
# return "Try again"
return ("Try Again")
Since you did not provide any information about what exactly you are trying to do all I can do is give you some advise based on what I can infer from your code. Notice that you are returning a value meaning you stop the execution of the while loop at the first iteration always (if you are 100% certain about the implementation of the function and your desired output is 'try again' then you have to 'catch' the returned value of the function and print that). Also check the while condition. I is initialised at 0 so (for me at least) it would make sense to check if i is less than a (but again this comes from my intuition about the code).

Why does a function return the error "list index out of range" in Python?

This code was written in Python 3. I am trying to find the 10001st prime number.
#10001st prime number
mylist=[]
def prime_index(n):
for i in range(99**99):
for x in range(2, int(i**1/2)):
if i % x == 0:
return False
return True
mylist.append(i)
n=int(n+1)
print(mylist[n])
break
prime_index(10001)
When I run, it says "list index out of range", referring to print(mylist[n]).
However I have been adding primes to the list along the way in the mylist.append(i). So can someone tell me what is the problem here because I do not understand what is going on. Is 99**99 too small? Or more subtle problem with code?
99**99 is not too small; if you actually print it, you're well beyond what you need (if you tried to run it out, you'd never finish, it's 657 bits of work). But your loop makes no sense; your inner loop will return either True or False immediately if it executes even once.
"Luckily" for you, it never executes even once. The first outer loop sets i to 0 the first time, so the inner loop doesn't run at all (side-note, you probably want i ** (1/2), not i ** 1 / 2; exponentiation has higher precedence than division). Because it doesn't run, you end up indexing into an empty list (and asking for index 10001 no less).
There are far too many problems in this code to address them all; look for other trial division prime finding code to get an idea of what it should look like.
The problem is that you try to print out the 10001st element as soon as you find (or not) the first prime. Also, note that you return from the routine without finding any prime number -- if you were to get that far. There is no way to reach the append statement.
You got to the print statement only because your first iteration has i = 0, so you don't enter the for loop at all.
Please follow the posting guidelines: take the time to research how to generate prime numbers. It can be much faster than you're doing, and will give you a nice, succinct bit of code to put into your program.

how can I detect infinite loops in python

I am learning Python 3 and working on an exercise that calls for writing a Python program which simulates/reads a BASIC program as input. I am stuck on writing the part of the Python program that should detect infinite loops. Here is the code I have so far:
def execute(prog):
while True:
location = 0
if prog[location] == len(prog) - 1:
break
return "success"
getT = prog[location].split()
T = len(getT) - 1
location = findLine(prog, T)
visited = [False] * len(prog)
Here, prog is a list of strings containing the BASIC program (strings are in the form of 5 GOTO 30, 10 GOTO 20, etc.).
T is the target string indicated in prog[location].
If the BASIC program has an infinite loop, then my Python program will have an infinite loop. I know that if any line is visited twice, then it loops forever, and my program should return "infinite loop".
A hint given by the tutorial assistant says "initialize a list visited = [False] * len(prog) and change visited[i] to True when prog[i] is visited. Each time through the loop, one value updates in visited[]. Think about how you change a single value in a list. Then think about how you identify which value in visited[] needs to change."
So this is the part I am stuck on. How do I keep track of which strings in prog have been visited/looped through?
I'm not sure I agree that visiting a line twice proves an infinite loop. See the comments under the question. But I can answer the actual question.
Here's the hint:
A hint given by the tutorial assistant says "initialize a list visited = [False] * len(prog) and change visited[i] to True when prog[i] is visited. Each time through the loop, one value updates in visited[]. Think about how you change a single value in a list. Then think about how you identify which value in visited[] needs to change."
This is saying you should have two lists, one that contains the program, and one that contains true/false flags. The second one is to be named visited and initially contains False values.
The Python code is just like the hint says:
visited = [False] * len(prog)
This uses the * list operator, "list repetition", to repeat a length-1 list and make a new list of a longer length.
To change visited[i] to True is simple:
visited[i] = True
Then you can do something like this:
if visited[i]:
print("We have already visited line {}".format(i))
print("Infinite loop? Exiting.")
sys.exit(1)
Note that we are testing for the True value by simply saying if visited[i]:
We could also write if visited[i] == True: but the shorter form is sufficient and is customary in the Python community. This and other customary idioms are documented here: http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
For a program this small, it's not too bad to keep two lists like this. For larger and complex programs, I prefer to keep everything together in one place. This would use a "class" which you might not have learned yet. Something like this:
class ProgramCode(object):
def __init__(self, statement):
self.code = statement
self.visited = False
prog = []
with open(input_basic_program_file, "rt") as f:
for line in f:
prog.append(ProgramCode(line))
Now instead of two lists, we have a single list where each item is a bit of BASIC code and a visited flag.
P.S. The above shows an explicit for loop that repeatedly uses .append() to add to a list. An experienced Python developer would likely use a "list comprehension" instead, but I wanted to make this as easy to follow as possible.
Here's the list comprehension. Don't worry if it looks weird now; your class will teach this to you eventually.
with open(input_basic_program_file, "rt") as f:
prog = [ProgramCode(line) for line in f]
I know of no automatic way of infinite loop detection in Python, but by using divide and conquer methods and testing individual functions, you can find the offending function or block of code and then proceed to debug further.
If the Python program outputs data, but you never see that output, that's a good indicator you have an infinite loop. You can test all your functions in the repl, and the function that does "not come back" [to the command prompt] is a likely suspect.
You can write output under a debug variable of some sort, to be shut off when everything works. This could be a member variable of a Python class to which your code would have to have access to at any time, or you could have a module-scoped variable like Debug=1 and use debug levels to print varying amounts of debug info, like 1 a little, 2 more, 3, even more, and 4 verbose.
As an example, if you printed the value of a loop counter in a suspected function, then eventually that loop counter would keep printing well beyond the count of data (test records) you were using to test.
Here is a combination I came up with using parts of J. Carlos P.'s answer with the hints that steveha gave and using the hint that the instructions gave:
def execute(prog):
location = 0
visited = [False] * len(prog)
while True:
if location==len(prog)-1:
return "success"
findT = prog[location].split()
T = findT[- 1]
if visited[location]:
return "infinite loop"
visited[location] = True
location = findLine(prog, T)

How to understand this nested recursion?

This code has me lost. When run, it outputs sequences which I finds strange:
def print_n(number):
if (number <= 0):
return None
else:
print number
print_n(number-1)
print_n(number-1)
print_n(4)
I thought it would output this sequence:
4,3,2,1,1,2,1,3,2,1
however it actually outputs:
4,3,2,1,1,2,1,1,3,2,1,1,2,1,1
I tried to draw the stack diagram of this function but when I get lost at the second appearance of the print_n(number-1).
I can understand this program without the second appearance of the print_n(number-1), as it's just normal recursion. However, the second print_n(number-1), seems much more complicated than I expected, I don't know how to trace this function call and explain the result...
Since the if block has an unconditional return, you can remove the else and the program will continue to behave the same way.
def print_n(number):
if (number <= 0):
return None
print number
print_n(number-1)
print_n(number-1)
Here, it's more apparent what is going on: You print number, and then call print_n twice using number-1. You can work backwards to derive the output.
print_n(1) prints "1"
print_n(2) prints "2" plus "1" plus "1": "211"
print_n(3) prints "3" plus "211" plus "211": "3211211"
print_n(4) prints "4" plus "3211211" plus "3211211": "432112113211211"
I liked the answer of Kevin, but let me add a few words towards "understanding recursion":
I often suggest using sheets of paper representing the stack. each sheet contains its local varaibles and current state - and you can mark the line you are "processing" with a pen.
Use a separate sheet as output / console.
This gives you a very good understanding of what is going on.
Of course, following your code in a debugger and examining the stack trace can be helpful as well. But try the paper-approach first!

Categories

Resources