Find the sum of all multiples of n below m
Keep in Mind n and m are natural numbers (positive integers) m is
excluded from the multiples
sumMul(2, 9) ==> 2 + 4 + 6 + 8 = 20
sumMul(3, 13) ==> 3 + 6 + 9 + 12 = 30
sumMul(4, -7) ==> "INVALID"
I did sum of list using range(n, m, n) using n as step.
I also tried modulus to avoid range 3 args error.
I can pass many tests but cannot pass all of them.
I have tried lots of logic but to no avail. What I am doing wrong?
CODEWARS: https://www.codewars.com/kata/57241e0f440cd279b5000829/train/python
MY CODE:
def sum_mul(n, m):
my_list = [number for number in range(n, m) if number % n == 0]
sum_list = sum(my_list)
if sum_list >= 1:
return sum_list
elif n == 0 and m == 0:
return 'INVALID'
elif n == m:
return n - m
elif n > m:
return 'INVALID'
Your code fails if n == 0 as then the number % n checks in the list comprehension fail, so you should check that before trying to compute the sum. Also, you could use a range with step and just do sum(range(n, m, n)). However, both ways might be too slow for some test cases with very large m.
You can do this in O(1) with the following observations:
there are (m-1) // n multiples of n below m
the sum of natural numbers from 1 to n is n*(n+1)//2
Combine those two to get the result.
Example for sumMul(3, 13) ==> 3 + 6 + 9 + 12 = 30:
(13-1) // 3 == 4 so we know there are 4 multiples of 3 below 13
those are 3 + 6 + 9 + 12 == 3 * (1 + 2 + 3 + 4)
with (2) we know 1 + 2 + 3 + 4 == 4*5//2 == 10
so the result is 10 * 3 == 30
Putting that into code and handling the special cases is left as an exercise to the interested reader.
You have one main problem, that is you should prevent the situation when n==0 and you divide it in your list comprehension. It will raise zero division error. so you should check before the validation that n is not equal to zero.
Second thing is that you need to check whether n or m are negatives, as the exercise declared both n and m should be positives.
def sum_mul(n, m):
if n==0:
return 'INVALID'
my_list = [number for number in range(n, m) if number % n == 0]
sum_list = sum(my_list)
if sum_list >= 1:
return sum_list
elif n < 0 and m <= 0:
return 'INVALID'
elif n == m:
return n - m
elif n > m:
return 'INVALID'
You can just compute that result mathematically using integer divisions:
def sum_mul(n, m):
if n<1 or n>m: return "INVALID"
return m//n*(m//n+1)//2*n
First you get the number of multiple of n in m (which is merely dividing m by n ignoring the remainder) : m//n
Multiples of n will be nx1, nx2, nx3, ... up to the number of multiples. Factorizing the sum of those by n we get: n(1+2+3+ ... m//n).
The sum of numbers from 1 up to a given number x is obtained by x(x+1)/2. In this case x is the number of multiples m//n
Putting it all together we get n * x * (x+1) /2 where x is m//n, so:
n * (m//n) * (m // n + 1) // 2
You should comprove all cases before call sum function.
Like this:
def sum_mul(n, m):
if n == 0 or m == 0:
return 'INVALID'
if n == m:
return n - m
if n<0 or m<0:
return 'INVALID'
my_list = [number for number in range(n, m) if number % n == 0]
return sum(my_list)
In fact, you dont't need to create if elif structure because you are using returns, so next instruction after return not executed.
Related
I am trying to solve the following problem:
Your task is to construct a building which will be a pile of n cubes. The cube at the bottom will have a volume of n^3, the cube above will have volume of (n-1)^3 and so on until the top which will have a volume of 1^3.
You are given the total volume m of the building. Being given m can you find the number n of cubes you will have to build?
The parameter of the function findNb (find_nb, find-nb, findNb) will be an integer m and you have to return the integer n such as n^3 + (n-1)^3 + ... + 1^3 = m if such a n exists or -1 if there is no such n.
Examples:
findNb(1071225) --> 45
findNb(91716553919377) --> -1
Here is my code:
def find_Nb(m):
n = 1
while n <= m // 2:
total_n = 0
for i in range(0, n):
total_n += (n - i) ** 3
n += 1
if total_n == m:
return n
break
else:
return -1
For some reason, the output produces (n+1) in the case where total_n ==m.
So for example, if we do m = 100, the output should be 4; but the above code produces 5; any idea why this is?
Or as per the example, when I run m=1071225 into the code I should get an output of 45, but my code produces 46.
this is the code that I wrote and it works but when I first tried it I had the same problem as you. I fixed it by putting the n = i before i+=1 so that n has the right value so I think you too have to make a copy of n before you say n += 1 and to return that value.
def findN(m):
sum = 0
i = 1
while sum < m:
sum += i ** 3
n = i
i += 1
if sum == m:
return n
else:
return -1
print(findN(1071225))
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.
I have to calculate the sum of all the multiples of 3 and 5 (including themselves) in a range of a given N number (N excluded). I created a python code and it works with N = 10 but doesn't work for N = 100. I don't understand why.
This is the code:
#!/bin/python3
import sys
def multiples_sum(n):
sum1 = 0
for i in range(n):
if i % 3 == 0:
sum1 = sum1 + i
if i % 5 == 0:
sum1 = sum1 + i
return sum1
t = int(input().strip())
for a0 in range(t):
n = int(input().strip())
print(multiples_sum(n))
you are counting the multiples of 15 (= 3 * 5) twice.
your code should be
for i in range(n):
if i % 3 == 0:
sum1 += i
elif i % 5 == 0:
sum1 += i
note the elif instead of if.
alternatively:
for i in range(n):
if i % 3 == 0 or i % 5 == 0:
sum1 += i
or directly (as DeepSpace suggests in the comments)
sum1 = sum(i for i in range(n) if 0 in {i % 3, i % 5})
note that there is no need for looping at all: knowing that the sum of the integers from 1 to (and including) n is
def sum_to(n):
return ((n+1)*n)//2
you can get your number from:
sum1 = 5 * sum_to((n-1)//5) + 3 * sum_to((n-1)//3) - 15 * sum_to((n-1)//15)
(that could be generalized and made somewhat prettier... but i'm sure you get the idea in this form).
EDIT:
In the meantime I saw #DeepSpace's solution in the comments, particularly the one with the if 0 in {i % 3, i % 5})-check - which I definitely admire; this is really smart!
I think I'd go this way, if you're interested in a different approach:
N = 100
sum(set(list(range(0, N, 3)) + list(range(0, N, 5))))
# 2318
Because 10 is less than 15, which is the least common multiple of 3 and 5.
You need to treat the particular case of 15.
This is my implementation, but it not efficient when given 6 digit number.
Input : n = 2
Output : 9009
9009 is the largest number which is product of two
2-digit numbers. 9009 = 91*99.
def isPali(x):
n = str(x)
for i in range(len(n)):
if not n[i] == n[-i-1]:
return False
return True
def isProduct(x,A):
counter = A
while counter > 1:
if x // counter <= A and x % counter == 0:
return True
else:
counter-=1
return False
def largestProduct(A):
for i in range(A*A,1,-1):
if isPali(i) and isProduct(i,A):
return i
return False
largestProduct(999999)
Let x and y be the two n-digit factors of the palindrome number.
You can iterate over them in a descending number.
Key is to stop as soon as possible, which mean, once a first solution has been found, you don't check any product below that solution.
def get_max_palindrome(n):
res = 0
for x in range(10 ** n - 1, 1, -1):
for y in range(10 ** n - 1, 1, -1):
p = x * y
if res > p:
break
if str(p) == str(p)[::-1]:
res = p
break
if (x - 1) ** 2 < res:
break
return res
print(get_max_palindrome(6))
Exec in 0.378s on my laptop.
Codewise, this is not too difficult:
n = 999999
max_pali =0
t = ()
for i in range(1,n+1):
for j in range(i,n+1):
m = i*j
s = str(m)
if s == s[::-1] and m > max_pali:
max_pali = m
t = (i,j)
print(max_pali,t)
However, this is a brute force approach. For numbers with 6 digits, this will not terminate in a reasonable amount of time. Even if it will, I could ask you the same question for 7 or 42 digits. I suggest you look for some structure, or property, of those numbers whose multiple is a palindrome. Could such a pair be any pair of numbers? Is the case 91*99 = 9009 a mere coincidence, or is there a pattern?
Write a recursive algorithm which enumerates dominant primes. Your algorithm should print dominant primes as it finds them (rather than at the end).By default we limit the dominant primes we are looking for to a maximum value of 10^12, the expected run time should be around or less than a minute.
The following is my python code which doesn't work as expected:
import math
def test_prime(n):
k = 2
maxk = int(math.sqrt(n))
while k <= maxk:
if n % k == 0:
return False
if k == 2:
k += 1
else:
k += 2
return (n is not 1)
def dominant_prime_finder(maxprime=10**12,n=1):
l = 1 #length of the current number
while n // 10 > 0:
l += 1
n //= 10
if test_prime(n) == True:
is_dominant_prime = True
index_smaller = n
while l > 1 and index_smaller > 9:
index_smaller //= 10
if test_prime(index_smaller) == False:
is_dominant_prime = False
break
for i in range(1,10):
if test_prime(i*10**l + n) == True:
is_dominant_prime = False
break
if is_dominant_prime == True:
print(n)
while n <= maxprime:
dominant_prime_finder()
You can solve the problem without enumerating all the numbers under 10^12 which is inefficient by doing a recursion on the length of the number.
It works the following way:
The prime number of length 1 are: 2,3,5,7.
For all these numbers check the third condition, for any digit dn+1∈{1,…,9} , dn+1dn…d0 is not prime. For 2 it's okay. For 3 it fails (13 for instance). Store all the prime you find in a list L. Do this for all the prime of length 1.
In L you now have all the prime number of length 2 with a prime as first digit, thus you have all the candidates for dominant prime of length 2
Doing this recursively gets you all the dominant prime, in python:
def test_prime(n):
k = 2
maxk = int(math.sqrt(n))
while k <= maxk:
if n % k == 0:
return False
if k == 2:
k += 1
else:
k += 2
return (n is not 1)
def check_add_digit(number,length):
res = []
for i in range(1,10):
if test_prime( i*10**(length) + number ):
res.append(i*10**(length) + number)
return res
def one_step(list_prime,length):
## Under 10^12
if length > 12:
return None
for prime in list_prime:
res = check_add_digit(prime,length)
if len(res) == 0:
#We have a dominant prime stop
print(prime)
else:
#We do not have a dominant prime but a list of candidates for length +1
one_step(res,length+1)
one_step([2,3,5,7], length=1)
This works in under a minute on my machine.
Well, there are several issues with this code:
You modify the original n at the beginning (n //= 10). This basically causes n to always be one digit. Use another variable instead:
m = n
while m // 10 > 0:
l += 1
m //= 10
Your recursive call doesn't update n, so you enter an infinite loop:
while n <= maxprime:
dominant_prime_finder()
Replace with:
if n <= maxprime:
dominant_prime_finder(maxprime, n + 1)
Even after fixing these issues, you'll cause a stack overflow simply because the dominant prime numbers are very far apart (2, 5, 3733, 59399...). So instead of using a recursive approach, use, for example, a generator:
def dominant_prime_finder(n=1):
while True:
l = 1 #length of the current number
m = n
while m // 10 > 0:
l += 1
m //= 10
if test_prime(n):
is_dominant_prime = True
index_smaller = n
while l > 1 and index_smaller > 9:
index_smaller //= 10
if not test_prime(index_smaller):
is_dominant_prime = False
break
for i in range(1,10):
if test_prime(i*10**l + n):
is_dominant_prime = False
break
if is_dominant_prime:
yield n
n = n + 1
g = dominant_prime_finder()
for i in range(1, 10): # find first 10 dominant primes
print(g.next())
This problem is cool. Here's how we can elaborate the recurrence:
def dominant_prime_finder(maxprime=10**12):
def f(n):
if n > maxprime:
return
is_dominant = True
power = 10**(math.floor(math.log(n, 10)) + 1)
for d in xrange(1, 10):
candidate = d * power + n
if test_prime(candidate):
f(candidate)
is_dominant = False
if is_dominant:
print int(n)
for i in [2,3,5,7]:
f(i)