Collatz sequence - Have I done it right? - python

I am now learning how to program in python and the book I am learning from gave me the practice project to build this sequence and place it in a loop until the value is 1.
My code looks like this:
print('Enter a number')
number = input()
num = number
def collatz(number):
global num
if int(num) % 2 == 0:
num = int(num) // 2
print(str(num))
elif int(num) % 2 == 1:
num = 3 * int(num) + 1
print(str(num))
while num != 1:
number = num
collatz(number)
It works, but I am not sure if I've done it in the way I should've.
My issue was that I was using number both as a global variable and a parameter and when I first write the code every 'num' was called 'number', when first testing it it would enter an endless loop and repeat the first if or elif all over again. I figured that the problem is that it doesn't refer to the global variable but creates a local one, so I've tried to declare at the beginning to use the global one, as you can see, however it didn't let me as global variable and parameter can't be the same, so I've created the 'num' variable and assigned it to the if block, but now it looks like that the number parameter doesn't really interfere with my code.
The fact that it works makes me happy, however I would like to confirm this is right.

There are a few things I'd suggest you fix.
No need for a separate print for input. Also might as well cast it to int now.
number = int(input('Enter a number'))
Get rid of the global and use return. Also get rid of your casting in your function.
See:
def collatz(n): # parameter is an int
if n % 2 == 0:
n = n // 2
elif n % 2 == 1:
n = 3 * n + 1
print(n) # single print
return n
So combined:
def collatz(n):
if n % 2 == 0:
n = n // 2
elif n % 2 == 1:
n = 3 * n + 1
print(n)
return n
number = int(input('Enter a number'))
while number != 1:
number = collatz(number)

Related

hailstone program in python

i have to write a hailstone program in python
you pick a number, if it's even then half it, and if it's odd then multiply it by 3 and add 1 to it. it says to continue this pattern until the number becomes 1.
the program will need methods for the following:
accepting user input
when printing the sequence, the program should loop until the number 1.
print a count for the number of times the loop had to run to make the sequence.
here's a sample run:
prompt (input)
Enter a positive integer (1-1000). To quit, enter -1: 20
20 10 5 16 8 4 2 1
The loop executed 8 times.
Enter a positive integer (1-1000). To quit, enter -1: 30
30 15 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1
The loop executed 19 times.
Enter a positive integer (1-1000). To quit, enter -1: -1
Thank you for playing Hailstone.
right now i have this:
count = 0
def hailstone(n):
if n > 0
print(n)
if n > 1:
if n % 2 == 0:
hailstone(n / 2)
else:
hailstone((n * 3) + 1)
count = count + 1
i don't know what to do after this
Try to think in a modular way, make two functions: check_number() and user_call(). Check_number will verify if the current number in the loop is odd or even and the user_call() just wraps it to count how many times the loop did iterate.
I found the exercise in a great book called Automate Boring Stuff with Python, you have to check it out, if you don't know it already.
Here's my code. Try to use what serves you the best.
from sys import exit
def check_number(number):
if number % 2 ==0:
print(number // 2)
return(number // 2)
else:
print(number*3+1)
return number*3+1
def user_call(number):
count = 0
while number != 1:
count += 1
number = check_number(number)
return count
if __name__ == "__main__":
try:
number = int(input('Give a number \n'))
count = user_call(number)
print('count ',count)
except Exception as e:
exit()
you can use global
visit https://www.programiz.com/python-programming/global-keyword to learn more
import sys
res = []
def hailstone(number):
global res
if number > 1:
if number % 2 == 0:
res.append( number // 2 )
hailstone(res[len(res)-1])
else:
res.append(number * 3 + 1)
hailstone(res[len(res)-1])
return res
number = int(input('Enter a positive integer. To quit, enter -1: '))
if number <= 0 or number == 0:
print('Thank you for playing Hailstone.')
sys.exit()
else:
answers = hailstone(number)
for answer in answers:
print(answer)
print('The loop executed {} times.'.format(len(answers) + 1))
I used recursion to solve the problem.
Heres my code:
Edit: All criteria met
count = 0
list_num = []
def input_check():
number = int(input("Enter a positive integer (1-1000). To quit, enter -1: "))
if number >= 1 and number <= 1000:
hailstone_game(number)
elif number == -1:
return
else:
print("Please type in a number between 1-1000")
input_check()
def hailstone_game(number):
global count
while number != 1:
count += 1
list_num.append(number)
if number % 2 == 0:
return hailstone_game(int(number/2))
else:
return hailstone_game(int(number*3+1))
list_num.append(1) # cheap uncreative way to add the one
print(*list_num, sep=" ")
print(f"The loop executed {count} times.")
return
input_check()
Additional stuff that could be done:
- Catching non-integer inputs using try / except
Keep in mind when programming it is a good habit to keep different functions of your code separate, by defining functions for each set of 'commands'. This leads to more readable and easier to maintain code. Of course in this situation it doesn't matter as the code is short.
Your recursive function is missing a base/terminating condition so it goes into an infinite loop.
resultArray = [] #list
def hailstone(n):
if n <= 0: # Base Condition
return
if n > 0:
resultArray.append(n)
if n > 1:
if n % 2 == 0:
hailstone(int(n/2))
else:
hailstone((n * 3) + 1)
# function call
hailstone(20)
print(len(resultArray), resultArray)
Output
8 [20, 10, 5, 16, 8, 4, 2, 1]
Here's a recursive approach for the problem.
count=0
def hailstone(n):
global count
count+=1
if n==1:
print(n)
else:
if n%2==0:
print(n)
hailstone(int(n/2))
else:
print(n)
hailstone(3*n+1)
hailstone(21)
print(f"Loop executed {count} times")

Automate the boring tasks - exercise - collatz function

Beginner question here.
I have just attempted an exercise from Automate the boring stuff. I've completed the question in the format suggested by first defining a function as below:
"Write a function named collatz() that has one parameter named number. If number is even, then collatz() should print number // 2 and return this value. If number is odd, then collatz() should print and return 3 * number + 1."
and then using that same function, meeting those minimal constraints, to write a programme that meets the following requirements:
Then write a program that lets the user type in an integer and that keeps calling collatz() on that number until the function returns the value 1.
I've managed to generate a sequence of numbers ending with one, following the above rules, but my program prints each number in the sequence three times. Is anyone able to explain why this might be?
Thanks so much for your help
def collatz(number):
if int(number) % 2 == 0:
print(int(number)//2)
return int(number)//2
else:
print(3 * int(number) + 1)
return 3 * int(number) + 1
collatz(5)
print('Enter a number')
entry = input()
while collatz(entry) != 1:
collatz(entry)
entry = collatz(entry)
Your loop should look like this:
entry = input()
while entry != 1:
entry = collatz(entry)
You are calling the function 3 times and you have a print call in the function.
Only call the function once and I would remove the print statements from the collatz method and just print in the calling loop, e.g.:
In []:
def collatz(number):
if number % 2 == 0:
return number//2
return 3*number + 1
entry = int(input("Enter a number: "))
print(entry)
while entry != 1:
entry = collatz(entry)
print(entry)
Out[]:
Enter a number: 10
10
5
16
8
4
2
1
You can try:
def collatz(number):
if number == 0:
return 'Try again with an integer other than 0'
elif number == 1:
return 1
elif number % 2 == 0:
n = number // 2
print(n)
elif number % 2 == 1:
n = 3 * number + 1
print(n)
while n != 1:
n = collatz(n)
return n
return n
The last statement return n in line 15 is optional.

Function keeps doing the same thing

This program suppose to find 1000 prime numbers and pack them into a list
Here's the code:
num = raw_input('enter a starting point')
primes = [2]
num = int(num)
def prime_count(num):
if num % 2 == 0: #supposed to check if the number is divided by 2 evenly
num = num +1 #if it is, then add 1 to that number and check again
return num
elif num % num == 0:
primes.append(num) #supposed to add that prime to a list
num = num + 1 #add 1 and check again
return num
while len(primes) <= 999:
prime_count(num)
So what actually happens when I run it:
it asks me raw_input and then goes to various things depending on what I choose as input:
If I choose a prime, let's say 3, it runs and adds 999 of 3s to the list instead of adding it just one time and going on to try 4
If I choose a non-prime, let's say 4, it just breaks, after that I can't even print out a list
What am i doing wrong?
UPDATE:
I fixed it, but when i run it with this i'm getting an error (TypeError: unsupported operand type(s) for %: 'NoneType' and 'int')
number = raw_input('enter a starting point')
primes = [2]
number = int(number)
def prime_count(x):
if x % 2 == 0: #supposed to check if the number is divided by 2 evenly
number = x +1 #if it is, then add 1 to that number and check again
return number
else:
for i in range(3, x-1):
if x % i == 0:
primes.append(x) #supposed to add that prime to a list
number = x + 1 #add 1 and check again
return number
while len(primes) <= 999:
number = prime_count(number)
You're never using the return value from prime_count. Try this:
while len(primes) <= 999:
num = prime_count(num)
You've set your self up for confusion by using the name num as both a parameter (also a local variable) inside of prime_count, and also as a global variable. Even though they have the same name, they are different variables, due to Python's rules for the scope of variables.
Also, prime_count is (probably unintentionally) leveraging the fact that primes is a global variable. Since you're not assigning to it, but rather just calling a method (append), the code will work without using the global keyword.
However, your algorithm isn't even correct. if num % num == 0 says "if a number divided by itself has a remainder of zero" which will always be true. This program will find a lot of "primes" that aren't primes.
Real Python programs do very little in the global scope; your current code is just asking for confusion. I suggest you start with this template, and also do some reading of existing Python code.
def add_three(a_param):
a_local_var = 3 # This is *different* than the one in main!
# Changes to this variable will *not* affect
# identically-named variables in other functions
return a_local_var + a_param
def main():
a_local_var = 2
result = add_three(a_local_var)
print result # prints 5
if __name__ == '__main__':
main()

Collatz Sequence. (Python 3)

I've started the book "Automate The Boring Stuff" by Al Sweigart.
At the end of Chapter 3, the author suggests creating a Collatz Sequence in Python as a practice exercise. (the practice exercise suggests I use a the print function and return statement)
When I use a print() function in my code, it works great and I get all the evaluated values I want to see on the screen:
print("This is The Collatz Sequence")
user = int(input("Enter a number: "))
def collatz(n):
print(n)
while n != 1:
if n % 2 == 0:
n = n // 2
print(n)
else:
n = n * 3 + 1
print(n)
collatz(user)
Question:
How come when I want to use the return statement, the while loop only runs once?
For example, passing the integer 3 into my function with the return statement only gives me the return value of 3 and 10:
print("This is The Collatz Sequence")
user = int(input("Enter a number: "))
def collatz(n):
print(n)
while n != 1:
if n % 2 == 0:
n = n // 2
return n
else:
n = n * 3 + 1
return n
result = collatz(user)
print(result)
return exits the function and, therefore terminates your while loop.
Perhaps you meant to use yield instead:
print("This is The Collatz Sequence")
user = int(input("Enter a number: "))
def collatz(n):
print(n)
while n != 1:
if n % 2 == 0:
n = n // 2
yield(n)
else:
n = n * 3 + 1
yield(n)
print(list(collatz(user)))
Output:
This is The Collatz Sequence
Enter a number: 3
3
[10, 5, 16, 8, 4, 2, 1]
Yield is logically similar to a return but the function is not terminated until a defined return or the end of the function is reached. When the yield statement is executed, the generator function is suspended and the value of the yield expression is returned to the caller. Once the caller finishes (and assumably uses the value that was sent) execution returns to the generator function right after the yield statement.
In your code you don't re-feed the new value back into your equation. Try separating your while loop from the collatz module. I have an example of this below:
def collatz(number):
if number % 2 == 0:
return number // 2
elif number % 2 == 1:
return 3 * number + 1
chosenInt = int(input('Enter an integer greater than 1: '))
print(chosenInt)
while chosenInt != 1:
chosenInt = collatz(chosenInt)
print(chosenInt)
def collatz(number):
if (number%2 == 0):
return print(number//2);
else:
return (print(number*3+1));
inputNumber = input("Enter a number greater than 1:");
result = collatz(int(inputNumber));
while result != 1:
result = collatz(result);
I am getting a typeError with it! Don't know why?

Python is not recognizing my variables, but instead replacing them with zero

Here is my code:
change = 0
count = 0
value = 0
answer = 0
def numberTest():
if change == 0:
skip()
else:
value = change
def skip():
count + 1
number = value
# Check if the input is valid
if value != number:
print('PLEASE ENTER A VALID NUMBER!!!')
else:
Even = number % 2
if Even == 0:
print('Substituting even number in: x / 2')
print('%s/2=' % number)
answer = number / 2
else:
print('Substituting odd number in: 3x + 1')
print('3' + number + ' + 1=')
answer = number * 3
answer = answer + 1
answer = str(answer)
print(''+ answer +'')
if answer == 1:
finalValue()
else:
check()
def check():
value = answer
skip()
def loop():
value = int(input('Input a number: '))
change = value
skip()
loop()
def finalValue():
print('The number (' + change + ') returned as 1.')
print('A total of (' + count + ') commands were executed.')
change = change + 1
count = 0
print('')
print('')
print('')
numberTest()
Whenever I start the code, I am asked to enter a number (as expected), but then this happens:
Input a number: 1
Substituting even number in: x / 2
0/2=
0.0
I really do not understand why the program is not working as I expected, but there is one part of the code that I am suspicious of:
value = int(input('Input a number: '))
I also wrote this myself, and I am new to Python, I have only previously worked with batch, so transitioning was quite easy, though I am not very familiar with some of the commands...
EDIT
What I was expecting the program to do was ask for a number, store that number, then run it through a series of tests, but when the number gets to the actual tests, it substitutes "x" for "0", even if I type in a number such as "54656". Maybe, when it asks for the number, when I input the number, it just doesn't store it right, or something is wrong with my code...
You are trying to change global variables without declaring them:
a = 'bad'
def bad_fn():
a = 'good'
bad_fn()
print('bad_fn() is'+a)
def good_fn():
global a
a = 'good'
good_fn()
print('good_fn() is'+a)
results in
bad_fn() is bad
good_fn() is good
In general, using global variables in bad practice. Passing parameters explicitly makes debugging and code reuse much less of a headache. Here is rewritten version of your code which should be easier to understand:
# Test the Collatz conjecture:
# http://en.wikipedia.org/wiki/Collatz_conjecture
import profile
# Python 2/3 compatibility shim
import sys
if sys.hexversion >= 0x3000000:
# Python 3.x
inp = input
rng = range
else:
# Python 2.x
inp = raw_input
rng = xrange
# cache of already-seen values
seen = set([1])
def test(n):
visited = set()
while True:
if n in seen: # Ran into an already-seen chain that goes to 1
seen.update(visited)
return len(visited)
elif n in visited: # Closed loop! this should never happen
print('Loop found at n = {}'.format(n))
return None
else:
visited.add(n)
if n % 2: # n is odd?
n = 3*n + 1
else:
n //= 2
def do_profile(upto=1000000):
prof = profile.Profile()
prof.run('for n in rng(2, {}): test(n)'.format(upto))
prof.print_stats()
def main():
while True:
try:
n = int(inp('Enter a number to test (or x to exit): '))
except ValueError:
break
res = test(n)
if res is None:
print("Well, that's odd...")
else:
print("Chain terminated after {} values were tested".format(res))
if __name__=="__main__":
main()
It took 17.7s on my machine to run do_profile(1000000). It looked at a total of 2,168,611 numbers, the highest of which was 56,991,483,520. No loops were found.
Edit: I have added an inp() shim function; the code should now run in both Python 2 or Python 3.
Edit2: moved the profiling code into the main code listing and added range/xrange to the Python 2/3 shims.
If you want to change a global variable, you need to declare it preceded by global in your function, ie:
value = 0
def changeValue():
global value
value += 1
If you do not need to change the variable, global is not required.

Categories