product of digits as a single-digit number - python

I am trying to compute the product of non-zero digits,
until the result is a single-digit number. For example, the number 512983 is 2, because 5×1×2×9×8×3 = 2160 and 2×1×6 = 12 (note the omission of the zero digit) and 1 × 2 = 2. But every time I run this I only get 0
def prod_digits(n):
a = 1
for each in str(n):
a = a * int(each)
return a
prod_digits(123)

Try this:
def prod_digits(n):
a = 1
for each in str(n):
if each is not '0':
a = a * int(each)
return a
a = 123
while (a > 10):
a = prod_digits(a)
print a

There are multiple issues with your code
Since initial value of a is 0, any subsequent multiplications will give 0. Change it to 1.
Your function needs to be called recursively until the argument to function is a single digit.
You need to ignore 0 while multiplying digits.

Or you can use the reduce function from functools:
from functools import reduce
def compute_nz_product(n):
digits = [int(c) for c in str(n) if c is not '0']
result = reduce(lambda x, y: x*y, digits)
if result < 10:
return result
else:
return compute_nz_product(result)
print(compute_nz_product(512983))
Output:
2

Related

calculating the last non-zero digit of a factorial- why does my code pass some tests but fail others?

I am writing code to solve the following codewars question: https://www.codewars.com/kata/5f79b90c5acfd3003364a337/train/python
My idea is to take all the integers from 1 to n, and take the last digit of each of these integers (bar 0), multiply them together, and return the 'last' non-zero digit of the result:
def last_digit(n):
factorials = []
factorials_n = 1
for i in range(1,n + 1):
i = str(i)
i = i[::-1]
for j in i:
if j == "0":
break
factorials.append(j)
break
# at this point factorials contains the first non-zero integers of each integer in reverse
for i in factorials:
factorials_n = factorials_n * int(i)
factorials_n = str(factorials_n)
factorials_n = factorials_n[::-1]
for i in factorials_n:
if i != "0":
return int(i)
The code passes a number of tests, but fails for 387 (returns 6, should be 2) and 1673 (returns 2 should be 4). I've tried doing print statements as debug but the code seems fine, perhaps it's the logic that fails at some point- any ideas?
The problem here is with the logic. Since you are dropping all the cases where the number ends in 0, we do not arrive at the correct answer.
Consider 2 x 8 x 30. To get the last digit of the factorial, multiplying last digits would suffice, but to find the last non zero digit, you have to evaluate 2 x 8 x 3
instead.
Using this solution as a reference, here's what you can do:
def last_digit(n):
# factorials = []
# factorials_n = 1
last = 1
d2 = 0
for i in range(1,n + 1):
ii = i
print(ii)
while(ii%2==0):
d2 +=1
ii = ii/2
while(ii%5==0):
d2 -=1
ii = ii/5
print(d2)
last = (last * ii)%10
print(last)
for i in range(0,d2):
last = (last *2)%10
return int(last)
Your code passes the test cases for numbers uptill 24, it fails when the non-zero digit from 25! gives an incorrect answer which get propogated forward for the numbers after it.
And also we can simply use the modulo operator to get the very last digit instead of converting it into a string
Example: 1234 % 10 equals 4 (which is the last digit)
My solution:
def last_digit(n):
factorial = 1
for i in range(1, n + 1):
# compute the factorial
prod = factorial * i
# keep dividing the product by 10 until the last digit is a !0 digit
while prod % 10 == 0:
prod = prod // 10
# only store the last 3 digits of the computed product.
# You can keep more digits to get accurate results for
# when the numbers are higher than 10000
factorial = prod % 1000
# return the last digit
return factorial % 10
As i said earlier, when the last !0 digit from 24! (6) is multiplied with 25, it outputs 150 which after removing the 0 gives 5 but it instead should be 4. Hence, in order to solve this we keep at least the last 3 digits instead of only the last digit.
Example: 6 * 25 = 150 => 5 (last !0 digit)
936 * 25 = 23400 => 4 (last !0 digit)
PS: !0 = non-zero

Calculating the sum of the 4th power of each digit, why do I get a wrong result?

I am trying to complete Project Euler question #30, I decided to verify my code against a known answer. Basically the question is this:
Find the sum of all the numbers that can be written as the sum of fifth powers of their digits.
Here is the known answer I am trying to prove with python:
1634 = 1^4 + 6^4 + 3^4 + 4^4
8208 = 8^4 + 2^4 + 0^4 + 8^4
9474 = 9^4 + 4^4 + 7^4 + 4^4
As 1 = 1^4 is not a sum it is not included.
The sum of these numbers is 1634 + 8208 + 9474 = 19316.
When I run my code I get all three of the values which add up to 19316, great! However among these values there is an incorrect one: 6688
Here is my code:
i=1
answer = []
while True:
list = []
i=i+1
digits = [int(x) for x in str(i)]
for x in digits:
a = x**4
list.append(a)
if sum(list) == i:
print(sum(list))
answer.append(sum(list))
The sum of list returns the three correct values, and the value 6688. Can anybody spot something I have missed?
You are checking the sum too early. You check for a matching sum for each individual digit in the number, and 6 ^ 4 + 6 ^ 4 + 8 ^ 4 is 6688. That's three of the digits, not all four.
Move your sum() test out of your for loop:
for x in digits:
a = x**4
list.append(a)
if sum(list) == i:
print(sum(list))
answer.append(sum(list))
At best you could discard a number early when the sum already exceeds the target:
digitsum = 0
for d in digits:
digitsum += d ** 4
if digitsum > i:
break
else:
if digitsum == i:
answer.append(i)
but I'd not bother with that here, and just use a generator expression to combine determining the digits, raising them to the 4th power, and summing:
if sum(int(d) ** 4 for d in str(i)) == i:
answer.append(i)
You haven't defined an upper bound, the point where numbers will always be bigger than the sum of their digits and you need to stop incrementing i. For the sum of nth powers, you can find such a point by taking 9 ^ n, counting its digits, then taking the number of digits in the nth power of 9 times the nth power of 9. If this creates a number with more digits, continue on until the number of digits no longer changes.
In the same vein, you can start i at max(10, 1 + 2 ** n), because the smallest sum you'll be able to make from digits will be using a single 2 digit plus the minimum number of 1 and 0 digits you can get away with, and at any power greater than 1, the power of digits other than 1 and 0 is always greater than the digit value itself, and you can't use i = 1:
def determine_bounds(n):
"""Given a power n > 1, return the lower and upper bounds in which to search"""
nine_power, digit_count = 9 ** n, 1
while True:
upper = digit_count * nine_power
new_count = len(str(upper))
if new_count == digit_count:
return max(10, 2 ** n), upper
digit_count = new_count
If you combine the above function with range(*<expression>) variable-length parameter passing to range(), you can use a for loop:
for i in range(*determine_bounds(4)):
# ...
You can put determining if a number is equal to the sum of its digits raised to a given power n in a function:
def is_digit_power_sum(i, n):
return sum(int(d) ** n for d in str(i)) == i
then you can put everything into a list comprehension:
>>> n = 4
>>> [i for i in range(*determine_bounds(n)) if is_digit_power_sum(i, n)]
[1634, 8208, 9474]
>>> n = 5
>>> [i for i in range(*determine_bounds(n)) if is_digit_power_sum(i, n)]
[4150, 4151, 54748, 92727, 93084, 194979]
The is_digit_power_sum() could benefit from a cache of powers; adding a cache makes the function more than twice as fast for 4-digit inputs:
def is_digit_power_sum(i, n, _cache={}):
try:
powers = _cache[n]
except KeyError:
powers = _cache[n] = {str(d): d ** n for d in range(10)}
return sum(powers[d] for d in str(i)) == i
and of course, the solution to the question is the sum of the numbers:
n = 5
answer = sum(i for i in range(*determine_bounds(n)) if is_digit_power_sum(i, n))
print(answer)
which produces the required output in under half a second on my 2.9 GHz Intel Core i7 MacBook Pro, using Python 3.8.0a3.
Here Fixed:
i=1
answer = []
while True:
list = []
i=i+1
digits = [int(x) for x in str(i)]
for x in digits:
a = x**4
list.append(a)
if sum(list) == i and len(list) == 4:
print(sum(list))
answer.append(sum(list))
The bug I found:
6^4+6^4+8^4 = 6688
So I just put a check for len of list.

Working with Python Arrays

What is wrong with this code please:
from array import array
import math
def solution(A):
A = array('i')
for i in A:
if i > 0:
digits = int(math.log10(i))+1
elif i == 0:
digits = 1
else:
digits = int(math.log10(-i))+2
if digits == 2:
sum += i
return sum
The task is to write a function that given an array A consisting of N integers, returns the sum of all two digit numbers
This will do the job
import math
def solution(A):
#A = array('i')
sumofarr=0
for i in A:
if i != 0:
digits = int(math.log10(math.fabs(i)))+1
if digits == 2:
sumofarr += i
return sumofarr
solution([12,3,45]) #output 57
Note that there is no need to separate between positive and negative numbers. Just take the absolute value. Also, you need to initialize the sumofarr variable at the beginning. Also it is better not to use sum as a name for variable, as this is already used as a name of function in python.
the problem with your code is that you don't initialize sum, don't have the correct indentation, overwrite the input argument and the check if a number is of 2 digit is more complicate that it need to be
here is a more simple version
def mysum(A):
total = 0
for i in A:
if 10 <= abs(i) < 100: # abs if you want to include negative numbers
total += i
return total
test
>>> test = [1, 2, 10, 80, 20, -10, -20, 500]
>>> mysum(test)
80
or with the build-in sum and a generator expression
>>> sum( i for i in test if 10 <= abs(i) < 100 )
80
>>>

Digital Root without loops Python

def digit_sum(n):
'''(int)->number
Returns the sum of all the digits in the given integer, n'''
if n<10:
return n
return n%10 + digit_sum(n//10)
def digital_root(n):
'''(int)->number
Returns the resulting sum of the digits in the given integer until it reaches a single digit number; via digit_sum'''
while n>9:
n=sum(digit_sum(n))
return n
Wrote the code for digit_sum and then used recursion to write digital_root. How would I go about this? Any help is appreciated!
Wikipedia lists a simple O(1) formula for the digital root:
def digit_root(n):
return (n - 1) % 9 + 1
This does not take into account an input less than 1, so you can modify as follows, with the assumption that the input is a whole number:
def digit_root(n):
return (n - 1) % 9 + 1 if n else 0
Examples:
>>> digit_root(1)
1
>>> digit_root(11)
2
>>> digit_root(235)
1
So the idea is that you have to use recursion for the last one as well? In that case, this should do the job:
def digital_root(n):
if n < 10:
return n
return digital_root(digit_sum(n))
Try this:
def num(n) :
sum = 0 #provided sum as 0
for i in str(n):
a = int(n) % 10 #taken variable a
sum = sum + a #put the sum.
n = n/10
print(sum)
if sum < 10:
print(str(sum) + "=this is a digital root")
else:
num(sum)
please try the following code:
def dgtl_rt(n):
return n%9 or n and 9
print(dgtl_rt(123))
def droot(a):
while a>=10:
b =int(a/10) #other digit(s)
c =a-(b*10) #the rightmost digit
a =b+c #add together
return a
I have not seen this solution anywhere, so I thought it would be nice to share, i discovered it myself! Just 1 looping structure.
{def MDR(n):
'''
this function returns multiplicative digital root.
'''
count, mdr = 0, n
while mdr > 9:
m, digitsMul = mdr, 1
while m:
m, md = divmod(m, 10)
digitsMul *= md
mdr = digitsMul
count += 1
return count, mdr}

Adding the digits of an int and Python

import sys
def keepsumming(number):
numberlist = []
for digit in str(number):
numberlist.append(int(digit))
total = reduce(add, numberlist)
if total > 9:
keepsumming(total)
if total <= 9:
return total
def add(x,y):
return x+y
keepsumming(sys.argv[1])
I want to create a function that adds the individual digits of any number, and to keep summing digits until the result is only one digit. (e.g. 1048576 = 1+0+4+8+5+7+6 = 31 = 3+1 = 4). The function seems to work in some laces but not in others. for example:
$python csp39.py 29
returns None, but:
$python csp39.py 30
returns 3, as it should...
Any help would be appreciated!
As others have mentioned, the problem seems to be with the part
if total > 9:
keepsumming(total) # you need return here!
Just for completeness, I want to present you some examples how this task could be solved a bit more elegantly (if you are interested). The first also uses strings:
while number >= 10:
number = sum(int(c) for c in str(number))
The second uses modulo so that no string operations are needed at all (which should be quite a lot faster):
while number >= 10:
total = 0
while number:
number, digit = divmod(number, 10)
total += digit
number = total
You can also use an iterator if you want to do different things with the digits:
def digits(number, base = 10):
while number:
yield number % base
number //= base
number = 12345
# sum digits
print sum(digits(number))
# multiply digits
from operator import mul
print reduce(mul, digits(number), 1)
This last one is very nice and idiomatic Python, IMHO. You can use it to implement your original function:
def keepsumming(number, base = 10):
if number < base:
return number
return keepsumming(sum(digits(number, base)), base)
Or iteratively:
def keepsumming(number, base = 10):
while number >= base:
number = sum(digits(number, base))
UPDATE: Thanks to Karl Knechtel for the hint that this actually is a very trivial problem. It can be solved in one line if the underlying mathematics are exploited properly:
def keepsumming(number, base = 10):
return 1 + (number - 1) % (b - 1)
There's a very simple solution:
while number >= 10:
number = sum(divmod(number, 10))
I am fairly sure you need to change
if total > 9:
keepsumming(total)
into
if total > 9:
return keepsumming(total)
As with most recursive algorithms, you need to pass results down through returning the next call.
and what about simply converting to string and summing?
res = 1234567
while len(str(res)) > 1 :
res = sum(int(val) for val in str(res))
return res
Thats's what I use to do :)
Here is a clean tail-recursive example with code that is designed to be easy to understand:
def keepsumming(n):
'Recursively sum digits until a single digit remains: 881 -> 17 -> 8'
return n if n < 10 else keepsumming(sum(map(int, str(n))))
Here you go:
>>> sumdig = (lambda recurse: (lambda fix: fix(lambda n: sum(int(c) for c in str(n)))) (recurse(lambda f, g: (lambda x: (lambda d, lg: d if d == lg else f(f,g)(d))(g(x),x)))))(lambda f: lambda x: f(f,x))
>>> sumdig(889977)
3
You are sure to get full, if not extra, credit for this solution.
Your code as currently written need to replace the recursive call to keepsumming(total) with return keepsumming(total); python does not automatically return the value of the last evaluated statement.
However, you should note that your code has redundancies.
for digit in str(number):
numberlist.append(int(digit))
total = reduce(add, numberlist)
should become
from operator import add
total = reduce(add, (int(digit) for digit in str(number)))
A code golf-able version that doesn't require a while loop, or recursion.
>>> import math
>>> (lambda x: sum(int((x * 10 ** -p) % 10) for p in range(math.ceil(math.log(x, 10)))))(1048576)
31
This is probably what I'd use though.
def sum_digits(number):
return sum(
int(number * (10 ** -place) % 10)
for place in range(math.ceil(math.log(number, 10)))
)
It's easier to see what's going on if you append to a list instead of summing the integer values.
def show_digits(number):
magnitude = int(math.log(number, 10))
forms = []
for digit_place in range(magnitude + 1):
form = number * (10 ** -digit_place) # force to one's place
forms.append(form)
return forms
>>> show_digits(1048576)
[1048576, 104857.6, 10485.76, 1048.576, 104.8576, 10.48576, 1.048576]
For keepsumming that takes a string:
def keepsumming(number):
return number if len(number) < 2 \
else keepsumming(str(sum(int(c) for c in number)))
For keepsumming that takes a number:
def keepsumming(number):
return number if number < 10 \
else keepsumming(sum(int(c) for c in str(number)))

Categories

Resources