• Mani Abedini

Finding Happy Number

Updated: Jul 11

This coding exercise is part of "30-day-leetcoding-challenge" campaign.

The link to the challenge is here: https://leetcode.com/explore/challenge/card/30-day-leetcoding-challenge/528/week-1/3284/

In a nutshell, Happy number is a number that eventually reaches to value "1" by replacing the number with the sum of the squared of its digits. Unhappy number on the other hand, is a number that never reaches to value "1" and stays in an infinite loop forever.


For example, 19 is a happy number:


But 4 is not Happy number:

...


Solution 1: Recursion

My first impression was, the problem is a typical recursive problem. However, to escape from infinite loop, we need a memorisation mechanism; by defining "Seen", a set of previous observed values.

My recursive solution is:

def SquareSumDigits(nom): # calculate the square sum of all digits
    sumDig =0      
    while nom > 0:         
        sumDig += (nom%10) **2         
        nom=nom//10     
    return sumDig
 #recursive function
 def isHappy (n, seen):
       if n in seen:  # if the number is already seen   
            return n        
      seen.add(n) # add the number to observed list (set)
      
      # call the function to run on squared sum of the digits
      return isHappy( SquareSumDigits(n), seen)
 #main code
 prev = set()
 return isHappy(n, prev) ==1               

Solution 2: Non-Recursion

However, recursion is expensive. It is much cheaper in terms of execution time and memory to solve it with iterative loop. However, in terms of speed complexity both are O(mn) when m is the length of generated numbers and n is the max number of digits.


Here is my code without recursive, which achieved a good score in terms of speed (above 99% submissions) but not a good score in terms of used memory (beats 34%).


def SquareSumDigits(nom): # calculate the square sum of digits
    sumDig =0 
    while nom > 0:
        sumDig += (nom%10) **2
        nom=nom//10
    return sumDig
prev = set() # a list of observed numbers 

while n != 1 and n not in prev: 
    prev.add(n)       
    n = sumDigits(n)

# the loop ends either we reach 1 or infinite loop is detected 
if n == 1:  
   return True
else:
   return False                          

Solution 3: Simpler Digit extraction


Further Pythonizing the code, here is the final code, faster and with less memory usage:

prev = set()# a list of observed numbers 
 
while n != 1 and n not in prev:
    prev.add(n)
    # Split the number into digits then Squared 
    n = sum (map(lambda x: int(x)**2, str(n)))
    
# the loop ends either we reach 1 or infinite loop is detected      
return n ==1 

Interesting key learning points:

  • Searching in a set is fast ( O(1) ) because implemented by hash-table.

  • (nom%10) **2 is faster than (nom%10) *(nom%10)

  • There is a maximum recursion depth that can limit recursive approach.

  • Converting the number to string then iterating over characters to extract digits was faster and much simpler in Python:

        [ int(d) for d in str(N) ] 


Follow me

  • LinkedIn
  • GitHub
  • Google Scholar Citations

© 2020 by Mani Abedini