HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Repeatedly multiplying digits until a single digit is obtained

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
obtaineduntilmultiplyingdigitssingledigitrepeatedly

Problem

I've written code that solves a problem from codewars.com. My code passes all the tests on the codewars site. It's a simple enough problem to solve naïvely, but I would love to improve the structure.

The prompt for the problem is as follows:


Write a function, persistence, that takes in a positive parameter num
and returns its multiplicative persistence, which is the number of
times you must multiply the digits in num until you reach a single
digit.


For example:

persistence(39) => 3  # Because 3*9 = 27, 2*7 = 14, 1*4=4
                      # and 4 has only one digit.


So my solution follows:

def get_digits(num, digits=[]):
    while num > 0:
        digits.append(num % 10)
        return get_digits(num / 10, digits)
    if num == 0:
        return digits

def multiply_all(digits, multiplier=1):
    while len(digits) > 0:
        multiplier = multiplier * digits.pop(0)
        return multiply_all(digits, multiplier)
    if len(digits) == 0:
        return multiplier

def persistence(num, count=0):
    while num >= 10:
        num = multiply_all(get_digits(num))
        count += 1
        return persistence(num, count)
    if num < 10:
        return count


I was thinking I might consolidate these three little recursive functions into one function containing all three... but I'd really like input from more seasoned programmers, so I very much appreciate you taking the time to make any suggestions.

Solution

Python 3.x bug

Did you know that your program outputs 1 for the number 39 if executed on Python 3?

This is because of the / division operator on this line:

return get_digits(num / 10, digits)


The meaning of / in Python 3.x was changed (PEP 238). Switching to // would be a quick fix.

Improving the solution


I was thinking I might consolidate these three little recursive
functions into one function containing all three..

What if we would get all the digits of a number by iterating over the string representation of an integer and converting every digit back to an integer - map(int, str(num)). In order to multiple the digits of a number we can use reduce() applying the operator.mul (multiplication operator) function:

from functools import reduce
from operator import mul

def persistence(number, count=0):
    # recursion base case - exit once the number is less than 10
    if number < 10:
        return count

    # get new number by multiplying digits of a number
    new_number = reduce(mul, map(int, str(number)))

    return persistence(new_number, count + 1)


Note that you don't actually need the while loop at all, since you are going recursive and have a num < 10 base case as a recursion "exit condition".

Note that you can also approach the "getting digits of a number" "mathematically" instead of converting the types back and forth:

from functools import reduce
from operator import mul

def get_digits(number):
    """Generates digits of a number."""
    while number:
        digit = number % 10

        yield digit

        # remove last digit from number (as integer)
        number //= 10

def persistence(number, count=0):
    if number < 10:
        return count

    # get new number by multiplying digits of a number
    new_number = reduce(mul, get_digits(number))

    return persistence(new_number, count + 1)


This would also be generally faster than the first version.

Code Snippets

return get_digits(num / 10, digits)
from functools import reduce
from operator import mul


def persistence(number, count=0):
    # recursion base case - exit once the number is less than 10
    if number < 10:
        return count

    # get new number by multiplying digits of a number
    new_number = reduce(mul, map(int, str(number)))

    return persistence(new_number, count + 1)
from functools import reduce
from operator import mul


def get_digits(number):
    """Generates digits of a number."""
    while number:
        digit = number % 10

        yield digit

        # remove last digit from number (as integer)
        number //= 10


def persistence(number, count=0):
    if number < 10:
        return count

    # get new number by multiplying digits of a number
    new_number = reduce(mul, get_digits(number))

    return persistence(new_number, count + 1)

Context

StackExchange Code Review Q#156769, answer score: 19

Revisions (0)

No revisions yet.