5
\$\begingroup\$

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 \92ドルk\$
  • 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.

Gareth Rees
50.1k3 gold badges130 silver badges210 bronze badges
asked May 21, 2015 at 0:08
\$\endgroup\$

2 Answers 2

6
\$\begingroup\$
  1. 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.

  2. 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.

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

  4. 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.

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

  6. 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.

  7. 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.

  8. 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.]

  9. 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))
answered May 21, 2015 at 8:46
\$\endgroup\$
2
  • \$\begingroup\$ Excellent answer as usual. Out of curiosity, any reason for using zip and count instead of enumerate with a start value ? I'd write sum(int(d) ** p for p, d in enumerate(str(n), p)) for instance. \$\endgroup\$ Commented Jun 10, 2015 at 10:38
  • \$\begingroup\$ @Josay: I forgot about the second argument to enumerate! \$\endgroup\$ Commented Jun 10, 2015 at 10:41
2
\$\begingroup\$

To make your code more pythonic we could modify the calculation of LHS in the following way:

''' calculate LHS '''
digits = [int(i) for i in list(str(n))]
tsum = 0
for i in digits:
 tsum += math.pow(i, p)
 p += 1

We use a list comprehension to build the digits list. The benefit being that later on when building up tsum we no longer have to call reversed on digits.

answered May 21, 2015 at 2:55
\$\endgroup\$
1
  • \$\begingroup\$ The call to list is not required. Also, you don't need to create the list digits, you just iterate over the string directly (or use a generator expression). \$\endgroup\$ Commented Jun 10, 2015 at 10:35

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.