Writing a function to perform Collatz Conjecture - python

I am asked to complete the rest of this code:
def collatz_step(n):
if n % 2 == 0:
return n // 2
and write a while loop that will set n to the next value given by collatz_step until it reaches 1, printing the value of n at each step.
which I completed as follows, which should give me 10, 5, 16, 8, 4, 2, 1
def collatz_step(n):
if n % 2 == 0:
return n // 2
else:
return 3*n + 1
while n != 1:
n = collatz_step(n)
print(n)
collatz_step(3)
but I get an error saying that 'n' is not defined? this part specifically,
while n != 1:
how do I solve this to get the correct results?

I see two things wrong:
You never define n before trying to use it.
At the end you call the function again but do nothing with the result.
It sounds like you just want to define n and then call the function in your loop, not again at the end. So something like:
# no changes to the function definition
n = 3
while n != 1:
n = collatz_step(n)
print(n)
Just think about it semantically... You want to start with a value of 3 and then repeat the operation of updating that value until it equals 1. So first you define a variable with the value you want, then you write a loop in which you update that value until the loop condition is met.

Related

Using recursion to determine a semiperfect number

I am in need of your help again. I'm writing a code that determines whether a number is a semiperfect number or not by returning a boolean value. So first I thought I would make a list of the factors of the number excluding the number itself
def isSemiPerfect(n):
factor = []
for i in range(n-1, 0, -1):
if n%i == 0:
factor.append(i)
If I were to check for the number 12, it would return
factor = [1, 2, 3, 4, 6]
Then I need to make a code to use recursion to check if you add certain numbers it would be equal to 12
True = 6 + 4 + 2 or 6 + 3 + 2 + 1
Can someone tell me how I can use recursion to do trial and error? Like I'll always start with the biggest number and try a path with the next biggest number until I've tried all combinations.
I know there isn't a lot to go on with I just hope that you can tell me how I can use recursion effectively. Thank you!
You can think about it this way.
The question "Can [1,2,3,4,6] sum to 12"?
Is the same as "Can [2,3,4,6] sum to 11" or "Can [2,3,4,6] sum to 12"?
One uses the first element (and has lower sum) and the other does not use the first element and has the same sum.
so a start function would be:
def f(lst,sum_left):
return f(lst[1:], sum_left-lst[0]) or f(lst[1:],sum_left)
However, this function does not know when to stop. So we need some base cases.
Obviously, if sum is 0, the answer is trivially yes:
if sum_left == 0:
return true
Another base case is, if the sum is < 0, we have taken a too big element and we can never recover.
if sum_left < 0:
return true
Also, we can risk running out of elements like [1,2] can never sum to 50, so we add a base case if there are no elements in the list:
if not lst:
return false
Putting it all together:
def f(lst, left):
if left == 0:
return True
if left < 0:
return False
if not lst:
return False
return f(lst[1:],left-lst[0]) or f(lst[1:],left)

Building a function to return integer until reaching 1 in Python

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.
This code is close to be working, a few insights and improvements:
Change n inside the while loop (otherwise n remains larger than 1 and you get infinity loop)
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]
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
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.)
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)

My python recursive function won't return and exceeds maximum recursive depth

I simply do not understand why this is not returning the value and stopping the recursion. I have tried everything but it seems to just keep on going no matter what I do. I am trying to get the program to get the loop to compare the first two values of the list if they are the same return that it was the first value. If they were not, add the first and second values of each list and compare, etc etc until it reaches the end of the list. If the sum of the values in each list never equal each other at any point then return 0.
It is supposed to take three inputs:
A single integer defining the length of the next two inputs
First set of input data
Second set of input data
Ex input
3
1 3 3
2 2 2
It should output a single number. In the case of the example data, it should output 2 because the sum of the lists equalled at the second value.
N = int(input())
s1 = input().split()
s2 = input().split()
count = 0
def func1(x,y):
if x == y:
return(count)
elif (N - 1) == count:
return(0)
else:
count + 1
return(func1(x + int(s1[count]), y + int(s2[count])))
days = func1(int(s1[0]),int(s2[0]))
print(days)
I am sorry in advance if I really messed up the formatting or made some dumb mistake, I am pretty new to programming and I have never posted on here before. Thanks in advance :)
The problem is that you never actually update the variable count. However, just writing:
count += 1
is not going to work either without declaring the variable global:
def func1(x, y):
global count
....
That said, global variables increase code complexity and break re-enterability, i.e. the same function can no longer be called twice, not to mention about concurrency. A much cleaner way is to make count a function argument, it will look like this (the code not tested and is here for illustration only):
N = int(input())
s1 = [int(c) for c in input().split()]
s2 = [int(c) for c in input().split()]
def func1(x, y, count=0):
if x == y:
return count
elif count == N - 1:
return 0
else:
return(func1(x + s1[count], y + s2[count]), count + 1)
days = func1(int(s1[0]),int(s2[0]))
print(days)
To answer "How would you go about solving this problem then" – If I understood the problem correctly, the aim is to find the index where the "running total" of the two lists is the same. If so,
def func1(s1, s2):
total_a = 0
total_b = 0
for i, (a, b) in enumerate(zip(s1, s2)):
total_a += a
total_b += b
if total_a == total_b:
return i
return 0
print(func1([1, 3, 3], [2, 2, 2]))
does the trick. (I've elided the input bits here – this function just works with two lists of integers.)

Function That Sums All Elements in List Up to But Not Including The First Even Number

I'm trying to write a python function that will sum up all of the elements in a list up to but not including the first even number. The function needs to pass the following tests:
from test import testEqual
testEqual(sum_of_initial_odds([1,3,1,4,3,8]), 5)
testEqual(sum_of_initial_odds([6,1,3,5,7]), 0)
testEqual(sum_of_initial_odds([1, -7, 10, 23]), -6)
testEqual(sum_of_initial_odds(range(1,555,2)), 76729)
I tried the following:
import random
lst = []
def sum_of_initial_odds(nums):
sum = 0
#test if element is odd number - if it's odd, add it to the previous integer
for i in lst:
if i % 2 != 0:
sum = sum + i
return sum
#test if element is even number - if it's even, don't include it and break code
else:
if i % 2 == 0:
break:
I'm currently getting a parse error:
ParseError: bad input on line 11
which is the line:
else:
How else can I write this code so that it adds the elements in a list, but doesn't include the first even number, without getting Parse errors?
You can do this very easily using itertools.takewhile:
>>> import itertools
>>> sum(itertools.takewhile(lambda x: x % 2 == 1, [1,3,1,4,3,8]))
5
takewhile will yield elements from the given sequence while the predicate x % 2 == 1 is True, i.e. it will get you all numbers up to the first even one. And sum, well, sums those values.
You have a few problems:
Indentations, which others have already mentioned
You return sum the first time you hit an odd number; this is so not what you want.
You ignore the input parameter nums and work with the empty global lst.
Your lowest if is redundant: you already know you have an even number when you get here.
In general, stuff partial results into local variables; have a single return at the bottom of your routine.
import random
def sum_of_initial_odds(lst):
sum = 0
#test if element is odd number - if it's odd, add it to the previous integer
for i in lst:
if i % 2 != 0:
sum = sum + i
#test if element is even number - if it's even, don't include it and break code
else:
break
return sum
print sum_of_initial_odds([1,3,1,4,3,8]) == 5
print sum_of_initial_odds([6,1,3,5,7]) == 0
print sum_of_initial_odds([1, -7, 10, 23]) == -6
print sum_of_initial_odds(range(1,555,2)) == 76729
THe output from this is four True values.
You could also use
def sum(numbers):
sum = 0
for number in numbers:
if number % 2 == 0:
break
else:
sum += number
return sum
And test using the asset statement which is a specialized form of raise statement
The advantage is that it throws AssertionError only when __debug__ is true thus avoiding throwing exception in production.
assert sum([1,3,1,4,3,8]) == 5 , "Your message if assertion fails"
You can turn off __debug__ by
Start interactive mode with python -O
Setting variable PYTHONOPTIMIZE to True

Python, For loop counter do not update?

I am writing a simple program which gives the list of all prime no. less than a given no. N.
I am facing a problem that my for loop counter check in the function primes_gen else clause do not updates. Though, when i use the same logic without a function, as mention in the second code snippet, everything works correct.
Logic applied:-
when n passed to function is <= 2, blank list is returned
when n is > 2, range(3,n,2) identifies all the odd number. This is step 1 marked in first code snippet.
second for loop, marked with step 2, checks if no. is divisible to the all no.'s less than i. If yes then check counter is incremented by +1 and this loop is ended with 'continue'
if check remains zero till the end of the for loop then i is appended to the list and the finally after completion of the loop list is returned.
Problem:-
-The list i got as a result was [2] which is wrong ans. so I wrote step 3, as marked in 1st snippet, and i found that my check counter do not updates after the 'continue', as shown in the output. when same logic is applied without a function as written in second code snippet everything works correct.
I am not able to get what is going wrong here, why does check do not updates after continue?
contents =['10']
def prime_gen(n):
num_list=[2]
if n <=2:
return []
else:
for i in range(3,n,2): #step 1
check=0
for u in (2,i): #step 2
if i%u == 0:
check += 1
continue
print (check,i) #step 3
if check == 0:
num_list.append(i)
return num_list
if __name__== '__main__':
for j in range(len(contents)):
print (int(contents[j]))
num_list = prime_gen(int(contents[j]))
print (str(num_list).replace('[','').replace(']',''))
Output
10
1 3
1 5
1 7
1 9
2
Expected ans is (when step 3 above is commented)
10
2, 3, 5, 7
Second code without function
contents = ['10'] #to-check
for i in range(len(contents)):
target = int(contents[i])
num_list= [2]
for j in range(3,target,2):
check = 0
for u in range(2,j):
if j%u == 0:
check +=1
continue
if check == 0:
num_list.append(j)
#print (num_list)
print (str(num_list).replace('[','').replace(']',''))
output
2, 3, 5, 7
Your problem took a second to find, but is very simple to solve. The issue is at your "Step 2". You have the line
for u in (2,i): #step 2
That iterates u through all the variables in the tuple you provide, namely 2 and i. Of course, i%i will be 0, so check will always be true. What you want instead is this:
for u in range(2,i): #step 2
This will iterate u through all the variables between 2 and i, as you intend.
See these two examples:
>>> i = 7
>>> for u in (2,i): print u,
2 7
>>> for u in range(2,i): print u,
2 3 4 5 6
Basically, you just forgot to use range in your for loop, and that's going to give you wildly different results than what you actually want.

Categories