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

Sum of successive powers of digits

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

Problem

Some numbers have funny properties. For example:

$$\eqalign{89 &→ 8^1 + 9^2 &= 89 &= 89 × 1 \\ 695 &→ 6^2 + 9^3 + 5^4 &= 1390 &= 695 × 2 \\ 46288 &→ 4^3 + 6^4 + 2^5 + 8^6 + 8^7 &= 2360688 &= 46288 × 51}$$

Given a positive integer \$n\$ written in decimal as \$abcd\dotso\$, and a positive integer \$p\$, we want to find a positive integer \$k\$, if it exists, such that the sum of the digits of \$n\$ taken to the successive powers \$p\$, \$p+1\$, \$p+2\$, …, is equal to \$nk\$. In other words:


Is there an integer \$k\$ such that \$ a^p + b^{p+1} + c^{p+2} + d^{p+3} + \dotsb = nk\$?

If it is the case we will return \$k\$, if not return \$-1\$.

Note: \$n\$, \$p\$ will always be given as strictly positive integers.

  • dig_pow(89, 1) should return \$1\$ since \$8^1 + 9^2 = 89 = 89 × 1\$.



  • dig_pow(92, 1) should return \$-1\$ since there is no \$k\$ such that \$9^1 + 2^2\$ equals \$92k\$



  • dig_pow(695, 2) should return \$2\$ since \$6^2 + 9^3 + 5^4 = 1390 = 695 × 2\$



  • dig_pow(46288, 3) should return \$51\$ since \$4^3 + 6^4 + 2^5 + 8^7 + 8^6 = 2360688 = 46288 × 51\$



import math

def dig_pow(n,p):

    ''' 
    Formula :

    (a ^ p + b ^ (p+1) + c ^(p+2) + d ^ (p+3) + ...) = n * k

    '''

    ''' calculate LHS '''
    digits, temp = [], n
    for i in range(len(str(n))):
        digits.append(temp%10)
        temp = temp / 10

    Tsum = 0
    for i in reversed(digits):
        Tsum = Tsum + math.pow(i,p)
        p = p + 1

    ''' Calculate RHS '''
    if Tsum % n == 0:
        return Tsum / n
    else:
        return -1

print dig_pow(46288, 3)
print dig_pow(89, 1)
print dig_pow(695, 2)
print dig_pow(92, 1)


Please feel free to suggest better/faster approaches.

Solution

-
The name could be more meaningful. It's usually a good idea to name a function after its result, which in this case is the multiplier \$k\$, so something like digit_power_multiplier is needed.

-
The docstring doesn't explain the arguments to the function, or what it returns. Write it from the point of view of a user who is trying to figure out how to call it.

-
Only the first string in a function definition becomes the docstring: the others are ignored. So make them comments instead.

-
Returning exceptional values to indicate failure (here, -1 when no \$k\$ is found) is risky: the caller might forget to check for the exceptional value. It's more reliable to raise an exception.

-
Python has an exponentiation operator ** so there is no need to use math.pow.

-
When splitting n into its digits you use len(str(n)) to find the number of digits. But if you're going to call str(n) you may as well extract the digits from the result, as suggested by vertere in another answer.

-
The division operator / changed its meaning in Python 3: it's now floating-point division. To make your code portable to Python 3, use the floor division operator // when you want the integer part of the quotient.

-
When simultaneously iterating over a sequence and an index, use enumerate. So instead of:

Tsum = 0
for i in digits:
    Tsum = Tsum + math.pow(i,p)
    p = p + 1


write:

Tsum = 0
for q, d in enumerate(digits, p):
    Tsum = Tsum + d ** q


[Thanks to Josay in comments for improving this.]

-
Take advantage of the sum function and write:

Tsum = sum(d ** q for q, d in enumerate(digits, p))


Putting all this together:

def digit_power_multiplier(n,p):
    """Given a positive integer n with decimal digits a, b, c, d, ..., and
    a positive integer p, return k such that:

        a ** p + b ** (p+1) + c ** (p+2) + d ** (p+3) + ... = n * k

    If there is no such k, raise ValueError. For example:

        >>> digit_power_multiplier(695, 2)
        2

    since 6**2 + 9**3 + 5**4 = 1390 = 695 * 2.

    """
    lhs = sum(int(d) ** q for q, d in enumerate(str(n), p))
    if lhs % n == 0:
        return lhs // n
    else:
        raise ValueError("no k such that {} = {}*k".format(lhs, n))

Code Snippets

Tsum = 0
for i in digits:
    Tsum = Tsum + math.pow(i,p)
    p = p + 1
Tsum = 0
for q, d in enumerate(digits, p):
    Tsum = Tsum + d ** q
Tsum = sum(d ** q for q, d in enumerate(digits, p))
def digit_power_multiplier(n,p):
    """Given a positive integer n with decimal digits a, b, c, d, ..., and
    a positive integer p, return k such that:

        a ** p + b ** (p+1) + c ** (p+2) + d ** (p+3) + ... = n * k

    If there is no such k, raise ValueError. For example:

        >>> digit_power_multiplier(695, 2)
        2

    since 6**2 + 9**3 + 5**4 = 1390 = 695 * 2.

    """
    lhs = sum(int(d) ** q for q, d in enumerate(str(n), p))
    if lhs % n == 0:
        return lhs // n
    else:
        raise ValueError("no k such that {} = {}*k".format(lhs, n))

Context

StackExchange Code Review Q#91351, answer score: 6

Revisions (0)

No revisions yet.