Given n, take tsum of the digits of n. If that value has more than one digit, continue reducing a single-digit number is produced

Viewed 4877
def digital_root(n):
    x = str(n)
    while len(x)!=0 and len(x)!=1:
        r = 0
        for i in range(len(x)):
            r= r + int(x[i])
        x = str(r)
    return r

A digital root is the recursive sum of all the digits in a number. Given n, take the sum of the digits of n. If that value has more than one digit, continue reducing in this way until a single-digit number is produced. This is only applicable to the natural numbers.

My code:

Is this code efficient? I get the following error:

Traceback (most recent call last):
  File "main.py", line 8, in <module>
    test.assert_equals( digital_root(0), 0 )
  File "/home/codewarrior/solution.py", line 8, in digital_root
    return r
UnboundLocalError: local variable 'r' referenced before assignment
4 Answers

It looks like r is out of scope when you reach the return statement. It only applies locally within the while statement, which ends before the return statement.

It's good that you only have one return statement and that it is at the broadest scope for the function, and at the end. So, try declaring r outside of the while loop, in the same scope as the return statement. That scope includes everything inside the while loop as well.

def digital_root(n):
    x = str(n)
    r = 0
    while len(x) > 1:
        r = 0
        for i in range(len(x)):
            r = r + int(x[i])
        x = str(r)
    return r

I would approach this with recursion

def root_sum(n):
    x = sum(int(i) for i in str(n))
    if x > 9:
       return root_sum(x)
    return x

that is the easiest solution imho

If the length of the string x is 1 (you can also just ask if the length is greater than 1 for the while loop), the code skips straight to returning r. However, it may not know what r refers to because it only gets assigned inside the while loop.

had the same task in codewars. This is the optimal solution:

def digital_root(n):
    return n if n < 10 else digital_root(sum(map(int, str(n))))
Related