Building a function to return integer until reaching 1 in Python

Viewed 448

I am trying to build a function that will take a number and return a list. If the number is even, the second integer of the list will be the number divided by two. If it is odd, then following number will be number multiplied by 3 plus one and so on until it reaches 1. For example:

Function(5) will give

[5, 16, 8, 4, 2, 1]

My code is: 
def Collatz(n):
    out=[]
    while n>1:
        if n%2==0:
            out.append(n/2)
        elif n%2!=0:
            out.append((n*3)+1)
    print(out)
    
Collatz(20)

It doesn't produce anything. Any suggestion? I want to do this with while loop.

6 Answers

This code is close to be working, a few insights and improvements:

  1. Change n inside the while loop (otherwise n remains larger than 1 and you get infinity loop)
  2. Use lower case in function names and use underscore to separate between words
def collatz(n):
    out = [n]
    while n > 1:
        if n % 2 == 0:
            n = int(n / 2)
        elif n % 2 != 0:
            n = (n * 3) + 1
        out.append(n)
    print(out)

# [5, 16, 8, 4, 2, 1]
collatz(5)

It is because the while loop never terminates, this is because you aren't actually changing the value of n each time. So before you append to the array say something like n=n/2 (or whatever the calculation is) then append n. This means that n will eventually reach one and you get a print out

The problem with your code is you are not decrementing the value of n which is leading to an infinite loop. Every time when you are appending a new value in the list you also need to update the value for n.

Try this:

def Collatz(n):
    out=[]
    while n>1:

        if n%2==0:
            out.append(n//2)
            #updating n
            n= n//2

        elif n%2!=0:
            out.append((n*3)+1)
            #updating n
            n= (n*3)+1
    print(out)
    
Collatz(20)

Output: [10, 5, 16, 8, 4, 2, 1]

Here's a version that works.

def Collatz(n):
    out=[n]
    while n>1:
        if n%2==0:
            n = n/2
        else:
            n = (n*3)+1
        out.append(int(n))
    return out

print(Collatz(5))

Some notes on your version:

  • Your loop never updates "n" so n stays at its initial value forever, leaving you in an infinite loop.
  • The "elif" can be simply "else" - if the value is not even, then it's odd (assuming we're working with integers)
  • If you want the initial value to be in the list, we should probably initialize the list that way.
  • In your problem statement, you say you need the function to "return a list". Printing from inside the function is fine if that's really all you need, but if we actually return the list with a return statement, then the list it generates can be used by other code.
  • If you're using Python 3, the operation n/2 will return a float, even if n was an integer to start. If we only want integers in our list, we need to do one of two things:
    • Make it explicitly integer division with n//2
    • Or re-cast n to an integer with int(n) before we insert it into the list. (Which is how I did it here.)

Try this:

def Collatz(n):
    l=[n]
    while l[-1]!=1:
        x=l[-1]/2 if l[-1]%2==0 else l[-1]*3+1
        l.append(int(x))
    return l

Notice that your code doesn't change n.Therefore your loop never terminates. Check this out:

def collatz(n):
    my_list = [n]
    while n > 1:
        if n % 2:
            n = n * 3 + 1
        else:
            n = n / 2
        my_list.append(n)
    return(my_list)
Related