Printing first n Happy Numbers - Python - python

I am writing a piece of code in python that checks whether a given number is happy or not, that is taking the number and adding the sum of their squares is 1 which is a happy number or ending up with a never ending loop which defines a unhappy number.
After that, I want to list the first n Happy Numbers. I got the checking for happy numbers down albeit sloppily, I just can't seem to figure out the listing part.
def adder(num):
total=0
if len(str(num))>1: #check if given number is double digit or not
tens = int(str(num)[0]) # splitting the digits
ones = int(str(num)[1])
total = (tens**2)+(ones**2) # summing up the squares
#print (total)
return total
else: # if the given number is a single digit
total = (num**2)
#print (total)
return total
#adder(9)
def happynumber(num, counter):
N = adder (num) # storing the sum in a variable
#print ("value of n is {}".format(N))
if N == 1: #checks if the sum is 1
# print ("In just {} tries we found that {} is a happy number.".format(counter, number))
print (number)
else: # if the sum isn't 1, racalls the happynumber function
counter += 1 # keeps track of number of tries so that we don't end up in an infinite loop
if counter < 11: # setting the limit for number of tries
#print (counter)
happynumber (N, counter)
else:
#print ("it took us {} tries and found that the number {} is not a happy number".format(counter, number))
return False
counter = 0
for i in range(0,100): # listing all the happy numbers between 0 and 100
number = i
happynumber (number, counter)
Additionally, I would like it if you guys would review my writing style and give some pointers.
The problem, I am not able to list the first n numbers however way I try.
I tried using counter in the loop but to no avail.

If your main problem is that you want to have all your happy numbers in a list, you can easily address this by defining a list outside the recursion loop.
def happynumber(num, counter):
N = adder(num)
if N == 1:
happyhappy.append(number) #new happy number into list
else:
...continue with your code
#-------main script-------------
happyhappy = [] #create a list to store your happy numbers
counter = 0
for i in range(100):
number = i
happynumber(number, counter)
print(happyhappy) #and retrieve the list
Having said that there is an inefficiency in your adder() function. It calculates only up to two digits. Even worse, it has to perform the square operation for each digit from scratch, which is very time consuming.
A better way is to pre-calculate the squares and store them in a dictionary:
square_dic = {str(i): i ** 2 for i in range(10)} #create a dictionary of squares
def adder(num):
s = str(num) #make the number into an iterable string
x = [square_dic[i] for i in s] #look up the square of each digit
return sum(x) #and calculate the sum of squares
Thanks to list comprehensions in Python we can make it even snappier
square_dic = {str(i): i ** 2 for i in range(10)}
def adder(num): #does exactly, what the other function did
return sum(square_dic[i] for i in str(num))

So first off, you shouldn't have to pass your counter variable since it is a global variable. I'd also probably declare it up above your methods just to make sure there are no issues.
Your next issue is you never reset your counter variable. So the first time the program runs into a number that is not-happy after 10 attempts it proceeds to return false for every single number that is not happy after the first attempt. Try adding in counter = 0 before your return False line in the if counter < 11/else block

Here is an example with very small change to your current code. I add another input for the happynumber function, that is your list that you want to store the happy numbers.
I am sure this is not the most efficient way, but it shows how you can update your code.
The code :
def happynumber(num, counter, the_list):
N = adder(num);
print ("value of n is {}".format(N));
if counter == 0: #THIS IS ADDED
the_list.append(num); #THIS IS ADDED
if N == 1:
print ("In just {} tries we found that {} is a happy number.".format(counter, number))
print (num);
else:
counter += 1
if counter < 11:
print (counter)
happynumber(N, counter, the_list)
else:
print ("it took us {} tries and found that the number {} is not a happy number".format(counter, number))
the_list.pop(); #THIS IS ADDED
return False
counter = 0;
happynumber_list = []; #THIS IS ADDED
for i in range(0,100):
number = i
happynumber (number, counter, happynumber_list)
In the first attempt, the number will be stored to the list...but if the number was found not a happy number.. then that number will be pop out of the list.
Here is the result :
[1, 7, 10, 13, 19, 23, 28, 31, 32, 44, 49, 59, 68, 70, 79, 82, 86, 91, 94, 95, 97]
Is this okay?

You could use memoization to make the use of a counter unnecessary. I would also use a generator (yield) so only the number of results are produced that you ask for. Then you don't have to specify a range (0, 100), but can just specify that you want the first 25 happy numbers (or whatever is needed).
Here is how that would look:
import itertools
def sum_digit_squares(num):
return sum(int(i)*int(i) for i in str(num))
def happynumbers():
memo = { 1: True }
for i in itertools.count(): # forever (as long as getting from this iterator)
result = None # Will become either True (happy) or False (not happy)
visited = set()
j = i
while result is None:
if j in memo: # Did we make the calculation for this one before?
result = memo[j]
elif j in visited:
result = False # endless loop detected
else:
visited.add(j)
j = sum_digit_squares(j)
for j in visited:
memo[j] = result
if result:
yield i
print (list(itertools.islice(happynumbers(), 0, 25))) # Produce first 25 happy numbers

Related

alternative to if else python

Ok i wrote this in python then rewrote because i don't like if else statements.
the first version worked perfectly. my second one failed and ended up taking more lines than my first version. my question is, am i just stupid? is there a better way to do this or should i just accept the need for if else statement?
sorry for the code dump, i am going mad
first attempt
#number we are starting with
num = 1
#what we are going to apply equation loop to
result = num
#how many times goes through equation loop before reaching 1
count = 0
# list of numbers used in equation loop
num_in_loop = [result]
#end equation loop function.
running = True
# equation loop
def eqation_loop(running, num_in_loop, count, num, result):
while running == True:
if (result % 2) == 0:
result = result /2
count +=1
num_in_loop.append(result)
elif result == 1.0:
print(num, "took", count ," loops to get to 1: numbers in loop = ", num_in_loop, file=open('3x+1/result.txt','a'))
num +=1
print(num)
result = num
num_in_loop = [result]
count = 0
elif num == 100:
running = False
elif (result % 2) != 0:
result = result * 3 + 1
count +=1
num_in_loop.append(result)
eqation_loop(running, num_in_loop, count, num, result)
second attempt:
#number we are starting with
num = 1
#what we are going to apply equation loop to
result = num
#how many times goes through equation loop before reaching 1
count = 0
# list of numbers used in equation loop
num_in_loop = [result]
limit = int(input("range you want to try: " ))
def update_var(num_in_loop,result,count):
count +=1
num_in_loop.append(result)
return equation_loop(limit,num, result)
def reset_var(num_in_loop, count, limit,num, result):
print(num, "took", count ," loops to get to 1: numbers in loop = ", num_in_loop, file=open('3x+1/test.txt','a'))
num +=1
result = num
num_in_loop = [result]
count = 0
return equation_loop(limit,num, result)
def equation_loop(limit,num, result):
if num == limit:
return
elif result == 1:
return reset_var(num_in_loop, count, limit,num, result)
elif (result % 2) == 0:
result = result /2
return update_var(num_in_loop,result,count)
elif (result % 2) != 0:
result = result *3 +1
return update_var(num_in_loop,result,count)
equation_loop(limit,num, result)
You can't write this without any if/else statements (okay maybe if you really know what you're doing you technically can by severely abusing while, but you shouldn't), but here's a simplified-down version that hopefully contains some useful examples of how to make your code easier to write (and read!):
def equation_loop(num: int) -> list[int]:
"""
Repeatedly applies the magic equation trying to
reach 1.0, starting with num. Returns all the results.
"""
nums = [num]
while True:
if num == 1:
return nums
if num % 2:
num = num * 3 + 1
else:
num = num // 2
nums.append(num)
for num in range(1, 100):
results = equation_loop(num)
print(f"{num} took {len(results)} loops to get to 1: {results}")
A key thing here is that you don't need so many variables! A single loop only needs its starting point (num) and only needs to return the list of results (from which you can get the count, since it'll be the length of the list). Reducing the number of redundant variables eliminates a lot of unnecessary lines of code that are just copying state back and forth. Passing a value like running = True is unnecessary when it will always be True until it's time to end the loop -- instead just use while True: and return or break when you're done.
The big takeaway is that if you have two variables that always have the same value (or even two values that are always related in exactly the same way, like a list and its length), you probably just need one of them.
You can also simplify the code by separating the two nested loops -- for a given num you want to loop until the number reaches 1, so that's one loop. You also want to loop over all the nums up to 99 (it took me a while to even figure out that that's what the code was doing; I had to run it and look at the output to see that some of those extra pieces of state were serving to implement nested loops inside a single loop). Doing those in two different loops makes it easy, and you can put one of them in a nice neat function (I used your equation_loop name for that, although it does less work than your original version does) that takes the starting num and returns the list of results for that starting point. That simple function can then be called in an even simpler for loop that iterates through the nums and prints the results for each one.
Note that I kept everything as ints by using int division (//) -- testing floats for exact equality is often dangerous because floating point numbers aren't exactly precise! Your equation is always operating with integer values (because you only divide by two if it's an even number) so it makes more sense to use int division and not even worry about floating point values.

Trying to return multiple random numbers as an input parameter to a defined function to return true if the numbers are even and false is odd

I have to have three defined functions. The first asks the user for an input and it is returned to the next which creates random numbers in the range specified in the first function. That is then returned to the third function which checks all the numbers and returns true if they are even and false if they are odd. I have this so far, but I get the error "TypeError: 'int' object not iterable." I'm not sure what I'm doing wrong.
import random
def odd(n):
my_list = []
my_list.append(n)
for i in len(my_list):
if n%2 != 0:
return False
else:
return True
def producer(num):
for i in range(num):
n = random.randint(100,2000)
return n
def main():
ask = int(input('How many numbers do you want to generate? '))
return ask
num = main()
numbers = producer(num)
gen = odd(n)
print(gen)
the error is because of the for i in len(my_list). len(my_list) will return an int.
you want "for num in my_list" instead. then inside the function, "num%2" instead of "n%2".
Is this what you are trying to achieve:
import random
def get_user_input():
# The first asks the user for an input and it is returned to the next
user_input = int(input('How many numbers do you want to generate? '))
return user_input
def producer(n):
# which creates random numbers in the range specified in the first function.
random_list = [random.randint(100, 2000) for i in range(n)]
return random_list
def odd(lst):
# That is then returned to the third function which checks all the numbers and
for n in lst:
if n % 2 == 0:
# returns true if they are even
print(True)
else:
# and false if they are odd
print(False)
if __name__ == '__main__':
user_input = get_user_input()
list_range = producer(user_input)
odd(list_range)
I see a number of problems with your code.
Your for loops will each return a value on the first iteration of the loop, so they aren't doing you any good as loops...there will never be a second iteration of either loop.
n is undefined in your outer scope, so the line gen = odd(n) will throw an exception stating that n is undefined.
You have a problem with the definition of what you want your code to do. You want to generate a list of numbers and pass them to the odd function, but then you say you want odd to return True or False based on if "that number" is odd or even. But there isn't a single number. You're passing a list of numbers, and in general, some will be odd and some will be even.
Your interpretation of what n%2 != 0 means seems backwards.
Here's a version of your code that addresses all of these issues. I decided to replace your odd function with a function named more_odd_than_even. The function does as the name implies. It returns True if more of the numbers in the list are odd than are even, and False otherwise.
import random
def more_odd_than_even(the_list):
odd_count = 0
even_count = 0
for n in the_list:
if n % 2 == 0:
even_count += 1
else:
odd_count += 1
return odd_count > even_count
def producer(num):
my_list = []
for i in range(num):
n = random.randint(100, 2000)
my_list.append(n)
return my_list
def main():
ask = int(input('How many numbers do you want to generate? '))
return ask
num = main()
numbers = producer(num)
print(f"The generated list is:{numbers}")
gen = more_odd_than_even(numbers)
if gen:
print("More of the numbers are odd")
else:
print("More of the numbers are even (or there are an equal number of each)")
Here are a few sample runs of this code:
How many numbers do you want to generate? 6
The generated list is:[972, 1327, 1603, 1714, 491, 1007]
More of the numbers are odd
How many numbers do you want to generate? 7
The generated list is:[1540, 696, 904, 475, 1007, 1238, 148]
More of the numbers are even (or there are an equal number of each)

About a function on Python

I'm a absolute beginner, and when I made a function to count the number of even ints on a given list, it didn't went as expected, and I can't see where I'm doing it wrong.
nums = [2,2,4,4,5,6,7,8,9]
def count_even(nums):
for number in nums:
num = 0
if number % 2 == 0:
num += number
return num
else:
continue
The output is:
count_even(nums)
2
It stops on nums[1] for some obscure reason.
Or it just prints the first "2" and adds it, and I don't know how to fix it, yet.
You have three problems here.
You're setting num = 0 every time through the loop, instead of just once at the start. So you're only going to get the very last count that you do.
You're doing num += number, instead of num += 1, so instead of counting the even numbers, you're adding them.
You're doing a return num as soon as the first even number is found, instead of only at the end of the function, after the loop. (And that also means that if there are no even numbers, instead of returning 0, you return None).
While we're at it, you don't need else: continue, because continuing is already what happens by default when you fall off the end of a loop.
Anyway, this means it's not stopping at nums[1], it's stopping at nums[0]—but it's adding 2 instead of 1 there, which makes things confusing. (It's always fun when bugs interact like that. Even more fun when they happen to exactly cancel out for your test case, like if you did nums = [6,2,4,4,5,6,7,8,9] and got back 6 and thought everything was working…)
So:
def count_even(nums):
num = 0
for number in nums:
if number % 2 == 0:
num += 1
return num
Your function stops on nums[1] because the return keyword exits the function and returns num (which is set to 0 + number). If you want to print out the sum of all the even numbers in nums you could move the return statement to the end and take the num = 0 expression out of the for loop (because it will reset to 0 after each iteration), like this:
def count_even(nums):
num = 0
for number in nums:
if number % 2 == 0:
num += number
else:
continue
return num
Also, your else statement is redundant here, so you could simply remove it, leaving you with the following:
def count_even(nums):
num = 0
for number in nums:
if number % 2 == 0:
num += number
return num
And you could simplify that further to:
def count_even(nums):
evens = [number for number in nums if number % 2 == 0]
return sum(evens)
Another solution:
def count_even(nums):
return len([i for i in nums if i % 2 == 0])
There are a couple of problems in your code:
Your num needs be defined outside the loop, so you can count every even number found. If you define it inside the loop, it resets to 0 every iteration.
When you find an even number, you increment it towards the counter num. You need to instead increment num by 1.
You return immediately when a even number is found, this means the function exits on the first even number. You instead want to return the total num at the end.
You have an unnecessary continue in the else block. If a uneven number is found, simply ignore it.
Which these suggestions, you could implement your code like this:
nums = [2,2,4,4,5,6,7,8,9]
def count_even(nums):
num = 0
for number in nums:
if number % 2 == 0:
num += 1
return num
print(count_even(nums))
# 6

Finding all numbers that evenly divide a number

So I'm trying to make a program that when I input a number it will give me all the factors(12->1,2,3,4,6,12). I only started programming very recently so there may be some very obvious things. But here's my code
numbers = [1]
newnum = 1
chosen = int(input("Enter what you want the factors of: "))
def factors(numbers,newnum,chosen):
lastnum = numbers[-1]
if (chosen == lastnum):
for number in numbers:
if (number % 1 != 0):
numbers.remove(number)
print (numbers)
else:
factors(numbers,newnum,chosen)
else:
newnum = numbers[-1] + 1
numbers.append(newnum)
print (numbers)
factors(numbers,newnum,chosen)
factors(numbers,newnum,chosen)
Ok, so I don't really need the redundancies addressed but if you see something that would completely stop the program from working please point it out. Sorry I bothered you all with this but I don't know what else to do.
There are lots of problems:
Every integer number modulo 1 is zero because each integer is divisible by one without remainder.
You remove items from the list you're iterating over, that will definetly give wrong results if you don't do it carefully!
You try to do recursion but you don't return the result of the recursive call. That's possible because you operate on a mutable list but it's generally not really good style
You don't have any inline comments explaining what that line is supposed to do, so it's hard to give any reasonable guidance on how to improve the code.
If you want a code that finds all factors, consider something like this:
chosen = int(input("Enter what you want the factors of: "))
def factors(chosen, currentnum=None, numbers=None):
# Recursion start, always append 1 and start with 2
if numbers is None:
numbers = [1]
currentnum = 2
# We're at the last value, it's always divisible by itself so
# append it and return
if currentnum == chosen:
numbers.append(currentnum)
return numbers
else:
# Check if the chosen item is divisible by the current number
if chosen % currentnum == 0:
numbers.append(currentnum)
# Always continue with the next number:
currentnum += 1
return factors(chosen, currentnum, numbers)
>>> factors(chosen)
Enter what you want the factors of: 12
[1, 2, 3, 4, 6, 12]
That's not the optimal solution but it uses recursion and gives a proper result. Just don't enter negative values or catch that case in the function at the beginning!
# Two Pointer Approach
ans = []
def divisor(val):
result = []
for i in range(1, val + 1):
ans.append(i)
i = 0
j = len(ans) - 1
while i < j:
if ans[i] * ans[j] == ans[-1]:
result.append(ans[i])
result.append(ans[j])
i += 1
else:
j -= 1
return sorted(result)
print(divisor(12))
# Output
>>> [1, 2, 3, 4, 6, 12]

Python Code to Find x Prime Numbers w/ For Loop?

I am trying to put together a simple program which could work out n prime numbers. I would like to do this by using a nested for loop, where one would go through the numbers, and another would divide that number by all of the numbers up to it to see if it would be divisible by anything.
The problem I am having is that in the main for loop, I need to start it at 2, seeing as 1 would mess up the system and I don't want it to be considered a prime. For the loop to have a starting number however, it also needs an ending number which is difficult in this instance as it is hard to generate the largest prime that will be needed prior to the loop working.
Here's the program that I am using right now. Where I have marked X is where I need to somehow put an ending number for the For Loop. I guess it would be much simpler if I let the For Loop be completely open, and simply take out anything that '1' would produce in the loop itself, but this feels like cheating and I want to do it right.
check = 0
limit = int(input("Enter the amount of Prime Numbers"))
for i in range(2,X):
check = 0
if i > 1:
for j in range(2,i):
if (i % j) == 0:
check = 1
if check == 0:
print (i)
Thanks for your help!
You can step through an unlimited amount of numbers using a generator object.
Insert the following somewhere near the top of your code:
def infinite_number_generator(initial_value=2):
""" Generates an infinite amount of numbers """
i = initial_value
while True:
yield i
i += 1
What this does is it creates a function for constructing generator objects that "pause" whenever they reach the yield statement to "yield" whatever value is specified by the yield command, and then continue to execute from the next line beneath the yield statement.
Python's own range function is itself an example of a generator, and is roughly equivalent to (ignoring the step argument and other peculiarities)
def range(start, end):
i = start
while i < end:
yield i
i += 1
So your program would then look like this:
def infinite_number_generator(initial_value=2):
""" Generates an infinite amount of numbers """
i = initial_value
while True:
yield i
i += 1
check = 0
limit = int(input("Enter the amount of Prime Numbers"))
for i in infinite_number_generator():
check = 0
for j in range(2,i):
if (i % j) == 0:
check = 1
if check == 0:
print (i)
if i == limit:
break
I should also point out that the code you provided is buggy - it will never stop printing because there's no checking whether you've found your limit number of primes yet or not.
This should do what you want.
check = 0
limit = int(input("Enter the amount of Prime Numbers"))
counter = 0
i = 2
while counter < limit:
check = 0
if i > 1:
for j in range(2,i):
if (i % j) == 0:
check = 1
if check == 0:
counter += 1
print (i)
i += 1
In your code you start i with 2 and always increment by 1, so the i will always remain greater than 1, therefore the test if i > 1 is useless.
For efficiency you can stop the check at the square of i or i/2 (no divisors in [i/2 + 1, i[ ).
you can update your code as follow:
n = int(input("Enter the amount of Prime Numbers: "))
FoundPrimes = 0
i = 2
while FoundPrimes < n:
isPrime = True
for j in range(2,1 + i//2):
if (i % j) == 0:
isPrime = False
if isPrime:
FoundPrimes += 1
print(i, end = '\t')
i += 1

Categories