Reduce runtime when finding permutations - python

I am working on one of Google's FooBar challenges for fun and running into a Time Limit Exceeded error when executing it in Google's run time environment. My solution runs fine - and to my eyes quickly - in my local development environment.
This is my code:
from itertools import permutations
def answer(l):
options = []
for r in range(1, len(l) + 1):
for p in permutations(l, r=r):
n = int(''.join(map(str, p)))
if n % 3 == 0:
options.append(n)
numbers = sorted(options, reverse=True)
return numbers[0]
l = [3, 1, 4, 1, 5, 9]
#l = [3, 1, 4, 1]
print(answer(l))
The goal is to find the largest number that is divisible by 3 from the list of numbers that is passed in.
The output of the two examples should be:
[3, 1, 4, 1, 5, 9] => 94311
[3, 1, 4, 1] => 4311
Based on the comments to generate the permutations from largest to smallest (instead of smallest to largest) and then breakout, I've modified the code. Against, it works in the local environment but the Google runtime says that the timelimit has been exceeded:
def answer(l):
options = []
l = sorted(l, reverse=True)
for r in range(len(l) + 1, 1, -1):
for p in permutations(l, r=r):
n = int(''.join(map(str, p)))
if n % 3 == 0:
return n
return 0
I am sorting the input list based on the permutations docs that say the tuples will be sorted if the input is sorted. It then, since it should be sorted, the first time it finds a value divisible by 3, that will be the higest value
As I said, my code (both versions) works. But, I seem to be running longer than Google expects. How can I reduce the run time of the above code?

The highest number will have the most number of digits. So for a
list of size n, the search should start from n and continue to n-1,
n-2...
The numbers divisible by 3 will always be in the solution. For
example 2514 is divisible by 3 so is 32514 or 35314. Therefore you can reduce
the search to the digits that are not divisible by 3.
For a list of n digits that are not divisible by 3 (n>=3), you can
get a number divisible by 3 by removing at most 2 digits. This is because the summation will have the remainder 1 or 2. If it is 1, in the worst case you can remove 2 digits with a remainder of 2. If it is 2, again in the worst case you can remove 2 digits with a remainder of 1.
Now the algorithm:
You have a list of numbers:
divisible = [i for i in numbers if i % 3 == 0]
candidate_list = [i for i in numbers if i % 3 != 0]
If the summation of candidate_list is divisible by 3, then you have the answer. If not, look at the remainder:
remainder = sum(candidate_list) % 3
If the remainder is 1, we are going to search for 1, 4, or 7 in the candidate list. If it is 2, the numbers will be 2, 5, and 8. If we find a number, we are going to remove that from the list and the sum of the remaining digits will be divisible by three.
if remainder!=0:
for i in range(3):
if (remainder + i*3) in candidate_list:
candidate_list.remove(remainder + i*3)
return candidate_list
This will start the search from the smallest digit and break out of the loop when a digit is found. If not, we will search for two digits instead of 1.
counter = 0
for candidate in candidate_list:
if candidate % 3 + remainder == 3:
candidate_list.remove(candidate)
counter += 1
if counter > 1:
return candidate_list
Overall, you'll have something like this:
numbers = [3, 1, 4, 1, 5, 9, 0, 2, 4, 7, 9, 1, 3]
divisible = [i for i in numbers if i % 3 == 0]
def search(numbers):
candidate_list = sorted([i for i in numbers if i % 3 != 0])
remainder = sum(candidate_list) % 3
if remainder!=0:
for i in range(3):
if (remainder + i*3) in candidate_list:
candidate_list.remove(remainder + i*3)
return candidate_list
counter = 0
for candidate in candidate_list:
if candidate % 3 + remainder == 3:
candidate_list.remove(candidate)
counter += 1
if counter > 1:
return candidate_list
else:
return candidate_list
candidate_list = search(numbers)
fin = int(''.join(map(str, sorted(divisible + candidate_list, reverse=True))))

Related

How could I call hundreds in python?

When I split certain numbers into "ones" and "tens" in python, like that (98) we code >>>
num = 98
ones = num % 10
tens = num // 10
print(ones) = 8
print(tens) = 9
So, if I have 3 digits number like 321 (based on the code have shown previously) the ones will be 1, tens = 32 . and I need python to execute 3 and 2 separately !!
First, you can use divmod:
tens, ones = divmod(98, 10)
tens
# 9
ones
# 8
Second, you could write a generic function to get all digits regardless of magnitude:
def digs(n):
while n:
n, dig = divmod(n, 10)
yield dig
ones, tens, hundreds, *_ = digs(321)
It will always produce all digits starting with ones:
[*digs(12345)]
# [5, 4, 3, 2, 1]
Of course, the simplest is just string conversion:
[*map(int, str(12345))]
# [1, 2, 3, 4, 5]
This is a generic Python code to split the digits (ones, tens, hundreds, and so on)
num = int(input())
count = 0
while (num != 0):
mod = num % 10
div = num // 10
count = count + 1
print ("The number in position", count, " is ", mod)
num = div
This code works by repeated division.
You can write a function doing the modulo operations, put those in a list, make the division by then and repeat until the end, reverse the list:
def split_number(number):
result = []
while number != 0:
result.append(number % 10)
number = number // 10
result.reverse()
return result
split_number(12345)
[1, 2, 3, 4, 5]
like this :
321 / 100
3.21
>>> int(3.21)
3
But you have already made a better thing for 10 why you don't make it for 100?

how to find divisors of a given number in python and append them to a list?

I'm trying to find all divisors of a given number and append them to a list. However, when I try to append them, the program also appends the number it iterates through and the proper divisors are appended in the end of the list. How can I avoid appending incorrect divisors? my code and output is below:
n=12
lis=[]
for i in range(2, (n+1) ):
for j in range(1,i):
if i % j == 0:
lis.append(j)
my output was: [1, 1, 1, 2, 1, 1, 2, 3, 1, 1, 2, 4, 1, 3, 1, 2, 5, 1, 1, 2, 3, 4, 6]
How can I get the following output: [1, 2, 3, 4, 6]
This code is seriously flawed. i % j is really an unrelated check. If I try to create an answer close to yours, this will work:
n=12
lis=[1]
for i in range(2, (n) ):
for j in range(1,i):
if i * j == n:
lis.append(i)
lis.append(j)
Which you can then sort afterward. But this inner loop is bad: it tells you no more information than n % i == 0 would and it does so much more slowly. A better answer which does not involve nesting loops would be:
n=12
lis=[1]
for i in range(2, n):
if n % i == 0:
lis.append(i)
Of course, this can be improved further: notice that we do not need to check any higher than half of n because no value greater than n / 2 will divide n evenly. In the best case, if you append i and n // i at the same time, you only have to check i from 2 to the square root of n.
I've found the below solution
def sumPdivisors():
n=int(input('Enter a number to find the sum of its divisors: '))
lis=[]
for i in range(2, (n+1) ):
pass
for j in range(1,i):
if i % j == 0:
lis.append(j)
print("the sum of the proper divisors of {} if {}".
format(n,sum(lis)))
sumPdivisors()
As it stands you are comparing the following
2%1
3%1
3%2
4%1
4%2
4%3
5%1
5%2
5%3
5%4
6%1
6%2
6%3
6%4
6%5
7%1
7%2
7%3
7%4
7%5
7%6
8%1
8%2
8%3
8%4
8%5
8%6
8%7
9%1
9%2
9%3
9%4
9%5
9%6
9%7
9%8
10%1
10%2
10%3
10%4
10%5
10%6
10%7
10%8
10%9
11%1
11%2
11%3
11%4
11%5
11%6
11%7
11%8
11%9
11%10
12%1
12%2
12%3
12%4
12%5
12%6
12%7
12%8
12%9
12%10
12%11
You can see that your script is comparing numbers that have nothing to do with 12.
Edit:
I just added print statements to your code.
n=12
lis=[]
for i in range(2, (n+1) ):
for j in range(1,i):
print("{}%{}".format(i,j))
if i % j == 0:
lis.append(j)

Why does this produce an assertionerror even though it's exactly what was taught?

I'm completing something for Coursera, and this is the code I made:
def getFactors(x):
"""Returns a list of factors of the given number x.
Basically, finds the numbers between 1 and the given integer that divide the number evenly.
For example:
- If we call getFactors(2), we'll get [1, 2] in return
- If we call getFactors(12), we'll get [1, 2, 3, 4, 6, 12] in return
"""
factors=[]
for i in range(1,x+1):
if x%1==0:
factors.append(i)
print(factors)
Yet,
num = 2
factors_test = [1, 2]
factors = getFactors(num)
assert_equal(factors_test, factors, str(factors) + ' are not the factors of ' + str(num))
num = 12
factors_test = [1, 2, 3, 4, 6, 12]
factors = getFactors(num)
assert_equal(factors_test, factors, str(factors) + ' are not the factors of ' + str(num))
num = 13
factors_test = [1, 13]
factors = getFactors(num)
assert_equal(factors_test, factors, str(factors) + ' are not the factors of ' + str(num))
# test existence of docstring
assert_true(len(getFactors.__doc__) > 1, "there is no docstring for getFactors")
print("Success!")
I get an assertionerror:
AssertionError: [1, 2] != None : None are not the factors of 2
As others have noted, you don't have a return value from the getFactors() function. Add this line to the end:
return factors
Once you do that, you'll maybe notice another bug. You are testing for the number modulus 1 == 0, which isn't quite right. You probably meant to be testing for modulus i. You'd have to replace this line:
if x%1==0:
with this line:
if x%i==0:
It's hard to see, but the first one uses the digit 1, and the second the letter i.

Calculating the least amount of numbers needed from an array to sum to a specific number

I am having an issue where I have been given an array of numbers [1,3,5] and need to find the least amount of numbers that could add to a specific number. Each number has a weight and I need to calculate the most efficient. For example if the number was 6 I would need to use [5,1] instead of [3,3] as 5 has a greater importance. In the case of 12 it would be [5,5,1,1] instead of [3,3,3,3]
I have already tried implementing dictionaries and arrays but the problem solving part is what I am having trouble with.
A valid way to do it, not relying on the presence of 1 in the list, is to try to use as many of the largest numbers as possible, and recursively try to obtain the remainder:
If no solution can be found, the function will return None
def solve(numbers, target):
'''Return a list of the largest of numbers whose sum is target,
None if impossible'''
if not numbers:
return None
# make sure that numbers is sorted
numbers = list(sorted(numbers))
# get the largest number and remove it from the list
largest = numbers.pop()
# we start with as many times the largest number as possible
quotient, remainder = divmod(target, largest)
# did we reach the target?
if remainder == 0:
return [largest] * quotient
# if not, try with a deacreasing number of times the largest
# (including 0 times)
for n in range(quotient, -1, -1):
remainder = target - n * largest
# and recursively try to obtain the remainder with the remaining numbers
solution = solve(numbers, remainder)
if solution:
return [largest] * n + solution
else:
return None
Some tests:
solve([1, 3, 5], 12)
# [5, 5, 1, 1]
solve([3, 5], 12) # no 1, we have to use smaller numbers
# [3, 3, 3, 3]
solve([7, 3, 4], 15)
# [7, 4, 4]
solve([3, 4], 5) # Impossible
# None
Keep looping, until n = 0, by taking away the largest number, then smaller numbers if n < 0.
As pointed out by Thierry Lathuille, This will probably not work if there is no 1 in your array. If that is the case, you might want to fiddle with the if n < 0 lines.
n = int(input())
a = [1, 3, 5]
ans = []
while n > 0:
n -= max(a)
if n == 0:
ans.append(max(a))
break
if n > 0:
ans.append(max(a))
if n < 0:
n += max(a)
a.pop(a.index(max(a)))
print(ans)

I want to know how to add to digits together in a list. Say the number is 10. I need it to add 1 + 0 to the new list

I want to know how to add to digits together in a list. Say the number is 10. I need it to add 1 + 0 to the new list. If the item in the list is 11 it needs to add 2 to the list
def main():
#Define List
mylist = [4,5,5,2,7,2,0,4,1,2,3,4,5,6,7,8]
print(mylist)
newlist = []
for each in mylist:
if (each % 2 == 0):
newlist.append(each)
else:
newlist.append(each + each)
for each in newlist:
if each >= 10:
newlist.append(each + each)
print(newlist)
main()
To get the digit sum of a number, you should convert the number to a string, loop over the string, and add all chars together (after converting them to int again):
def get_digit_sum(num):
return sum(int(x) for x in str(num))
To do this for a list of numbers, you should use list comprehension:
>>my_nums = [11, 22, 56, 345]
>>digit_sums = [get_digit_sum(x) for x in my_nums]
>>print(digit_sums)
[2, 4, 11, 12]
This is also possible to do in one expression:
>>my_nums = [11, 22, 56, 345]
>>digit_sums = [sum(int(x) for x in str(num)) for num in my_nums]
As per abarnet's comment, you may want the number 345 to be converted to 3+4+5=12, and the convert 12 to 1+2=3. This can be done using recursion. By using a max_digits parameter, you can specify the maximum number of digits in the returned number.
def get_digit_sum(num, max_digits=1):
d = sum(int(x) for x in str(num))
if len(str(d)) > max_digits:
return get_digit_sum(d)
return d
or a bit shorter:
def get_digit_sum(num, max_digits=1):
d = sum(int(x) for x in str(num))
return get_digit_sum(d) if len(str(d)) > max_digits else d
Both of the above functions would then yield:
>>my_nums = [11, 22, 56, 345]
>>print [get_digit_sum(x) for x in my_nums]
[2, 4, 2, 3]
It looks like your code is trying to do a little bit more than what you describe - in particular, you double any odd numbers. Because you're doing two things to each number (possibly double, sum the digits), you don't want to put it in the list until both steps are done. Instead do something like this:
for each in mylist:
if each % 2 != 0:
each *= 2
if each >= 10:
each = # sum of digits
newlist.append(each)
There's a couple of ways you could sum the digits. You can use divmod to get the quotient and the remainder of the number divided by 10 - the remainder is the units column, the quotient is every column to the left of that:
if each >= 10:
q, r = divmod(each, 10)
each = q + r
the other way is to get the string representation, which lets you iterate over every digit, convert it back to a number and add them:
if each >= 10:
each = sum(int(d) for d in str(each))
this is possibly easier to understand It is, however, a fair bit slower - which might matter if you have a very large amount of input.
These two approaches do work differently if you end up with any three or more digit numbers in the list - for 110, the divmod version will do 11 + 0 = 11, while the string version will do 1 + 1 + 0 = 2.
Also note that neither is guaranteed to end up with a single-digit number in the end - for 99, both of these will give you 18. If you want that further reduced to 9, you can change the if to a loop:
while each >= 10:
each = sum(int(d) for d in str(each))
to do this for the divmod version, you could put it into a recursive function instead:
def digit_sum(number):
q, r = divmod(number, 10)
if q > 10:
q = digit_sum(q)
return q+r
Making the same change to the divmod version as the string version appears to give the same answer in every case I've tried, but I have no idea if this is guaranteed to be the case.
> [int(d) for d in str(123)]
[1, 2, 3]
Try this:
A function that sums the digits for a given number:
def sum_of_digits(num):
return sum([int(x) for x in str(num)])
And a list comprehension to apply it to the entire list:
newlist=[sum_of_digits(number) for number in mylist]
I believe I know what you are going for. If the value is even and less than 10 add it to the new list, if the value is odd then double it and if that is less than 10 add it to the new list. Now if the value is greater than 10 then take the sum of all the digits and add it to the list.
I am sure there is a better way to do this, but this is what I came up with.
list = [4,5,5,2,7,2,0,4,1,2,3,4,5,6,7,8]
newlist = []
for i in list:
if i % 2 == 0 and len(str(i)) > 1:
newlist.append(sum([int(x) for x in str(i)]))
elif i % 2 == 0:
newlist.append(i)
elif len(str(i*2)) > 1:
newlist.append(sum([int(x) for x in str(i*2)]))
else:
newlist.append(i*2)
print newlist
input: [4, 5, 5, 2, 7, 2, 0, 4, 1, 2, 3, 4, 5, 6, 7, 8]
outcome: [4, 1, 1, 2, 5, 2, 0, 4, 2, 2, 6, 4, 1, 6, 5, 8]

Categories